diff --git a/cookbook/map.rst.inc b/cookbook/map.rst.inc
index d98207e594d..cf4496ebee4 100644
--- a/cookbook/map.rst.inc
+++ b/cookbook/map.rst.inc
@@ -129,6 +129,8 @@
* :doc:`/cookbook/security/form_login`
* :doc:`/cookbook/security/securing_services`
* :doc:`/cookbook/security/custom_provider`
+ * :doc:`/cookbook/security/custom_password_authenticator`
+ * :doc:`/cookbook/security/api_key_authentication`
* :doc:`/cookbook/security/custom_authentication_provider`
* :doc:`/cookbook/security/target_path`
diff --git a/cookbook/security/api_key_authentication.rst b/cookbook/security/api_key_authentication.rst
new file mode 100644
index 00000000000..48aa3f0aa20
--- /dev/null
+++ b/cookbook/security/api_key_authentication.rst
@@ -0,0 +1,208 @@
+.. index::
+ single: Security; Custom Request Authenticator
+
+How to Authenticate Users with API Keys
+=======================================
+
+Nowadays, it's quite usual to authenticate the user via an API key (when developing
+a web service for instance). The API key is provided for every request and is
+passed as a query string parameter or via a HTTP header.
+
+The API Key Authenticator
+-------------------------
+
+.. versionadded:: 2.4
+ The ``SimplePreAuthenticatorInterface`` interface was added in Symfony 2.4.
+
+Authenticating a user based on the Request information should be done via a
+pre-authentication mechanism. The :class:`Symfony\\Component\\Security\\Core\\Authentication\\SimplePreAuthenticatorInterface`
+interface allows to implement such a scheme really easily::
+
+ // src/Acme/HelloBundle/Security/ApiKeyAuthenticator.php
+ namespace Acme\HelloBundle\Security;
+
+ use Symfony\Component\Security\Core\Authentication\SimplePreAuthenticatorInterface;
+ use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
+ use Symfony\Component\Security\Core\Exception\AuthenticationException;
+ use Symfony\Component\Security\Core\Authentication\Token\PreAuthenticatedToken;
+ use Symfony\Component\HttpFoundation\Request;
+ use Symfony\Component\Security\Core\User\User;
+ use Symfony\Component\Security\Core\User\UserProviderInterface;
+ use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
+ use Symfony\Component\Security\Core\Exception\BadCredentialsException;
+
+ class ApiKeyAuthenticator implements SimplePreAuthenticatorInterface
+ {
+ protected $userProvider;
+
+ public function __construct(ApiKeyUserProviderInterface $userProvider)
+ {
+ $this->userProvider = $userProvider;
+ }
+
+ public function createToken(Request $request, $providerKey)
+ {
+ if (!$request->query->has('apikey')) {
+ throw new BadCredentialsException('No API key found');
+ }
+
+ return new PreAuthenticatedToken(
+ 'anon.',
+ $request->query->get('apikey'),
+ $providerKey
+ );
+ }
+
+ public function authenticateToken(TokenInterface $token, UserProviderInterface $userProvider, $providerKey)
+ {
+ $apikey = $token->getCredentials();
+ if (!$this->userProvider->getUsernameForApiKey($apikey)) {
+ throw new AuthenticationException(
+ sprintf('API Key "%s" does not exist.', $apikey)
+ );
+ }
+
+ $user = new User(
+ $this->userProvider->getUsernameForApiKey($apikey),
+ $apikey,
+ array('ROLE_USER')
+ );
+
+ return new PreAuthenticatedToken(
+ $user,
+ $apikey,
+ $providerKey,
+ $user->getRoles()
+ );
+ }
+
+ public function supportsToken(TokenInterface $token, $providerKey)
+ {
+ return $token instanceof PreAuthenticatedToken && $token->getProviderKey() === $providerKey;
+ }
+ }
+
+``$userProvider`` can be any user provider implementing an interface similar to
+this::
+
+ // src/Acme/HelloBundle/Security/ApiKeyUserProviderInterface.php
+ namespace Acme\HelloBundle\Security;
+
+ use Symfony\Component\Security\Core\User\UserProviderInterface;
+
+ interface ApiKeyUserProviderInterface extends UserProviderInterface
+ {
+ public function getUsernameForApiKey($apikey);
+ }
+
+.. note::
+
+ Read the dedicated article to learn
+ :doc:`how to create a custom user provider `.
+
+To access a resource protected by such an authenticator, you need to add an apikey
+parameter to the query string, like in ``http://example.com/admin/foo?apikey=37b51d194a7513e45b56f6524f2d51f2``.
+
+Configuration
+-------------
+
+Configure your ``ApiKeyAuthenticator`` as a service:
+
+.. configuration-block::
+
+ .. code-block:: yaml
+
+ # app/config/config.yml
+ services:
+ # ...
+
+ apikey_authenticator:
+ class: Acme\HelloBundle\Security\ApiKeyAuthenticator
+ arguments: [@your_api_key_user_provider]
+
+ .. code-block:: xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+ .. code-block:: php
+
+ // app/config/config.php
+ use Symfony\Component\DependencyInjection\Definition;
+ use Symfony\Component\DependencyInjection\Reference;
+
+ // ...
+
+ $container->setDefinition('apikey_authenticator', new Definition(
+ 'Acme\HelloBundle\Security\ApiKeyAuthenticator',
+ array(new Reference('your_api_key_user_provider'))
+ ));
+
+Then, activate it in your firewalls section using the ``simple-preauth`` key
+like this:
+
+.. configuration-block::
+
+ .. code-block:: yaml
+
+ security:
+ firewalls:
+ secured_area:
+ pattern: ^/admin
+ simple-preauth:
+ provider: ...
+ authenticator: apikey_authenticator
+
+ .. code-block:: xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+ .. code-block:: php
+
+ // app/config/security.php
+
+ // ..
+
+ $container->loadFromExtension('security', array(
+ 'firewalls' => array(
+ 'secured_area' => array(
+ 'pattern' => '^/admin',
+ 'provider' => 'authenticator',
+ 'simple-preauth' => array(
+ 'provider' => ...,
+ 'authenticator' => 'apikey_authenticator',
+ ),
+ ),
+ ),
+ ));
diff --git a/cookbook/security/custom_password_authenticator.rst b/cookbook/security/custom_password_authenticator.rst
new file mode 100644
index 00000000000..2560bd52223
--- /dev/null
+++ b/cookbook/security/custom_password_authenticator.rst
@@ -0,0 +1,225 @@
+.. index::
+ single: Security; Custom Password Authenticator
+
+How to create a Custom Password Authenticator
+=============================================
+
+Imagine you want to allow access to your website only between 2pm and 4pm (for
+the UTC timezone). Before Symfony 2.4, you had to create a custom token, factory,
+listener and provider.
+
+The Password Authenticator
+--------------------------
+
+.. versionadded:: 2.4
+ The ``SimpleFormAuthenticatorInterface`` interface was added in Symfony 2.4.
+
+But now, thanks to new simplified authentication customization options in
+Symfony 2.4, you don't need to create a whole bunch of new classes, but use the
+:class:`Symfony\\Component\\Security\\Core\\Authentication\\SimpleFormAuthenticatorInterface`
+interface instead::
+
+ // src/Acme/HelloBundle/Security/TimeAuthenticator.php
+ namespace Acme\HelloBundle\Security;
+
+ use Symfony\Component\HttpFoundation\Request;
+ use Symfony\Component\Security\Core\Authentication\SimpleFormAuthenticatorInterface;
+ use Symfony\Component\Security\Core\Authentication\TokenInterface;
+ use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
+ use Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface;
+ use Symfony\Component\Security\Core\Exception\AuthenticationException;
+ use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
+ use Symfony\Component\Security\Core\User\UserProviderInterface;
+
+ class TimeAuthenticator implements SimpleFormAuthenticatorInterface
+ {
+ private $encoderFactory;
+
+ public function __construct(EncoderFactoryInterface $encoderFactory)
+ {
+ $this->encoderFactory = $encoderFactory;
+ }
+
+ public function authenticateToken(TokenInterface $token, UserProviderInterface $userProvider, $providerKey)
+ {
+ try {
+ $user = $userProvider->loadUserByUsername($token->getUsername());
+ } catch (UsernameNotFoundException $e) {
+ throw new AuthenticationException('Invalid username or password');
+ }
+
+ $encoder = $this->encoderFactory->getEncoder($user);
+ $passwordValid = $encoder->isPasswordValid(
+ $user->getPassword(),
+ $token->getCredentials(),
+ $user->getSalt()
+ );
+
+ if ($passwordValid) {
+ $currentHour = date('G');
+ if ($currentHour < 14 || $currentHour > 16) {
+ throw new AuthenticationException(
+ 'You can only log in between 2 and 4!',
+ 100
+ );
+ }
+
+ return new UsernamePasswordToken(
+ $user->getUsername(),
+ $user->getPassword(),
+ $providerKey,
+ $user->getRoles()
+ );
+ }
+
+ throw new AuthenticationException('Invalid username or password');
+ }
+
+ public function supportsToken(TokenInterface $token, $providerKey)
+ {
+ return $token instanceof UsernamePasswordToken
+ && $token->getProviderKey() === $providerKey;
+ }
+
+ public function createToken(Request $request, $username, $password, $providerKey)
+ {
+ return new UsernamePasswordToken($username, $password, $providerKey);
+ }
+ }
+
+How it Works
+------------
+
+There are a lot of things going on:
+
+* ``createToken()`` creates a Token that will be used to authenticate the user;
+* ``authenticateToken()`` checks that the Token is allowed to log in by first
+ getting the User via the user provider and then, by checking the password
+ and the current time (a Token with roles is authenticated);
+* ``supportsToken()`` is just a way to allow several authentication mechanisms to
+ be used for the same firewall (that way, you can for instance first try to
+ authenticate the user via a certificate or an API key and fall back to a
+ form login);
+* An encoder is needed to check the user password's validity; this is a
+ service provided by default::
+
+ $encoder = $this->encoderFactory->getEncoder($user);
+ $passwordValid = $encoder->isPasswordValid(
+ $user->getPassword(),
+ $token->getCredentials(),
+ $user->getSalt()
+ );
+
+Configuration
+-------------
+
+Now, configure your ``TimeAuthenticator`` as a service:
+
+.. configuration-block::
+
+ .. code-block:: yaml
+
+ # app/config/config.yml
+ services:
+ # ...
+
+ time_authenticator:
+ class: Acme\HelloBundle\Security\TimeAuthenticator
+ arguments: [@security.encoder_factory]
+
+ .. code-block:: xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+ .. code-block:: php
+
+ // app/config/config.php
+ use Symfony\Component\DependencyInjection\Definition;
+ use Symfony\Component\DependencyInjection\Reference;
+
+ // ...
+
+ $container->setDefinition('time_authenticator', new Definition(
+ 'Acme\HelloBundle\Security\TimeAuthenticator',
+ array(new Reference('security.encoder_factory'))
+ ));
+
+Then, activate it in your ``firewalls`` section using the ``simple-form`` key
+like this:
+
+.. configuration-block::
+
+ .. code-block:: yaml
+
+ # app/config/security.yml
+ security:
+ # ...
+
+ firewalls:
+ secured_area:
+ pattern: ^/admin
+ provider: authenticator
+ simple-form:
+ provider: ...
+ authenticator: time_authenticator
+ check_path: login_check
+ login_path: login
+
+ .. code-block:: xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+ .. code-block:: php
+
+ // app/config/security.php
+
+ // ..
+
+ $container->loadFromExtension('security', array(
+ 'firewalls' => array(
+ 'secured_area' => array(
+ 'pattern' => '^/admin',
+ 'provider' => 'authenticator',
+ 'simple-form' => array(
+ 'provider' => ...,
+ 'authenticator' => 'time_authenticator',
+ 'check_path' => 'login_check',
+ 'login_path' => 'login',
+ ),
+ ),
+ ),
+ ));
diff --git a/cookbook/security/index.rst b/cookbook/security/index.rst
index a8edbdc4317..d0856f144cf 100644
--- a/cookbook/security/index.rst
+++ b/cookbook/security/index.rst
@@ -13,5 +13,7 @@ Security
form_login
securing_services
custom_provider
+ custom_password_authenticator
+ api_key_authentication
custom_authentication_provider
target_path