Skip to content

Commit afdb708

Browse files
xabbuhweaverryan
authored andcommitted
add example on how to use the new SimplePreAuthenticatorInterface
1 parent 8ddb26b commit afdb708

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
@@ -136,6 +136,7 @@
136136
* :doc:`/cookbook/security/securing_services`
137137
* :doc:`/cookbook/security/custom_provider`
138138
* :doc:`/cookbook/security/custom_password_authenticator`
139+
* :doc:`/cookbook/security/api_key_authentication`
139140
* :doc:`/cookbook/security/custom_authentication_provider`
140141
* :doc:`/cookbook/security/target_path`
141142

Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
.. index::
2+
single: Security; Custom Request Authenticator
3+
4+
How to Authenticate Users with API Keys
5+
=======================================
6+
7+
Nowadays, it's quite usual to authenticate the user via an API key (when developing
8+
a web service for instance). The API key is provided for every request and is
9+
passed as a query string parameter or via a HTTP header.
10+
11+
The API Key Authenticator
12+
-------------------------
13+
14+
.. versionadded:: 2.4
15+
The ``SimplePreAuthenticatorInterface`` interface was added in Symfony 2.4.
16+
17+
Authenticating a user based on the Request information should be done via a
18+
pre-authentication mechanism. The :class:`Symfony\\Component\\Security\\Core\\Authentication\\SimplePreAuthenticatorInterface`
19+
interface allows to implement such a scheme really easily::
20+
21+
// src/Acme/HelloBundle/Security/ApiKeyAuthenticator.php
22+
namespace Acme\HelloBundle\Security;
23+
24+
use Symfony\Component\Security\Core\Authentication\SimplePreAuthenticatorInterface;
25+
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
26+
use Symfony\Component\Security\Core\Exception\AuthenticationException;
27+
use Symfony\Component\Security\Core\Authentication\Token\PreAuthenticatedToken;
28+
use Symfony\Component\HttpFoundation\Request;
29+
use Symfony\Component\Security\Core\User\User;
30+
use Symfony\Component\Security\Core\User\UserProviderInterface;
31+
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
32+
use Symfony\Component\Security\Core\Exception\BadCredentialsException;
33+
34+
class ApiKeyAuthenticator implements SimplePreAuthenticatorInterface
35+
{
36+
protected $userProvider;
37+
38+
public function __construct(ApiKeyUserProviderInterface $userProvider)
39+
{
40+
$this->userProvider = $userProvider;
41+
}
42+
43+
public function createToken(Request $request, $providerKey)
44+
{
45+
if (!$request->query->has('apikey')) {
46+
throw new BadCredentialsException('No API key found');
47+
}
48+
49+
return new PreAuthenticatedToken(
50+
'anon.',
51+
$request->query->get('apikey'),
52+
$providerKey
53+
);
54+
}
55+
56+
public function authenticateToken(TokenInterface $token, UserProviderInterface $userProvider, $providerKey)
57+
{
58+
$apikey = $token->getCredentials();
59+
if (!$this->userProvider->getUsernameForApiKey($apikey)) {
60+
throw new AuthenticationException(
61+
sprintf('API Key "%s" does not exist.', $apikey)
62+
);
63+
}
64+
65+
$user = new User(
66+
$this->userProvider->getUsernameForApiKey($apikey),
67+
$apikey,
68+
array('ROLE_USER')
69+
);
70+
71+
return new PreAuthenticatedToken(
72+
$user,
73+
$apikey,
74+
$providerKey,
75+
$user->getRoles()
76+
);
77+
}
78+
79+
public function supportsToken(TokenInterface $token, $providerKey)
80+
{
81+
return $token instanceof PreAuthenticatedToken && $token->getProviderKey() === $providerKey;
82+
}
83+
}
84+
85+
``$userProvider`` can be any user provider implementing an interface similar to
86+
this::
87+
88+
// src/Acme/HelloBundle/Security/ApiKeyUserProviderInterface.php
89+
namespace Acme\HelloBundle\Security;
90+
91+
use Symfony\Component\Security\Core\User\UserProviderInterface;
92+
93+
interface ApiKeyUserProviderInterface extends UserProviderInterface
94+
{
95+
public function getUsernameForApiKey($apikey);
96+
}
97+
98+
.. note::
99+
100+
Read the dedicated article to learn
101+
:doc:`how to create a custom user provider </cookbook/security/custom_provider>`.
102+
103+
To access a resource protected by such an authenticator, you need to add an apikey
104+
parameter to the query string, like in ``http://example.com/admin/foo?apikey=37b51d194a7513e45b56f6524f2d51f2``.
105+
106+
Configuration
107+
-------------
108+
109+
Configure your ``ApiKeyAuthenticator`` as a service:
110+
111+
.. configuration-block::
112+
113+
.. code-block:: yaml
114+
115+
# app/config/config.yml
116+
services:
117+
# ...
118+
119+
apikey_authenticator:
120+
class: Acme\HelloBundle\Security\ApiKeyAuthenticator
121+
arguments: [@your_api_key_user_provider]
122+
123+
.. code-block:: xml
124+
125+
<!-- app/config/config.xml -->
126+
<?xml version="1.0" ?>
127+
<container xmlns="http://symfony.com/schema/dic/services"
128+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
129+
xsi:schemaLocation="http://symfony.com/schema/dic/services
130+
http://symfony.com/schema/dic/services/services-1.0.xsd">
131+
<services>
132+
<!-- ... -->
133+
134+
<service id="apikey_authenticator"
135+
class="Acme\HelloBundle\Security\ApiKeyAuthenticator"
136+
>
137+
<argument type="service" id="your_api_key_user_provider" />
138+
</service>
139+
</services>
140+
</container>
141+
142+
.. code-block:: php
143+
144+
// app/config/config.php
145+
use Symfony\Component\DependencyInjection\Definition;
146+
use Symfony\Component\DependencyInjection\Reference;
147+
148+
// ...
149+
150+
$container->setDefinition('apikey_authenticator', new Definition(
151+
'Acme\HelloBundle\Security\ApiKeyAuthenticator',
152+
array(new Reference('your_api_key_user_provider'))
153+
));
154+
155+
Then, activate it in your firewalls section using the ``simple-preauth`` key
156+
like this:
157+
158+
.. configuration-block::
159+
160+
.. code-block:: yaml
161+
162+
security:
163+
firewalls:
164+
secured_area:
165+
pattern: ^/admin
166+
simple-preauth:
167+
provider: ...
168+
authenticator: apikey_authenticator
169+
170+
.. code-block:: xml
171+
172+
<!-- app/config/security.xml -->
173+
<?xml version="1.0" encoding="UTF-8"?>
174+
<srv:container xmlns="http://symfony.com/schema/dic/security"
175+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
176+
xmlns:srv="http://symfony.com/schema/dic/services"
177+
xsi:schemaLocation="http://symfony.com/schema/dic/services
178+
http://symfony.com/schema/dic/services/services-1.0.xsd">
179+
<config>
180+
<!-- ... -->
181+
182+
<firewall name="secured_area"
183+
pattern="^/admin"
184+
provider="..."
185+
>
186+
<simple-preauth authenticator="apikey_authenticator" />
187+
</firewall>
188+
</config>
189+
</srv:container>
190+
191+
.. code-block:: php
192+
193+
// app/config/security.php
194+
195+
// ..
196+
197+
$container->loadFromExtension('security', array(
198+
'firewalls' => array(
199+
'secured_area' => array(
200+
'pattern' => '^/admin',
201+
'provider' => 'authenticator',
202+
'simple-preauth' => array(
203+
'provider' => ...,
204+
'authenticator' => 'apikey_authenticator',
205+
),
206+
),
207+
),
208+
));

cookbook/security/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,6 @@ Security
1414
securing_services
1515
custom_provider
1616
custom_password_authenticator
17+
api_key_authentication
1718
custom_authentication_provider
1819
target_path

0 commit comments

Comments
 (0)