4
4
How to use Access Token Authentication
5
5
======================================
6
6
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.
10
12
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.
13
17
14
18
Using the Access Token Authenticator
15
19
------------------------------------
@@ -22,9 +26,10 @@ this is not yet the case.
22
26
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
23
27
24
28
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.).
28
33
29
34
.. configuration-block ::
30
35
@@ -37,69 +42,108 @@ the token (e.g. revocation, expiration time, digital signature...).
37
42
access_token :
38
43
token_handler : App\Security\AccessTokenHandler
39
44
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 >
46
63
47
64
.. code-block :: php
48
65
49
- // src/Security/AccessTokenHandler.php
50
- namespace App\Security;
66
+ // config/packages/security.php
67
+ use App\Security\AccessTokenHandler;
68
+ use Symfony\Config\SecurityConfig;
51
69
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
+ };
54
76
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;
61
82
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;
68
85
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.');
70
99
}
100
+
101
+ // and return the user identifier from the found token
102
+ return $accessToken->getUserId();
71
103
}
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 >`.
72
108
73
109
.. caution ::
74
110
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 ``.
80
116
81
- Customizing the Authenticator
82
- -----------------------------
117
+ 2) Configure the Token Extractor (Optional)
118
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
83
119
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).
85
122
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 ``).
88
126
89
- This authenticator provides services able to extract the access token as per the RFC6750:
127
+ Symfony provides other extractors as per the ` RFC6750 `_ :
90
128
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 ``.
94
137
95
138
.. caution ::
96
139
97
140
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.
101
145
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
103
147
:class: `Symfony\\ Component\\ Security\\ Http\\ AccessToken\\ AccessTokenExtractorInterface `.
104
148
105
149
.. configuration-block ::
@@ -112,10 +156,60 @@ Also, you can also create a custom extractor. The class shall implement the inte
112
156
main :
113
157
access_token :
114
158
token_handler : App\Security\AccessTokenHandler
115
- token_extractors : ' my_custom_access_token_extractor'
116
159
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.
119
213
120
214
.. configuration-block ::
121
215
@@ -129,37 +223,70 @@ In this case, **the order is important**: the first in the list is called first.
129
223
token_handler : App\Security\AccessTokenHandler
130
224
token_extractors :
131
225
- ' 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 >
135
249
136
- 2) Customizing the Success Handler
250
+ .. code-block :: php
137
251
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;
142
256
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
+ };
145
267
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
+ ~~~~~~~~~~~~~~~~~~~
150
270
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.
157
273
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:
161
276
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 ``:
163
290
164
291
.. configuration-block ::
165
292
@@ -173,10 +300,48 @@ Then, configure this service ID as the ``success_handler``:
173
300
token_handler : App\Security\AccessTokenHandler
174
301
success_handler : App\Security\Authentication\AuthenticationSuccessHandler
175
302
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
+
176
339
.. tip ::
177
340
178
341
If you want to customize the default failure handling, use the
179
342
``failure_handler `` option and create a class that implements
180
343
:class: `Symfony\\ Component\\ Security\\ Http\\ Authentication\\ AuthenticationFailureHandlerInterface `.
181
344
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
182
347
.. _`RFC6750` : https://datatracker.ietf.org/doc/html/rfc6750
0 commit comments