Skip to content

Commit b65c136

Browse files
committed
Finish the docs for the new Access token authenticator
1 parent 600bb80 commit b65c136

File tree

1 file changed

+241
-76
lines changed

1 file changed

+241
-76
lines changed

security/access_token.rst

Lines changed: 241 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,16 @@
44
How to use Access Token Authentication
55
======================================
66

7-
Access tokens are commonly used in API contexts. The access token is obtained
8-
through an authorization server (or similar) whose role is to verify the user identity
9-
and receive consent before the token is issued.
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.
1012

11-
Access Tokens can be of any kind: opaque strings, Json Web Tokens (JWT) or SAML2 (XML structures).
12-
Please refer to the `RFC6750`_: *The OAuth 2.0 Authorization Framework: Bearer Token Usage*.
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.
1317

1418
Using the Access Token Authenticator
1519
------------------------------------
@@ -22,9 +26,10 @@ this is not yet the case.
2226
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2327

2428
To use the access token authenticator, you must configure a ``token_handler``.
25-
The token handler retrieves the user identifier from the token.
26-
In order to get the user identifier, implementations may need to load and validate
27-
the token (e.g. revocation, expiration time, digital signature...).
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.).
2833

2934
.. configuration-block::
3035

@@ -37,69 +42,108 @@ the token (e.g. revocation, expiration time, digital signature...).
3742
access_token:
3843
token_handler: App\Security\AccessTokenHandler
3944
40-
This handler shall implement the interface
41-
:class:`Symfony\\Component\\Security\\Http\\AccessToken\\AccessTokenHandlerInterface`.
42-
In the following example, the handler will retrieve the token from a database
43-
using a fictive repository.
44-
45-
.. configuration-block::
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>
4663
4764
.. code-block:: php
4865
49-
// src/Security/AccessTokenHandler.php
50-
namespace App\Security;
66+
// config/packages/security.php
67+
use App\Security\AccessTokenHandler;
68+
use Symfony\Config\SecurityConfig;
5169
52-
use App\Repository\AccessTokenRepository;
53-
use Symfony\Component\Security\Http\AccessToken\AccessTokenHandlerInterface;
70+
return static function (SecurityConfig $security) {
71+
$security->firewall('main')
72+
->accessToken()
73+
->tokenHandler(AccessTokenHandler::class)
74+
;
75+
};
5476
55-
class AccessTokenHandler implements AccessTokenHandlerInterface
56-
{
57-
public function __construct(
58-
private readonly AccessTokenRepository $repository
59-
) {
60-
}
77+
This handler must implement
78+
:class:`Symfony\\Component\\Security\\Http\\AccessToken\\AccessTokenHandlerInterface`::
79+
80+
// src/Security/AccessTokenHandler.php
81+
namespace App\Security;
6182

62-
public function getUserIdentifierFrom(string $token): string
63-
{
64-
$accessToken = $this->repository->findOneByValue($token);
65-
if ($accessToken === null || !$accessToken->isValid()) {
66-
throw new BadCredentialsException('Invalid credentials.');
67-
}
83+
use App\Repository\AccessTokenRepository;
84+
use Symfony\Component\Security\Http\AccessToken\AccessTokenHandlerInterface;
6885

69-
return $accessToken->getUserId();
86+
class AccessTokenHandler implements AccessTokenHandlerInterface
87+
{
88+
public function __construct(
89+
private AccessTokenRepository $repository
90+
) {
91+
}
92+
93+
public function getUserIdentifierFrom(string $token): string
94+
{
95+
// e.g. query the "access token" database to search for this token
96+
$accessToken = $this->repository->findOneByValue($token);
97+
if ($accessToken === null || !$accessToken->isValid()) {
98+
throw new BadCredentialsException('Invalid credentials.');
7099
}
100+
101+
// and return the user identifier from the found token
102+
return $accessToken->getUserId();
71103
}
104+
}
105+
106+
The access token authenticator will use the returned user identifier to
107+
load the user using the :ref:`user provider <security-user-providers>`.
72108

73109
.. caution::
74110

75-
It is important to check the token is valid.
76-
For instance, in the example we verify the token has not expired.
77-
With self-contained access tokens such as JWT, the handler is required to
78-
verify the digital signature and understand all claims,
79-
especially ``sub``, ``iat``, ``nbf`` and ``exp``.
111+
It is important to check the token if is valid. For instance, the
112+
example above verifies whether the token has not expired. With
113+
self-contained access tokens such as JWT, the handler is required to
114+
verify the digital signature and understand all claims, especially
115+
``sub``, ``iat``, ``nbf`` and ``exp``.
80116

81-
Customizing the Authenticator
82-
-----------------------------
117+
2) Configure the Token Extractor (Optional)
118+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
83119

84-
1) Access Token Extractors
120+
The application is now ready to handle incoming tokens. A *token extractor*
121+
retrieves the token from the request (e.g. a header or request body).
85122

