44How 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
1418Using the Access Token Authenticator
1519------------------------------------
@@ -22,9 +26,10 @@ this is not yet the case.
2226~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2327
2428To 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