Index: main/kernel/src/org/ametys/runtime/authentication/AuthenticateAction.java
===================================================================
--- main/kernel/src/org/ametys/runtime/authentication/AuthenticateAction.java	(revision 27254)
+++ main/kernel/src/org/ametys/runtime/authentication/AuthenticateAction.java	(working copy)
@@ -97,7 +97,7 @@
      * @param objectModel the current objectModel
      * @param redirector the Cocoon redirector
      * @return true if the current user is authenticated, false otherwise
-     * @throws Exception if an error occures during the authentication process
+     * @throws Exception if an error occurs during the authentication process
      */
     protected boolean _checkAuth(Map objectModel, Redirector redirector) throws Exception
     {
Index: main/kernel/src/org/ametys/runtime/authentication/Credentials.java
===================================================================
--- main/kernel/src/org/ametys/runtime/authentication/Credentials.java	(revision 27254)
+++ main/kernel/src/org/ametys/runtime/authentication/Credentials.java	(working copy)
@@ -1,5 +1,5 @@
 /*
- *  Copyright 2009 Anyware Services
+ *  Copyright 2014 Anyware Services
  *
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
@@ -15,17 +15,20 @@
  */
 package org.ametys.runtime.authentication;
 
+import org.apache.avalon.framework.logger.AbstractLogEnabled;
+
 /**
  * Credentials represents the caracteristics of a user needed to log him
  */
-public class Credentials
+public class Credentials extends AbstractLogEnabled
 {
-    private String _login;
-
-    private String _passwd;
+    /** The login */
+    protected String _login;
+    /** The password */
+    protected String _passwd;
 
     /**
-     * Create a credentials
+     * Create credentials
      * 
      * @param login Login of the user
      * @param password Password associated to the login
Index: main/plugin-core/plugin.xml
===================================================================
--- main/plugin-core/plugin.xml	(revision 27254)
+++ main/plugin-core/plugin.xml	(working copy)
@@ -901,6 +901,11 @@
                        logger="org.ametys.runtime.plugins.core.authentication.mixed.MixedSourceUsersManagerAuthentication"/>
                        
             <extension point="org.ametys.runtime.authentication.AuthenticationManager"
+                       id="org.ametys.runtime.plugins.core.authentication.token.TokenUsersManagerAuthentication"
+                       class="org.ametys.runtime.plugins.core.authentication.token.TokenUsersManagerAuthentication"
+                       logger="org.ametys.runtime.plugins.core.authentication.token.TokenUsersManagerAuthentication"/>
+                       
+            <extension point="org.ametys.runtime.authentication.AuthenticationManager"
                        id="org.ametys.runtime.plugins.core.authentication.HasRightAuthentication"
                        class="org.ametys.runtime.plugins.core.authentication.HasRightAuthentication"
                        logger="org.ametys.runtime.plugins.core.authentication.HasRightAuthentication"/>
Index: main/plugin-core/scripts/derby/users_token.sql
===================================================================
--- main/plugin-core/scripts/derby/users_token.sql	(revision 0)
+++ main/plugin-core/scripts/derby/users_token.sql	(revision 0)
@@ -0,0 +1,22 @@
+--
+--  Copyright 2014 Anyware Services
+--
+--  Licensed under the Apache License, Version 2.0 (the "License");
+--  you may not use this file except in compliance with the License.
+--  You may obtain a copy of the License at
+--
+--      http://www.apache.org/licenses/LICENSE-2.0
+--
+--  Unless required by applicable law or agreed to in writing, software
+--  distributed under the License is distributed on an "AS IS" BASIS,
+--  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+--  See the License for the specific language governing permissions and
+--  limitations under the License.
+--
+CREATE TABLE UsersToken (
+  id int PRIMARY KEY NOT NULL GENERATED ALWAYS AS IDENTITY (START WITH 1, INCREMENT BY 1),
+  login varchar(64),
+  token varchar(128)  NOT NULL, -- the hashed token + salt is stocked here
+  salt varchar(64) NOT NULL,
+  creation_date date not null
+);
Index: main/plugin-core/scripts/hsqldb/users_token.sql
===================================================================
--- main/plugin-core/scripts/hsqldb/users_token.sql	(revision 0)
+++ main/plugin-core/scripts/hsqldb/users_token.sql	(revision 0)
@@ -0,0 +1,22 @@
+--
+--  Copyright 2014 Anyware Services
+--
+--  Licensed under the Apache License, Version 2.0 (the "License");
+--  you may not use this file except in compliance with the License.
+--  You may obtain a copy of the License at
+--
+--      http://www.apache.org/licenses/LICENSE-2.0
+--
+--  Unless required by applicable law or agreed to in writing, software
+--  distributed under the License is distributed on Databasean "AS IS" BASIS,
+--  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+--  See the License for the specific language governing permissions and
+--  limitations under the License.
+--
+CREATE CACHED TABLE UsersToken (
+  id int PRIMARY KEY NOT NULL IDENTITY,
+  login varchar(64),
+  token varchar(128)  NOT NULL, -- the hashed token + salt are stocked here
+  salt varchar(64) NOT NULL,
+  creation_date datetime not null
+);
Index: main/plugin-core/scripts/mysql/users_token.sql
===================================================================
--- main/plugin-core/scripts/mysql/users_token.sql	(revision 0)
+++ main/plugin-core/scripts/mysql/users_token.sql	(revision 0)
@@ -0,0 +1,23 @@
+--
+--  Copyright 2014 Anyware Services
+--
+--  Licensed under the Apache License, Version 2.0 (the "License");
+--  you may not use this file except in compliance with the License.
+--  You may obtain a copy of the License at
+--
+--      http://www.apache.org/licenses/LICENSE-2.0
+--
+--  Unless required by applicable law or agreed to in writing, software
+--  distributed under the License is distributed on an "AS IS" BASIS,
+--  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+--  See the License for the specific language governing permissions and
+--  limitations under the License.
+--
+drop table if exists UsersToken;
+CREATE TABLE UsersToken (
+  id int PRIMARY KEY NOT NULL AUTO_INCREMENT,
+  login varchar(64),
+  token varchar(128)  NOT NULL, -- the hashed token + salt are stocked here
+  salt varchar(64) NOT NULL,
+  creation_date datetime not null
+)ENGINE=innodb;
Index: main/plugin-core/scripts/oracle/users_token.sql
===================================================================
--- main/plugin-core/scripts/oracle/users_token.sql	(revision 0)
+++ main/plugin-core/scripts/oracle/users_token.sql	(revision 0)
@@ -0,0 +1,26 @@
+--
+--  Copyright 2014 Anyware Services
+--
+--  Licensed under the Apache License, Version 2.0 (the "License");
+--  you may not use this file except in compliance with the License.
+--  You may obtain a copy of the License at
+--
+--      http://www.apache.org/licenses/LICENSE-2.0
+--
+--  Unless required by applicable law or agreed to in writing, software
+--  distributed under the License is distributed on an "AS IS" BASIS,
+--  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+--  See the License for the specific language governing permissions and
+--  limitations under the License.
+--
+CREATE TABLE UsersToken
+(
+  id number PRIMARY KEY NOT NULL,
+  login varchar(64),
+  token varchar(128) NOT NULL, -- the hashed token + salt are stocked here
+  salt varchar(64) NOT NULL,
+  creation_date date NOT NULL
+);
+
+create sequence seq_userstoken;
+
Index: main/plugin-core/scripts/postgresql/users_token.sql
===================================================================
--- main/plugin-core/scripts/postgresql/users_token.sql	(revision 0)
+++ main/plugin-core/scripts/postgresql/users_token.sql	(revision 0)
@@ -0,0 +1,30 @@
+--
+--  Copyright 2014 Anyware Services
+--
+--  Licensed under the Apache License, Version 2.0 (the "License");
+--  you may not use this file except in compliance with the License.
+--  You may obtain a copy of the License at
+--
+--      http://www.apache.org/licenses/LICENSE-2.0
+--
+--  Unless required by applicable law or agreed to in writing, software
+--  distributed under the License is distributed on an "AS IS" BASIS,
+--  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+--  See the License for the specific language governing permissions and
+--  limitations under the License.
+--
+BEGIN;
+
+DROP TABLE IF EXISTS UsersToken;
+
+CREATE TABLE UsersToken(
+  id SERIAL PRIMARY KEY,
+  login VARCHAR(64),
+  token VARCHAR(128)  NOT NULL, -- the hashed token + salt are stocked here
+  salt VARCHAR(64) NOT NULL,
+  creation_date DATE NOT NULL
+);
+
+COMMIT;
+
+
Index: main/plugin-core/src/org/ametys/runtime/plugins/core/authentication/FormBasedCredentialsProvider.java
===================================================================
--- main/plugin-core/src/org/ametys/runtime/plugins/core/authentication/FormBasedCredentialsProvider.java	(revision 27254)
+++ main/plugin-core/src/org/ametys/runtime/plugins/core/authentication/FormBasedCredentialsProvider.java	(working copy)
@@ -1,5 +1,5 @@
 /*
- *  Copyright 2009 Anyware Services
+ *  Copyright 2014 Anyware Services
  *
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
@@ -16,7 +16,6 @@
 package org.ametys.runtime.plugins.core.authentication;
 
 import java.sql.Connection;
-import java.sql.Date;
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
 import java.sql.SQLException;
@@ -42,6 +41,8 @@
 import org.apache.cocoon.environment.Request;
 import org.apache.cocoon.environment.Response;
 import org.apache.cocoon.environment.http.HttpCookie;
+import org.apache.commons.codec.digest.DigestUtils;
+import org.apache.commons.lang.RandomStringUtils;
 import org.apache.commons.lang.StringUtils;
 import org.joda.time.DateTime;
 
@@ -50,13 +51,16 @@
 import org.ametys.runtime.captcha.CaptchaHelper;
 import org.ametys.runtime.config.Config;
 import org.ametys.runtime.datasource.ConnectionHelper;
+import org.ametys.runtime.datasource.ConnectionHelper.DatabaseType;
+import org.ametys.runtime.plugins.core.authentication.token.TokenCredentials;
 import org.ametys.runtime.workspace.WorkspaceMatcher;
 
 /**
  * This manager gets the credentials coming from an authentification form. <br>
  * This manager can create a cookie to save credentials
  * <br>
- * Parameters are : - The html field name for user name<br>
+ * Parameters are : - The name of the pool<br>
+ *                  - The html field name for user name<br>
  *                  - The html field name for user password<br>
  *                  - The html field name for the check box which allow to create a cookie, must return 'true' when checked<br>
  *                  - A boolean, to activate or not the user info saving by cookie <br>
@@ -67,6 +71,7 @@
  *                  - A list of URL prefixes that are accessible without authentication. The login and failure URLs are always accessible without authentication.<br><br>
  * 
  * For example :<br>
+ *               &lt;pool&gt;runtime.datasource.core.jdbc.pool&lt;/pool&gt;<br>
  *               &lt;username-field&gt;Username&lt;/username-field&gt;<br>
  *               &lt;password-field&gt;Password&lt;/password-field&gt;<br>
  *               &lt;cookie&gt;<br>
@@ -92,13 +97,18 @@
     public static final String SECURITY_LEVEL_HIGH = "high";
     /** Number of connection attempts allowed */
     public static final Integer NB_CONNECTION_ATTEMPTS = 3;
-    
+    /** 15 days in seconds */
+    public static final int DEFAULT_COOKIE_LIFETIME = 1209600;
+
     /** Name of the user name html field */
     protected String _usernameField;
 
     /** Name of the user password html field */
     protected String _passwordField;
       
     /** Name of the "remember me" html field */
     protected String _rememberMeField;
     
@@ -117,7 +127,7 @@
     /** Cookie duration in seconds, by default 1 week */
     protected long _cookieLifetime;
 
-    /** Redirection in case of none login infos found from request params or cookie */
+    /** Redirection in case of no login infos were found from request params or cookie */
     protected String _loginUrl;
 
     /** Redirection when the authentication fails */
@@ -138,9 +148,60 @@
     /** A list of accepted url patterns */
     protected Collection<Pattern> _acceptedUrlPatterns = Arrays.asList(new Pattern[]{Pattern.compile("^plugins/core/captcha/[^/]+/image.png")});   // captcha  
     
+    /** The configuration */
+    protected Config _config = Config.getInstance();
+    
     /** Context */
     protected Context _context;
     
+    /** The pool name */
+    private String _poolName;
+    
+    public void configure(Configuration configuration) throws ConfigurationException
+    {
+        _poolName = configuration.getChild("pool").getValue(ConnectionHelper.CORE_POOL_NAME);
+        _usernameField = configuration.getChild("username-field").getValue("Username");
+        _passwordField = configuration.getChild("password-field").getValue("Password");
+        _rememberMeField =  configuration.getChild("rememberMe-field").getValue("rememberMe");
+        _captchaField =  configuration.getChild("capcha-field").getValue("Captcha");
+        _captchaKeyField =  configuration.getChild("captchaKey-field").getValue("CaptchaKey");
+        _cookieEnabled = configuration.getChild("cookie").getChild("cookieEnabled").getValueAsBoolean(true);
+        _cookieLifetime = configuration.getChild("cookie").getChild("cookieLifeTime").getValueAsLong(DEFAULT_COOKIE_LIFETIME);
+        _cookieName = configuration.getChild("cookie").getChild("cookieName").getValue("AmetysAuthentication");
+        _loginUrl = configuration.getChild("loginUrl").getValue("login.html");
+        _loginFailedUrl = configuration.getChild("loginFailedUrl").getValue("login_failed.html");
+        _provideLoginParameter = configuration.getChild("loginFailedUrl").getAttributeAsBoolean("provideLoginParameter", false);
+        _loginUrlInternal = configuration.getChild("loginUrl").getAttributeAsBoolean("internal", false);
+        _loginFailedUrlInternal = configuration.getChild("loginFailedUrl").getAttributeAsBoolean("internal", false);
+        _acceptedUrlPrefixes = new HashSet<String>();
+        for (Configuration prefixConf : configuration.getChild("unauthenticated").getChildren("urlPrefix"))
+        {
+            String prefix = prefixConf.getValue(null);
+            if (prefix != null)
+            {
+                _acceptedUrlPrefixes.add(prefix);
+            }
+        }
+        
+        if (getLogger().isDebugEnabled())
+        {
+            getLogger().debug(
+                    "FormBasedCredentialsProvider values : " + " Name field=" + _usernameField + ", Pwd field="
+                            + _passwordField + ", CookieEnabled=" + _cookieEnabled + ", Cookie duration="
+                            + _cookieLifetime + ", Cookie name=" + _cookieName + ", Login url=" + getLoginURL()
+                            + " [" + (_loginUrlInternal ? "internal" : "external") + "]"
+                            + ", Login failed url=" + getLoginFailedURL() 
+                            + " [" + (_loginFailedUrlInternal ? "internal" : "external")
+                            + ", provide login on redirection : " + _provideLoginParameter + "]"
+                            + ", accepted prefixes : [" + StringUtils.join(_acceptedUrlPrefixes, ", ") + "]");
+        }
+    }
+
+    public void contextualize(Context context) throws ContextException
+    {
+        this._context = context;
+    }
+    
     /**
      * Get the login url
      * @return the login url
@@ -159,6 +220,54 @@
         return _loginFailedUrl;
     }
     
+    public void notAllowed(Redirector redirector) throws Exception
+    {
+        Request request = ContextHelper.getRequest(_context);
+
+        StringBuffer parameters = new StringBuffer();
+        parameters.append(getLoginFailedURL().indexOf('?') >= 0 ? "" : "?");
+        
+        if (_provideLoginParameter)
+        {
+            parameters.append("&login=" + request.getParameter(_usernameField));
+        }
+        
+        String level = Config.getInstance().getValueAsString("runtime.authentication.form.security.level");
+        if (SECURITY_LEVEL_HIGH.equals(level))
+        {
+            String captchaKey = request.getParameter(_captchaKeyField);
+            int nbConnect = _setNbConnectBDD(request.getParameter(_usernameField));
+            int nbAttempts = NB_CONNECTION_ATTEMPTS - 1;
+            
+            if (nbConnect == nbAttempts || (captchaKey == null && nbConnect > nbAttempts))
+            {
+                parameters.append("&tooManyAttempts=" + true);
+            }
+        }
+        
+        if (StringUtils.isNotEmpty(getCookieValue(request, _cookieName)))
+        {
+            parameters.append("&cookieFailure=" + true);
+            deleteCookie(request, ContextHelper.getResponse(_context), _cookieName, (int) _cookieLifetime); 
+        }
+
+        String redirectUrl;
+        if (_loginFailedUrlInternal)
+        {
+            redirectUrl = "cocoon://" + getLoginFailedURL() + parameters.toString();
+        }
+        else
+        {
+            redirectUrl = request.getContextPath() + request.getAttribute(WorkspaceMatcher.WORKSPACE_URI) + "/" + getLoginFailedURL() + parameters.toString();
+        }
+        redirector.redirect(false, redirectUrl);
+    }
+
+    public boolean validate(Redirector redirector) throws Exception
+    {
+        return true;
+    }
+    
     public boolean accept()
     {
         Request request = ContextHelper.getRequest(_context);
@@ -230,24 +339,31 @@
         if (!SECURITY_LEVEL_HIGH.equals(level))
         {
             Request request = ContextHelper.getRequest(_context);
+            String cookieValue = getCookieValue(request, _cookieName);
+            
+            // Hash token + salt
+            String token = RandomStringUtils.randomAlphanumeric(16);
+            String salt = RandomStringUtils.randomAlphanumeric(48);
+            String hashedTokenAndSalt = DigestUtils.sha512Hex(token + salt);
             
-            String value = getCookieValue(request, _cookieName);
-            if (value != null && !"".equals(value))
+            if (hashedTokenAndSalt == null)
             {
-                updateCookie(value, _cookieName, (int) _cookieLifetime, _context);
+                getLogger().error("Unable to hash token and salt");
             }
-            else
+            
+            String login = null;
+            String rememberMe = request.getParameter(_rememberMeField);
+            if ("true".equals(rememberMe))
             {
-                String login = request.getParameter(_usernameField);
-                String password = request.getParameter(_passwordField);
-                String rememberMe = request.getParameter(_rememberMeField);
-                if (rememberMe != null)
-                {
-                    if (rememberMe.equalsIgnoreCase("true"))
-                    {
-                        updateCookie(login + "/n" + password, _cookieName, (int) _cookieLifetime, _context);
-                    }
-                }
+                login = request.getParameter(_usernameField);
+                _insertUserToken(login, salt, hashedTokenAndSalt);
+                updateCookie(login + "," + token, _cookieName, (int) _cookieLifetime, _context);
+            }
+            else if (StringUtils.isNotEmpty(cookieValue))
+            {
+                login = cookieValue.split(",")[0];
+                _insertUserToken(login, salt, hashedTokenAndSalt);
+                updateCookie(login + "," + token, _cookieName, (int) _cookieLifetime, _context);
             }
         }
         else
@@ -256,7 +372,6 @@
             String login = request.getParameter(_usernameField);
 
             _deleteLoginFailedBDD(login);
-            
         }
     }
 
@@ -290,10 +405,10 @@
         }
 
         String value = getCookieValue(request, _cookieName);
-        if (value != null && !"".equals(value))
+        if (StringUtils.isNotEmpty(value))
         {
-            String [] values = value.split("/n");
-            return new Credentials(values[0], values[1]);
+            String [] values = value.split(",");
+            return new TokenCredentials(values[0], values[1]);
         }
 
         String redirectUrl;
@@ -308,6 +423,65 @@
         redirector.redirect(false, redirectUrl);
         return null;
     }
+    
+    /**
+     * Inserts a new line into the users token table
+     * @param login the login of the user
+     * @param salt the salt associated to this user
+     * @param hashedTokenAndSalt token + salt hashed with SHA-512
+     */
+    protected void _insertUserToken(String login, String salt, String hashedTokenAndSalt)
+    {
+        Connection connection = null;
+        PreparedStatement statement = null;
+        ResultSet rs = null;
+        try
+        {
+            connection = ConnectionHelper.getConnection(_poolName);
+            DatabaseType dbType = ConnectionHelper.getDatabaseType(connection);
+            
+            if (dbType.equals(DatabaseType.DATABASE_ORACLE))
+            {
+                statement = connection.prepareStatement("SELECT seq_userstoken.nextval FROM dual");
+                rs = statement.executeQuery();
+                
+                String id = null;
+                if (rs.next())
+                {
+                    id = rs.getString(1);
+                }
+                ConnectionHelper.cleanup(rs);
+                ConnectionHelper.cleanup(statement);
+                
+                statement = connection.prepareStatement("INSERT INTO UsersToken (id, login, token, salt, creation_date) VALUES (?, ?, ?, ?, ?)");
+                statement.setString(1, id);
+                statement.setString(2, login);
+                statement.setString(3, hashedTokenAndSalt);
+                statement.setString(4, salt);
+                statement.setDate(5, new java.sql.Date(System.currentTimeMillis()));
+            }
+            else
+            {
+                statement = connection.prepareStatement("INSERT INTO UsersToken (login, token, salt, creation_date) VALUES (?, ?, ?, ?)");
+                
+                statement.setString(1, login);
+                statement.setString(2, hashedTokenAndSalt);
+                statement.setString(3, salt);
+                statement.setDate(4, new java.sql.Date(System.currentTimeMillis()));
+            }
+            
+            statement.executeUpdate();
+        }
+        catch (SQLException e)
+        {
+            getLogger().error("Communication error with the database", e);
+        }
+        finally
+        {
+            ConnectionHelper.cleanup(statement);       
+            ConnectionHelper.cleanup(connection);
+        }
+    }
 
     /**
      * Delete the login from the table of the failed connection
@@ -488,92 +662,6 @@
         }
     }
     
-    public void notAllowed(Redirector redirector) throws Exception
-    {
-        Request request = ContextHelper.getRequest(_context);
-
-        StringBuffer parameters = new StringBuffer();
-        if (_provideLoginParameter)
-        {
-            parameters.append(getLoginFailedURL().indexOf('?') >= 0 ? "&" : "?");
-            parameters.append("login=" + request.getParameter(_usernameField));
-            
-            String level = Config.getInstance().getValueAsString("runtime.authentication.form.security.level");
-            if (SECURITY_LEVEL_HIGH.equals(level))
-            {
-                String captchaKey = request.getParameter(_captchaKeyField);
-                int nbConnect = _setNbConnectBDD(request.getParameter(_usernameField));
-                int nbAttempts = NB_CONNECTION_ATTEMPTS - 1;
-                
-                if (nbConnect == nbAttempts || (captchaKey == null && nbConnect > nbAttempts))
-                {
-                    parameters.append("&tooManyAttempts=" + true);
-                }
-            }
-            
-        }
-
-        String redirectUrl;
-        if (_loginFailedUrlInternal)
-        {
-            redirectUrl = "cocoon://" + getLoginFailedURL() + parameters.toString();
-        }
-        else
-        {
-            redirectUrl = request.getContextPath() + request.getAttribute(WorkspaceMatcher.WORKSPACE_URI) + "/" + getLoginFailedURL() + parameters.toString();
-        }
-        redirector.redirect(false, redirectUrl);
-    }
-
-    public boolean validate(Redirector redirector) throws Exception
-    {
-        return true;
-    }
-
-    public void configure(Configuration configuration) throws ConfigurationException
-    {
-        _usernameField = configuration.getChild("username-field").getValue("Username");
-        _passwordField = configuration.getChild("password-field").getValue("Password");
-        _rememberMeField =  configuration.getChild("rememberMe-field").getValue("rememberMe");
-        _captchaField =  configuration.getChild("capcha-field").getValue("Captcha");
-        _captchaKeyField =  configuration.getChild("captchaKey-field").getValue("CaptchaKey");
-        _cookieEnabled = configuration.getChild("cookie").getChild("cookieEnabled").getValueAsBoolean(true);
-        _cookieLifetime = configuration.getChild("cookie").getChild("cookieLifeTime").getValueAsLong(604800);
-        _cookieName = configuration.getChild("cookie").getChild("cookieName").getValue("AmetysAuthentication");
-        _loginUrl = configuration.getChild("loginUrl").getValue("login.html");
-        _loginFailedUrl = configuration.getChild("loginFailedUrl").getValue("login_failed.html");
-        _provideLoginParameter = configuration.getChild("loginFailedUrl").getAttributeAsBoolean("provideLoginParameter", false);
-        _loginUrlInternal = configuration.getChild("loginUrl").getAttributeAsBoolean("internal", false);
-        _loginFailedUrlInternal = configuration.getChild("loginFailedUrl").getAttributeAsBoolean("internal", false);
-        _acceptedUrlPrefixes = new HashSet<String>();
-        for (Configuration prefixConf : configuration.getChild("unauthenticated").getChildren("urlPrefix"))
-        {
-            String prefix = prefixConf.getValue(null);
-            if (prefix != null)
-            {
-                _acceptedUrlPrefixes.add(prefix);
-            }
-        }
-        
-        if (getLogger().isDebugEnabled())
-        {
-            getLogger().debug(
-                    "FormBasedCredentialsProvider values : " + " Name field=" + _usernameField + ", Pwd field="
-                            + _passwordField + ", CookieEnabled=" + _cookieEnabled + ", Cookie duration="
-                            + _cookieLifetime + ", Cookie name=" + _cookieName + ", Login url=" + getLoginURL()
-                            + " [" + (_loginUrlInternal ? "internal" : "external") + "]"
-                            + ", Login failed url=" + getLoginFailedURL() 
-                            + " [" + (_loginFailedUrlInternal ? "internal" : "external")
-                            + ", provide login on redirection : " + _provideLoginParameter + "]"
-                            + ", accepted prefixes : [" + StringUtils.join(_acceptedUrlPrefixes, ", ") + "]");
-        }
-    }
-
-    public void contextualize(Context context) throws ContextException
-    {
-        this._context = context;
-    }
-    
    /************************************************************************************************
     *    COOKIE EXCHANGES MANAGEMENT
     */
Index: main/plugin-core/src/org/ametys/runtime/plugins/core/authentication/UsersManagerAuthentication.java
===================================================================
--- main/plugin-core/src/org/ametys/runtime/plugins/core/authentication/UsersManagerAuthentication.java	(revision 27254)
+++ main/plugin-core/src/org/ametys/runtime/plugins/core/authentication/UsersManagerAuthentication.java	(working copy)
@@ -32,7 +32,8 @@
  */
 public class UsersManagerAuthentication extends AbstractLogEnabled implements Authentication, Serviceable
 {
-    private UsersManager _users;
+    /** The users manager */
+    protected UsersManager _users;
     
     public void service(ServiceManager manager) throws ServiceException
     {
@@ -50,7 +51,7 @@
         if (_users instanceof CredentialsAwareUsersManager)
         {
             CredentialsAwareUsersManager auth = (CredentialsAwareUsersManager) _users;
-
+           
             // Effectuer l'authentification en cryptant le mot de passe
             return auth.checkCredentials(credentials);
         }
Index: main/plugin-core/src/org/ametys/runtime/plugins/core/authentication/mixed/MixedSourceUsersManagerAuthentication.java
===================================================================
--- main/plugin-core/src/org/ametys/runtime/plugins/core/authentication/mixed/MixedSourceUsersManagerAuthentication.java	(revision 27254)
+++ main/plugin-core/src/org/ametys/runtime/plugins/core/authentication/mixed/MixedSourceUsersManagerAuthentication.java	(working copy)
@@ -15,41 +15,22 @@
  */
 package org.ametys.runtime.plugins.core.authentication.mixed;
 
-import org.apache.avalon.framework.logger.AbstractLogEnabled;
-import org.apache.avalon.framework.service.ServiceException;
-import org.apache.avalon.framework.service.ServiceManager;
-import org.apache.avalon.framework.service.Serviceable;
-
-import org.ametys.runtime.authentication.Authentication;
 import org.ametys.runtime.authentication.Credentials;
+import org.ametys.runtime.plugins.core.authentication.UsersManagerAuthentication;
 import org.ametys.runtime.user.CredentialsAwareUsersManager;
-import org.ametys.runtime.user.UsersManager;
 
 /**
  * Authenticate Credentials with the current UsersManager (which must implement CredentialsAwareUsersManager).
  * Replace standard UsersManagerAuthentication with this one when using CASBasicCredentialsProvider.
  */
-public class MixedSourceUsersManagerAuthentication extends AbstractLogEnabled implements Authentication, Serviceable
+public class MixedSourceUsersManagerAuthentication extends UsersManagerAuthentication
 {
-    private UsersManager _users;
-    
-    public void service(ServiceManager manager) throws ServiceException
-    {
-        _users = (UsersManager) manager.lookup(UsersManager.ROLE);
-    }
-    
-    /**
-     * Check if a User can log in. Returns the User identified by this login or null if none.
-     * @param credentials Contains user information with an unencrypted password
-     * @return true if the user is authenticated, false otherwise.
-     */
+    @Override
     public boolean login(Credentials credentials)
     {
         // Check that the UsersManager knows how to authenticate credentials.
         if (_users instanceof CredentialsAwareUsersManager)
         {
-            CredentialsAwareUsersManager auth = (CredentialsAwareUsersManager) _users;
-            
             // If the credentials come from a SSO (like CAS), they are already authenticated : grant access.
             if (credentials instanceof MixedSourceCredentials)
             {
@@ -58,14 +39,8 @@
                     return true;
                 }
             }
-            
-            // Authenticate the credentials.
-            return auth.checkCredentials(credentials);
         }
         
-        getLogger().error("UsersManager cannot authenticate");
-        
-        // Invalid users manager, access refused.
-        return false;
+        return super.login(credentials);
     }
 }
Index: main/plugin-core/src/org/ametys/runtime/plugins/core/authentication/token/TokenCredentials.java
===================================================================
--- main/plugin-core/src/org/ametys/runtime/plugins/core/authentication/token/TokenCredentials.java	(revision 0)
+++ main/plugin-core/src/org/ametys/runtime/plugins/core/authentication/token/TokenCredentials.java	(revision 0)
@@ -0,0 +1,158 @@
+/*
+ *  Copyright 2014 Anyware Services
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.ametys.runtime.plugins.core.authentication.token;
+
+import java.sql.Connection;
+import java.sql.Date;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+
+import org.apache.commons.codec.digest.DigestUtils;
+
+import org.ametys.runtime.authentication.Credentials;
+import org.ametys.runtime.datasource.ConnectionHelper;
+
+/**
+ * Credentials coming from more than one source.
+ * If the credentials come from a SSO (like CAS), they are already authenticated.
+ */
+public class TokenCredentials extends Credentials
+{
+    /** 15 days in seconds */
+    private static final int COOKIE_LIFETIME = 1209600;
+
+    /**
+     * Constructor
+     * @param login The user's login
+     * @param token The user's token
+     */
+    public TokenCredentials(String login, String token)
+    {
+        super(login, token);
+    }
+    
+    @Override
+    public String getPassword()
+    {
+        throw new UnsupportedOperationException("TokenCredentials does not allow to use password");
+    }
+
+    /**
+     * Test if the user is already authenticated by the CredentialsProvider ?
+     * @return true if the user is already authenticated by the CredentialsProvider, false otherwise.
+     */
+    public boolean checkToken()
+    {
+        Connection connection = null;
+        PreparedStatement deleteStatement = null;
+        PreparedStatement selectStatement = null;
+        PreparedStatement updateStatement = null;
+        ResultSet resultSet = null;
+        
+        try
+        {
+            connection = ConnectionHelper.getConnection(ConnectionHelper.CORE_POOL_NAME);
+            
+            // Delete 2 weeks or more old entries
+            deleteStatement = _getDeleteOldUserTokenStatement(connection);
+            deleteStatement.executeUpdate();
+
+            // Retrieve entries corresponding to this login
+            String login = getLogin();
+            String token = this._passwd;
+            selectStatement = _getSelectUserTokenStatement(connection, login);
+            
+            resultSet = selectStatement.executeQuery();
+            
+            // Find the database entry using this token
+            while (resultSet.next())
+            {
+                if (resultSet.getString("token").equals(DigestUtils.sha512Hex(token + resultSet.getString("salt"))))
+                {
+                    // Delete it
+                    _deleteUserToken(connection, resultSet.getString("token"));
+                    return true;
+                }
+            }
+                
+            return false;
+        }
+        catch (Exception e)
+        {
+            getLogger().error("Communication error with the database", e); 
+            return false;
+        }
+        finally
+        {
+            ConnectionHelper.cleanup(resultSet);       
+            ConnectionHelper.cleanup(deleteStatement);  
+            ConnectionHelper.cleanup(selectStatement);
+            ConnectionHelper.cleanup(updateStatement);
+            ConnectionHelper.cleanup(connection);
+        }
+    }
+    
+    /**
+     * Generates the sql statement that deletes the entries of the users token database that are at least 2 weeks old
+     * @param connection the database's session
+     * @return statement the delete statement
+     * @throws SQLException if a sql exception occurs
+     */
+    private PreparedStatement _getDeleteOldUserTokenStatement(Connection connection) throws SQLException
+    {
+            String sqlRequest = null; 
+            sqlRequest = "DELETE FROM UsersToken WHERE creation_date < ?";
+            
+            Date thresholdDate = new Date(System.currentTimeMillis() - COOKIE_LIFETIME * 1000);
+            
+            PreparedStatement statement = connection.prepareStatement(sqlRequest);
+            statement.setDate(1, thresholdDate);
+            
+            return statement;        
+    }
+    
+    /**
+     * Generates the statement that selects the users having the specified login in the UsersToken table
+     * @param connection the database's session
+     * @param login the user's login
+     * @return the retrieve statement
+     * @throws SQLException if a sql exception occurs
+     */
+    private PreparedStatement _getSelectUserTokenStatement(Connection connection, String login) throws SQLException
+    {
+        String sqlRequest = "SELECT id, token, salt FROM UsersToken WHERE login= ?";
+        
+        PreparedStatement statement = connection.prepareStatement(sqlRequest);
+        statement.setString(1, login);
+
+        return statement;
+    }
+    
+    /**
+     * Deletes the database entry that has this token 
+     * @param connection the database's session
+     * @param token the token
+     * @throws SQLException 
+     */
+    private void _deleteUserToken(Connection connection, String token) throws SQLException
+    {
+        PreparedStatement statement = connection.prepareStatement("DELETE FROM UsersToken WHERE token = ?");
+        statement.setString(1, token);
+        
+        statement.executeUpdate();
+    }
+}
Index: main/plugin-core/src/org/ametys/runtime/plugins/core/authentication/token/TokenUsersManagerAuthentication.java
===================================================================
--- main/plugin-core/src/org/ametys/runtime/plugins/core/authentication/token/TokenUsersManagerAuthentication.java	(revision 0)
+++ main/plugin-core/src/org/ametys/runtime/plugins/core/authentication/token/TokenUsersManagerAuthentication.java	(revision 0)
@@ -0,0 +1,27 @@
+package org.ametys.runtime.plugins.core.authentication.token;
+
+import org.ametys.runtime.authentication.Credentials;
+import org.ametys.runtime.plugins.core.authentication.UsersManagerAuthentication;
+import org.ametys.runtime.user.CredentialsAwareUsersManager;
+
+/**
+ * Manager for users login with tokens
+ */
+public class TokenUsersManagerAuthentication extends UsersManagerAuthentication
+{
+    @Override
+    public boolean login(Credentials credentials)
+    {
+        // Check that the UsersManager knows how to authenticate credentials.
+        if (_users instanceof CredentialsAwareUsersManager)
+        {
+            // If the credentials come from a SSO (like CAS), they are already authenticated : grant access.
+            if (credentials instanceof TokenCredentials)
+            {
+                return ((TokenCredentials) credentials).checkToken();
+            }
+        }
+        
+        return super.login(credentials);
+    }
+}