86-
By default, the access token is read from the request header parameter ``Authorization`` with the scheme ``Bearer``.
87-
You can change the behavior and send the access token through different ways.
123+
By default, the access token is read from the request header parameter
124+
``Authorization`` with the scheme ``Bearer`` (e.g. ``Authorization: Bearer
125+
the-token-value``).
88126

89-
This authenticator provides services able to extract the access token as per the RFC6750:
127+
Symfony provides other extractors as per the `RFC6750`_:
90128

91-
- ``header`` or ``security.access_token_extractor.header``: the token is sent through the request header. Usually ``Authorization`` with the ``Bearer`` scheme.
92-
- ``query_string`` or ``security.access_token_extractor.query_string``: the token is part of the query string. Usually ``access_token``.
93-
- ``request_body`` or ``security.access_token_extractor.request_body``: the token is part of the request body during a POST request. Usually ``access_token``.
129+
``header`` (default)
130+
The token is sent through the request header. Usually ``Authorization``
131+
with the ``Bearer`` scheme.
132+
``query_string``
133+
The token is part of the request query string. Usually ``access_token``.
134+
``request_body``
135+
The token is part of the request body during a POST request. Usually
136+
``access_token``.
94137

95138
.. caution::
96139

97140
Because of the security weaknesses associated with the URI method,
98-
including the high likelihood that the URL or the request body containing the access token will be logged,
99-
methods ``query_string`` and ``request_body`` **SHOULD NOT** be used unless it is impossible
100-
to transport the access token in the request header field.
141+
including the high likelihood that the URL or the request body
142+
containing the access token will be logged, methods ``query_string``
143+
and ``request_body`` **SHOULD NOT** be used unless it is impossible to
144+
transport the access token in the request header field.
101145

102-
Also, you can also create a custom extractor. The class shall implement the interface
146+
You can also create a custom extractor. The class must implement
103147
:class:`Symfony\\Component\\Security\\Http\\AccessToken\\AccessTokenExtractorInterface`.
104148

