Skip to content

Commit 4588822

Browse files
committed
feature #16819 [Security] Access Tokens (Spomky, wouterj)
This PR was merged into the 6.2 branch. Discussion ---------- [Security] Access Tokens Documentation page related to the PR symfony/symfony#46428 Commits ------- f3f47ad Update docs for 6.2.0-RC1 changes to TokenHandlerInterface b65c136 Finish the docs for the new Access token authenticator 600bb80 Access Token Documentation
2 parents af67543 + f3f47ad commit 4588822

File tree

2 files changed

+357
-0
lines changed

2 files changed

+357
-0
lines changed

security.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1205,6 +1205,15 @@ website.
12051205

12061206
You can learn all about this authenticator in :doc:`/security/login_link`.
12071207

1208+
Access Tokens
1209+
~~~~~~~~~~~~~
1210+
1211+
Access Tokens are often used in API contexts.
1212+
The user receives a token from an authorization server
1213+
which authenticates them.
1214+
1215+
You can learn all about this authenticator in :doc:`/security/access_token`.
1216+
12081217
X.509 Client Certificates
12091218
~~~~~~~~~~~~~~~~~~~~~~~~~
12101219

security/access_token.rst

Lines changed: 348 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,348 @@
1+
.. index::
2+
single: Security; Access Token
3+
4+
How to use Access Token Authentication
5+
======================================
6+
7+
Access tokens or API tokens are commonly used as authentication mechanism
8+
in API contexts. The access token is a string, obtained during authentication
9+
(using the application or an authorization server). The access token's role
10+
is to verify the user identity and receive consent before the token is
11+
issued.
12+
13+
Access tokens can be of any kind, for instance opaque strings,
14+
`JSON Web Tokens (JWT)`_ or `SAML2 (XML structures)`_. Please refer to the
15+
`RFC6750`_: *The OAuth 2.0 Authorization Framework: Bearer Token Usage* for
16+
a detailed specification.
17+
18+
Using the Access Token Authenticator
19+
------------------------------------
20+
21+
This guide assumes you have setup security and have created a user object
22+
in your application. Follow :doc:`the main security guide </security>` if
23+
this is not yet the case.
24+
25+
1) Configure the Access Token Authenticator
26+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
27+
28+
To use the access token authenticator, you must configure a ``token_handler``.
29+
The token handler receives the token from the request and returns the
30+
correct user identifier. To get the user identifier, implementations may
31+
need to load and validate the token (e.g. revocation, expiration time,
32+
digital signature, etc.).
33+
34+
.. configuration-block::
35+
36+
.. code-block:: yaml
37+
38+
# config/packages/security.yaml
39+
security:
40+
firewalls:
41+
main:
42+
access_token:
43+
token_handler: App\Security\AccessTokenHandler
44+
45+
.. code-block:: xml
46+
47+
<!-- config/packages/security.xml -->
48+
<?xml version="1.0" encoding="UTF-8"?>
49+
<srv:container xmlns="http://symfony.com/schema/dic/security"
50+
xmlns:srv="http://symfony.com/schema/dic/services"
51+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
52+
xsi:schemaLocation="http://symfony.com/schema/dic/services
53+
https://symfony.com/schema/dic/services/services-1.0.xsd
54+
http://symfony.com/schema/dic/security
55+
https://symfony.com/schema/dic/security/security-1.0.xsd">
56+
57+
<config>
58+
<firewall name="main">
59+
<access-token token-handler="App\Security\AccessTokenHandler"/>
60+
</firewall>
61+
</config>
62+
</srv:container>
63+
64+
.. code-block:: php
65+
66+
// config/packages/security.php
67+
use App\Security\AccessTokenHandler;
68+
use Symfony\Config\SecurityConfig;
69+
70+
return static function (SecurityConfig $security) {
71+
$security->firewall('main')
72+
->accessToken()
73+
->tokenHandler(AccessTokenHandler::class)
74+
;
75+
};
76+
77+
This handler must implement
78+
:class:`Symfony\\Component\\Security\\Http\\AccessToken\\AccessTokenHandlerInterface`::
79+
80+
// src/Security/AccessTokenHandler.php
81+
namespace App\Security;
82+
83+
use App\Repository\AccessTokenRepository;
84+
use Symfony\Component\Security\Http\AccessToken\AccessTokenHandlerInterface;
85+
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
86+
87+
class AccessTokenHandler implements AccessTokenHandlerInterface
88+
{
89+
public function __construct(
90+
private AccessTokenRepository $repository
91+
) {
92+
}
93+
94+
public function getUserBadgeFrom(string $accessToken): UserBadge
95+
{
96+
// e.g. query the "access token" database to search for this token
97+
$accessToken = $this->repository->findOneByValue($token);
98+
if (null === $accessToken || !$accessToken->isValid()) {
99+
throw new BadCredentialsException('Invalid credentials.');
100+
}
101+
102+
// and return a UserBadge object containing the user identifier from the found token
103+
return new UserBadge($accessToken->getUserId());
104+
}
105+
}
106+
107+
The access token authenticator will use the returned user identifier to
108+
load the user using the :ref:`user provider <security-user-providers>`.
109+
110+
.. caution::
111+
112+
It is important to check the token if is valid. For instance, the
113+
example above verifies whether the token has not expired. With
114+
self-contained access tokens such as JWT, the handler is required to
115+
verify the digital signature and understand all claims, especially
116+
``sub``, ``iat``, ``nbf`` and ``exp``.
117+
118+
2) Configure the Token Extractor (Optional)
119+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
120+
121+
The application is now ready to handle incoming tokens. A *token extractor*
122+
retrieves the token from the request (e.g. a header or request body).
123+
124+
By default, the access token is read from the request header parameter
125+
``Authorization`` with the scheme ``Bearer`` (e.g. ``Authorization: Bearer
126+
the-token-value``).
127+
128+
Symfony provides other extractors as per the `RFC6750`_:
129+
130+
``header`` (default)
131+
The token is sent through the request header. Usually ``Authorization``
132+
with the ``Bearer`` scheme.
133+
``query_string``
134+
The token is part of the request query string. Usually ``access_token``.
135+
``request_body``
136+
The token is part of the request body during a POST request. Usually
137+
``access_token``.
138+
139+
.. caution::
140+
141+
Because of the security weaknesses associated with the URI method,
142+
including the high likelihood that the URL or the request body
143+
containing the access token will be logged, methods ``query_string``
144+
and ``request_body`` **SHOULD NOT** be used unless it is impossible to
145+
transport the access token in the request header field.
146+
147+
You can also create a custom extractor. The class must implement
148+
:class:`Symfony\\Component\\Security\\Http\\AccessToken\\AccessTokenExtractorInterface`.
149+
150+
.. configuration-block::
151+
152+
.. code-block:: yaml
153+
154+
# config/packages/security.yaml
155+
security:
156+
firewalls:
157+
main:
158+
access_token:
159+
token_handler: App\Security\AccessTokenHandler
160+
161+
# use a different built-in extractor
162+
token_extractors: request_body
163+
164+
# or provide the service ID of a custom extractor
165+
token_extractors: 'App\Security\CustomTokenExtractor'
166+
167+
.. code-block:: xml
168+
169+
<!-- config/packages/security.xml -->
170+
<?xml version="1.0" encoding="UTF-8"?>
171+
<srv:container xmlns="http://symfony.com/schema/dic/security"
172+
xmlns:srv="http://symfony.com/schema/dic/services"
173+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
174+
xsi:schemaLocation="http://symfony.com/schema/dic/services
175+
https://symfony.com/schema/dic/services/services-1.0.xsd
176+
http://symfony.com/schema/dic/security
177+
https://symfony.com/schema/dic/security/security-1.0.xsd">
178+
179+
<config>
180+
<firewall name="main">
181+
<access-token token-handler="App\Security\AccessTokenHandler">
182+
<!-- use a different built-in extractor -->
183+
<token-extractor>request_body</token-extractor>
184+
185+
<!-- or provide the service ID of a custom extractor -->
186+
<token-extractor>App\Security\CustomTokenExtractor</token-extractor>
187+
</access-token>
188+
</firewall>
189+
</config>
190+
</srv:container>
191+
192+
.. code-block:: php
193+
194+
// config/packages/security.php
195+
use App\Security\AccessTokenHandler;
196+
use App\Security\CustomTokenExtractor;
197+
use Symfony\Config\SecurityConfig;
198+
199+
return static function (SecurityConfig $security) {
200+
$security->firewall('main')
201+
->accessToken()
202+
->tokenHandler(AccessTokenHandler::class)
203+
204+
// use a different built-in extractor
205+
->tokenExtractors('request_body')
206+
207+
# or provide the service ID of a custom extractor
208+
->tokenExtractors(CustomTokenExtractor::class)
209+
;
210+
};
211+
212+
It is possible to set multiple extractors. In this case, **the order is
213+
important**: the first in the list is called first.
214+
215+
.. configuration-block::
216+
217+
.. code-block:: yaml
218+
219+
# config/packages/security.yaml
220+
security:
221+
firewalls:
222+
main:
223+
access_token:
224+
token_handler: App\Security\AccessTokenHandler
225+
token_extractors:
226+
- 'header'
227+
- 'App\Security\CustomTokenExtractor'
228+
229+
.. code-block:: xml
230+
231+
<!-- config/packages/security.xml -->
232+
<?xml version="1.0" encoding="UTF-8"?>
233+
<srv:container xmlns="http://symfony.com/schema/dic/security"
234+
xmlns:srv="http://symfony.com/schema/dic/services"
235+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
236+
xsi:schemaLocation="http://symfony.com/schema/dic/services
237+
https://symfony.com/schema/dic/services/services-1.0.xsd
238+
http://symfony.com/schema/dic/security
239+
https://symfony.com/schema/dic/security/security-1.0.xsd">
240+
241+
<config>
242+
<firewall name="main">
243+
<access-token token-handler="App\Security\AccessTokenHandler">
244+
<token-extractor>header</token-extractor>
245+
<token-extractor>App\Security\CustomTokenExtractor</token-extractor>
246+
</access-token>
247+
</firewall>
248+
</config>
249+
</srv:container>
250+
251+
.. code-block:: php
252+
253+
// config/packages/security.php
254+
use App\Security\AccessTokenHandler;
255+
use App\Security\CustomTokenExtractor;
256+
use Symfony\Config\SecurityConfig;
257+
258+
return static function (SecurityConfig $security) {
259+
$security->firewall('main')
260+
->accessToken()
261+
->tokenHandler(AccessTokenHandler::class)
262+
->tokenExtractors([
263+
'header',
264+
CustomTokenExtractor::class,
265+
])
266+
;
267+
};
268+
269+
3) Submit a Request
270+
~~~~~~~~~~~~~~~~~~~
271+
272+
That's it! Your application can now authenticate incoming requests using an
273+
API token.
274+
275+
Using the default header extractor, you can test the feature by submitting
276+
a request like this:
277+
278+
.. code-block:: terminal
279+
280+
$ curl -H 'Authorization: Bearer an-accepted-token-value' \
281+
https://localhost:8000/api/some-route
282+
283+
Customizing the Success Handler
284+
-------------------------------
285+
286+
By default, the request continues (e.g. the controller for the route is
287+
run). If you want to customize success handling, create your own success
288+
handler by creating a class that implements
289+
:class:`Symfony\\Component\\Security\\Http\\Authentication\\AuthenticationSuccessHandlerInterface`
290+
and configure the service ID as the ``success_handler``:
291+
292+
.. configuration-block::
293+
294+
.. code-block:: yaml
295+
296+
# config/packages/security.yaml
297+
security:
298+
firewalls:
299+
main:
300+
access_token:
301+
token_handler: App\Security\AccessTokenHandler
302+
success_handler: App\Security\Authentication\AuthenticationSuccessHandler
303+
304+
.. code-block:: xml
305+
306+
<!-- config/packages/security.xml -->
307+
<?xml version="1.0" encoding="UTF-8"?>
308+
<srv:container xmlns="http://symfony.com/schema/dic/security"
309+
xmlns:srv="http://symfony.com/schema/dic/services"
310+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
311+
xsi:schemaLocation="http://symfony.com/schema/dic/services
312+
https://symfony.com/schema/dic/services/services-1.0.xsd
313+
http://symfony.com/schema/dic/security
314+
https://symfony.com/schema/dic/security/security-1.0.xsd">
315+
316+
<config>
317+
<firewall name="main">
318+
<access-token token-handler="App\Security\AccessTokenHandler"
319+
success-handler="App\Security\Authentication\AuthenticationSuccessHandler"
320+
/>
321+
</firewall>
322+
</config>
323+
</srv:container>
324+
325+
.. code-block:: php
326+
327+
// config/packages/security.php
328+
use App\Security\AccessTokenHandler;
329+
use App\Security\Authentication\AuthenticationSuccessHandler;
330+
use Symfony\Config\SecurityConfig;
331+
332+
return static function (SecurityConfig $security) {
333+
$security->firewall('main')
334+
->accessToken()
335+
->tokenHandler(AccessTokenHandler::class)
336+
->successHandler(AuthenticationSuccessHandler::class)
337+
;
338+
};
339+
340+
.. tip::
341+
342+
If you want to customize the default failure handling, use the
343+
``failure_handler`` option and create a class that implements
344+
:class:`Symfony\\Component\\Security\\Http\\Authentication\\AuthenticationFailureHandlerInterface`.
345+
346+
.. _`Json Web Tokens (JWT)`: https://datatracker.ietf.org/doc/html/rfc7519
347+
.. _`SAML2 (XML structures)`: https://docs.oasis-open.org/security/saml/Post2.0/sstc-saml-tech-overview-2.0.html
348+
.. _`RFC6750`: https://datatracker.ietf.org/doc/html/rfc6750

0 commit comments

Comments
 (0)