Skip to content

Commit 37deaa7

Browse files
xabbuhweaverryan
authored andcommitted
document the new authentication customization options
1 parent 6212968 commit 37deaa7

File tree

3 files changed

+210
-0
lines changed

3 files changed

+210
-0
lines changed

cookbook/map.rst.inc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@
135135
* :doc:`/cookbook/security/form_login`
136136
* :doc:`/cookbook/security/securing_services`
137137
* :doc:`/cookbook/security/custom_provider`
138+
* :doc:`/cookbook/security/custom_authenticator`
138139
* :doc:`/cookbook/security/custom_authentication_provider`
139140
* :doc:`/cookbook/security/target_path`
140141

Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
.. index::
2+
single: Security; Custom Authenticator
3+
4+
How to create a custom Authenticator
5+
====================================
6+
7+
Introduction
8+
------------
9+
10+
Imagine you want to allow access to your website only between 2pm and 4pm (for
11+
the UTC timezone). Before Symfony 2.4, you had to create a custom token, factory,
12+
listener and provider.
13+
14+
The Authenticator
15+
-----------------
16+
17+
Thanks to new simplified authentication customization options in Symfony 2.4,
18+
you don't need to create a whole bunch of new classes, but use the
19+
:class:`Symfony\\Component\\Security\\Core\\Authentication\\SimpleFormAuthenticatorInterface`
20+
interface instead::
21+
22+
// src/Acme/HelloBundle/Security/TimeAuthenticator.php
23+
namespace Acme\HelloBundle\Security;
24+
25+
use Symfony\Component\HttpFoundation\Request;
26+
use Symfony\Component\Security\Core\Authentication\SimpleFormAuthenticatorInterface;
27+
use Symfony\Component\Security\Core\Authentication\TokenInterface;
28+
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
29+
use Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface;
30+
use Symfony\Component\Security\Core\Exception\AuthenticationException;
31+
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
32+
use Symfony\Component\Security\Core\User\UserProviderInterface;
33+
34+
class TimeAuthenticator implements SimpleFormAuthenticatorInterface
35+
{
36+
private $encoderFactory;
37+
38+
public function __construct(EncoderFactoryInterface $encoderFactory)
39+
{
40+
$this->encoderFactory = $encoderFactory;
41+
}
42+
43+
public function authenticateToken(TokenInterface $token, UserProviderInterface $userProvider, $providerKey)
44+
{
45+
try {
46+
$user = $userProvider->loadUserByUsername($token->getUsername());
47+
} catch (UsernameNotFoundException $e) {
48+
throw new AuthenticationException('Invalid username or password');
49+
}
50+
51+
$encoder = $this->encoderFactory->getEncoder($user);
52+
$passwordValid = $encoder->isPasswordValid(
53+
$user->getPassword(),
54+
$token->getCredentials(),
55+
$user->getSalt()
56+
);
57+
58+
if ($passwordValid) {
59+
$currentHour = date('G');
60+
if ($currentHour < 14 || $currentHour > 16) {
61+
throw new AuthenticationException(
62+
'You can only log in between 2 and 4!',
63+
100
64+
);
65+
}
66+
67+
return new UsernamePasswordToken(
68+
$user->getUsername(),
69+
$user->getPassword(),
70+
$providerKey,
71+
$user->getRoles()
72+
);
73+
}
74+
75+
throw new AuthenticationException('Invalid username or password');
76+
}
77+
78+
public function supportsToken(TokenInterface $token, $providerKey)
79+
{
80+
return $token instanceof UsernamePasswordToken
81+
&& $token->getProviderKey() === $providerKey;
82+
}
83+
84+
public function createToken(Request $request, $username, $password, $providerKey)
85+
{
86+
return new UsernamePasswordToken($username, $password, $providerKey);
87+
}
88+
}
89+
90+
.. versionadded:: 2.4
91+
The ``SimpleFormAuthenticatorInterface`` interface was added in Symfony 2.4.
92+
93+
How it works
94+
------------
95+
96+
There are a lot of things going on:
97+
98+
* ``createToken()`` creates a Token that will be used to authenticate the user;
99+
* ``authenticateToken()`` checks that the Token is allowed to log in by first
100+
getting the User via the user provider and then, by checking the password
101+
and the current time (a Token with roles is authenticated);
102+
* ``supportsToken()`` is just a way to allow several authentication mechanisms to
103+
be used for the same firewall (that way, you can for instance first try to
104+
authenticate the user via a certificate or an API key and fall back to a
105+
form login);
106+
* An encoder is needed to check the user password's validity; this is a
107+
service provided by default::
108+
109+
$encoder = $this->encoderFactory->getEncoder($user);
110+
$passwordValid = $encoder->isPasswordValid(
111+
$user->getPassword(),
112+
$token->getCredentials(),
113+
$user->getSalt()
114+
);
115+
116+
Configuration
117+
-------------
118+
119+
Now, configure your ``TimeAuthenticator`` as a service:
120+
121+
.. configuration-block::
122+
123+
.. code-block:: yaml
124+
125+
# app/config/config.yml
126+
services:
127+
time_authenticator:
128+
class: Acme\HelloBundle\Security\TimeAuthenticator
129+
arguments: [@security.encoder_factory]
130+
131+
.. code-block:: xml
132+
133+
<!-- app/config/config.xml -->
134+
<services>
135+
<service id="time_authenticator"
136+
class="Acme\HelloBundle\Security\TimeAuthenticator">
137+
<argument type="service" id="security.encoder_factory"/>
138+
</service>
139+
</services>
140+
141+
.. code-block:: php
142+
143+
// app/config/config.php
144+
use Symfony\Component\DependencyInjection\Definition;
145+
use Symfony\Component\DependencyInjection\Reference;
146+
147+
$container->setDefinition('time_authenticator', new Definition(
148+
'Acme\HelloBundle\Security\TimeAuthenticator',
149+
array(new Reference('security.encoder_factory'))
150+
));
151+
152+
Then, activate it in your ``firewalls`` section using the ``simple-form`` key
153+
like this:
154+
155+
.. configuration-block::
156+
157+
.. code-block:: yaml
158+
159+
# app/config/security.yml
160+
security:
161+
# ...
162+
163+
firewalls:
164+
secured_area:
165+
pattern: ^/admin
166+
provider: authenticator
167+
simple-form:
168+
provider: ...
169+
authenticator: time_authenticator
170+
check_path: login_check
171+
login_path: login
172+
173+
.. code-block:: xml
174+
175+
<!-- app/config/security.xml -->
176+
<?xml version="1.0" encoding="UTF-8"?>
177+
<srv:container xmlns="http://symfony.com/schema/dic/security"
178+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
179+
xmlns:srv="http://symfony.com/schema/dic/services"
180+
xsi:schemaLocation="http://symfony.com/schema/dic/services
181+
http://symfony.com/schema/dic/services/services-1.0.xsd">
182+
<config>
183+
<firewall name="secured_area" pattern="^/admin">
184+
<provider name="authenticator" />
185+
<simple-form authenticator="time_authenticator"
186+
check_path="login_check"
187+
login_path="login" />
188+
</firewall>
189+
</config>
190+
</srv:container>
191+
192+
.. code-block:: php
193+
194+
// app/config/security.php
195+
$container->loadFromExtension('security', array(
196+
'firewalls' => array(
197+
'secured_area' => array(
198+
'pattern' => '^/admin',
199+
'provider' => 'authenticator',
200+
'simple-form' => array(
201+
'provider' => ...,
202+
'authenticator' => 'time_authenticator',
203+
'check_path' => 'login_check',
204+
'login_path' => 'login',
205+
),
206+
),
207+
),
208+
));

cookbook/security/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,6 @@ Security
1313
form_login
1414
securing_services
1515
custom_provider
16+
custom_authenticator
1617
custom_authentication_provider
1718
target_path

0 commit comments

Comments
 (0)