105149
.. configuration-block::
@@ -112,10 +156,60 @@ Also, you can also create a custom extractor. The class shall implement the inte
112156
main:
113157
access_token:
114158
token_handler: App\Security\AccessTokenHandler
115-
token_extractors: 'my_custom_access_token_extractor'
116159
117-
It is possible to set multiple extractors.
118-
In this case, **the order is important**: the first in the list is called first.
160+
# use a different built-in extractor
161+
token_extractors: request_body
162+
163+
# or provide the service ID of a custom extractor
164+
token_extractors: 'App\Security\CustomTokenExtractor'
165+
166+
.. code-block:: xml
167+
168+
<!-- config/packages/security.xml -->
169+
<?xml version="1.0" encoding="UTF-8"?>
170+
<srv:container xmlns="http://symfony.com/schema/dic/security"
171+
xmlns:srv="http://symfony.com/schema/dic/services"
172+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
173+
xsi:schemaLocation="http://symfony.com/schema/dic/services
174+
https://symfony.com/schema/dic/services/services-1.0.xsd
175+
http://symfony.com/schema/dic/security
176+
https://symfony.com/schema/dic/security/security-1.0.xsd">
177+
178+
<config>
179+
<firewall name="main">
180+
<access-token token-handler="App\Security\AccessTokenHandler">
181+
<!-- use a different built-in extractor -->
182+
<token-extractor>request_body</token-extractor>
183+
184+
<!-- or provide the service ID of a custom extractor -->
185+
<token-extractor>App\Security\CustomTokenExtractor</token-extractor>
186+
</access-token>
187+
</firewall>
188+
</config>
189+
</srv:container>
190+
191+
.. code-block:: php
192+
193+
// config/packages/security.php
194+
use App\Security\AccessTokenHandler;
195+
use App\Security\CustomTokenExtractor;
196+
use Symfony\Config\SecurityConfig;
197+
198+
return static function (SecurityConfig $security) {
199+
$security->firewall('main')
200+
->accessToken()
201+
->tokenHandler(AccessTokenHandler::class)
202+
203+
// use a different built-in extractor
204+
->tokenExtractors('request_body')
205+
206+
# or provide the service ID of a custom extractor
207+
->tokenExtractors(CustomTokenExtractor::class)
208+
;
209+
};
210+
211+
It is possible to set multiple extractors. In this case, **the order is
212+
important**: the first in the list is called first.
119213

120214
.. configuration-block::
121215

@@ -129,37 +223,70 @@ In this case, **the order is important**: the first in the list is called first.
129223
token_handler: App\Security\AccessTokenHandler
130224
token_extractors:
131225
- 'header'
132-
- 'request_body'
133-
- 'query_string'
134-
- 'my_custom_access_token_extractor'
226+
- 'App\Security\CustomTokenExtractor'
227+
228+
.. code-block:: xml
229+
230+
<!-- config/packages/security.xml -->
231+
<?xml version="1.0" encoding="UTF-8"?>
232+
<srv:container xmlns="http://symfony.com/schema/dic/security"
233+
xmlns:srv="http://symfony.com/schema/dic/services"
234+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
235+
xsi:schemaLocation="http://symfony.com/schema/dic/services
236+
https://symfony.com/schema/dic/services/services-1.0.xsd
237+
http://symfony.com/schema/dic/security
238+
https://symfony.com/schema/dic/security/security-1.0.xsd">
239+
240+
<config>
241+
<firewall name="main">
242+
<access-token token-handler="App\Security\AccessTokenHandler">
243+
<token-extractor>header</token-extractor>
244+
<token-extractor>App\Security\CustomTokenExtractor</token-extractor>
245+
</access-token>
246+
</firewall>
247+
</config>
248+
</srv:container>
135249
136-
2) Customizing the Success Handler
250+
.. code-block:: php
137251
138-
Sometimes, the default success handling does not fit your use-case (e.g.
139-
when you need to generate and return additional response header parameters).
140-
To customize how the success handler behaves, create your own handler as a class that implements
141-
:class:`Symfony\\Component\\Security\\Http\\Authentication\\AuthenticationSuccessHandlerInterface`::
252+
// config/packages/security.php
253+
use App\Security\AccessTokenHandler;
254+
use App\Security\CustomTokenExtractor;
255+
use Symfony\Config\SecurityConfig;
142256
143-
// src/Security/Authentication/AuthenticationSuccessHandler.php
144-
namespace App\Security\Authentication;
257+
return static function (SecurityConfig $security) {
258+
$security->firewall('main')
259+
->accessToken()
260+
->tokenHandler(AccessTokenHandler::class)
261+
->tokenExtractors([
262+
'header',
263+
CustomTokenExtractor::class,
264+
])
265+
;
266+
};
145267
146-
use Symfony\Component\HttpFoundation\JsonResponse;
147-
use Symfony\Component\HttpFoundation\Request;
148-
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
149-
use Symfony\Component\Security\Http\Authentication\AuthenticationSuccessHandlerInterface;
268+
3) Submit a Request
269+
~~~~~~~~~~~~~~~~~~~
150270

151-
class AuthenticationSuccessHandler implements AuthenticationSuccessHandlerInterface
152-
{
153-
public function onAuthenticationSuccess(Request $request, TokenInterface $token): JsonResponse
154-
{
155-
$user = $token->getUser();
156-
$userApiToken = $user->getApiToken();
271+
That's it! Your application can now authenticate incoming requests using an
272+
API token.
157273

158-
return new JsonResponse(['apiToken' => $userApiToken]);
159-
}
160-
}
274+
Using the default header extractor, you can test the feature by submitting
275+
a request like this:
161276

162-
Then, configure this service ID as the ``success_handler``:
277+
.. code-block:: terminal
278+
279+
$ curl -H 'Authorization: Bearer an-accepted-token-value' \
280+
https://localhost:8000/api/some-route
281+
282+
Customizing the Success Handler
283+
-------------------------------
284+
285+
By default, the request continues (e.g. the controller for the route is
286+
run). If you want to customize success handling, create your own success
287+
handler by creating a class that implements
288+
:class:`Symfony\\Component\\Security\\Http\\Authentication\\AuthenticationSuccessHandlerInterface`
289+
and configure the service ID as the ``success_handler``:
163290

164291
.. configuration-block::
165292

@@ -173,10 +300,48 @@ Then, configure this service ID as the ``success_handler``:
173300
token_handler: App\Security\AccessTokenHandler
174301
success_handler: App\Security\Authentication\AuthenticationSuccessHandler
175302
303+
.. code-block:: xml
304+
305+
<!-- config/packages/security.xml -->
306+
<?xml version="1.0" encoding="UTF-8"?>
307+
<srv:container xmlns="http://symfony.com/schema/dic/security"
308+
xmlns:srv="http://symfony.com/schema/dic/services"
309+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
310+
xsi:schemaLocation="http://symfony.com/schema/dic/services
311+
https://symfony.com/schema/dic/services/services-1.0.xsd
312+
http://symfony.com/schema/dic/security
313+
https://symfony.com/schema/dic/security/security-1.0.xsd">
314+
315+
<config>
316+
<firewall name="main">
317+
<access-token token-handler="App\Security\AccessTokenHandler"
318+
success-handler="App\Security\Authentication\AuthenticationSuccessHandler"
319+
/>
320+
</firewall>
321+
</config>
322+
</srv:container>
323+
324+
.. code-block:: php
325+
326+
// config/packages/security.php
327+
use App\Security\AccessTokenHandler;
328+
use App\Security\Authentication\AuthenticationSuccessHandler;
329+
use Symfony\Config\SecurityConfig;
330+
331+
return static function (SecurityConfig $security) {
332+
$security->firewall('main')
333+
->accessToken()
334+
->tokenHandler(AccessTokenHandler::class)
335+
->successHandler(AuthenticationSuccessHandler::class)
336+
;
337+
};
338+
176339
.. tip::
177340

178341
If you want to customize the default failure handling, use the
179342
``failure_handler`` option and create a class that implements
180343
:class:`Symfony\\Component\\Security\\Http\\Authentication\\AuthenticationFailureHandlerInterface`.
181344

345+
.. _`Json Web Tokens (JWT)`: https://datatracker.ietf.org/doc/html/rfc7519
346+
.. _`SAML2 (XML structures)`: https://docs.oasis-open.org/security/saml/Post2.0/sstc-saml-tech-overview-2.0.html
182347
.. _`RFC6750`: https://datatracker.ietf.org/doc/html/rfc6750

0 commit comments

Comments
 (0)