Skip to content

Commit f3629df

Browse files
committed
minor #6706 Update "How to Authenticate Users with API Keys" (gondo, WouterJ)
This PR was merged into the 2.7 branch. Discussion ---------- Update "How to Authenticate Users with API Keys" Finishes #6157 Original PR description: > * `supportsToken` should be defined above `authenticateToken` to reflect documentation numbering > * `onAuthenticationFailure` should return http code 401 Unauthorized (RFC 7235) not 403 Forbidden. > * added missing information about defining `access_control` - figuring this out kept me hanging for a while > * used `ROLE_API` instead of `ROLE_USER` to demonstrate `access_control` configuration Commits ------- f008819 Use a more realistic /api instead of /admin 81dd5e7 removed message from BadCredentialsException dbd8bb9 fixes
2 parents 7887aa5 + f008819 commit f3629df

File tree

1 file changed

+55
-16
lines changed

1 file changed

+55
-16
lines changed

cookbook/security/api_key_authentication.rst

Lines changed: 55 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ value and then a User object is created::
4141
// $apiKey = $request->headers->get('apikey');
4242

4343
if (!$apiKey) {
44-
throw new BadCredentialsException('No API key found');
44+
throw new BadCredentialsException();
4545

4646
// or to just skip api key authentication
4747
// return null;
@@ -54,6 +54,11 @@ value and then a User object is created::
5454
);
5555
}
5656

57+
public function supportsToken(TokenInterface $token, $providerKey)
58+
{
59+
return $token instanceof PreAuthenticatedToken && $token->getProviderKey() === $providerKey;
60+
}
61+
5762
public function authenticateToken(TokenInterface $token, UserProviderInterface $userProvider, $providerKey)
5863
{
5964
if (!$userProvider instanceof ApiKeyUserProvider) {
@@ -83,16 +88,11 @@ value and then a User object is created::
8388
$user->getRoles()
8489
);
8590
}
86-
87-
public function supportsToken(TokenInterface $token, $providerKey)
88-
{
89-
return $token instanceof PreAuthenticatedToken && $token->getProviderKey() === $providerKey;
90-
}
9191
}
9292

9393
Once you've :ref:`configured <cookbook-security-api-key-config>` everything,
9494
you'll be able to authenticate by adding an apikey parameter to the query
95-
string, like ``http://example.com/admin/foo?apikey=37b51d194a7513e45b56f6524f2d51f2``.
95+
string, like ``http://example.com/api/foo?apikey=37b51d194a7513e45b56f6524f2d51f2``.
9696

9797
The authentication process has several steps, and your implementation will
9898
probably differ:
@@ -177,7 +177,7 @@ The ``$userProvider`` might look something like this::
177177
null,
178178
// the roles for the user - you may choose to determine
179179
// these dynamically somehow based on the user
180-
array('ROLE_USER')
180+
array('ROLE_API')
181181
);
182182
}
183183

@@ -249,6 +249,7 @@ would allow you to have custom data on the ``User`` object.
249249

250250
Finally, just make sure that ``supportsClass()`` returns ``true`` for User
251251
objects with the same class as whatever user you return in ``loadUserByUsername()``.
252+
252253
If your authentication is stateless like in this example (i.e. you expect
253254
the user to send the API key with every request and so you don't save the
254255
login to the session), then you can simply throw the ``UnsupportedUserException``
@@ -262,7 +263,7 @@ exception in ``refreshUser()``.
262263
Handling Authentication Failure
263264
-------------------------------
264265

265-
In order for your ``ApiKeyAuthenticator`` to correctly display a 403
266+
In order for your ``ApiKeyAuthenticator`` to correctly display a 401
266267
http status when either bad credentials or authentication fails you will
267268
need to implement the :class:`Symfony\\Component\\Security\\Http\\Authentication\\AuthenticationFailureHandlerInterface` on your
268269
Authenticator. This will provide a method ``onAuthenticationFailure`` which
@@ -285,7 +286,7 @@ you can use to create an error ``Response``.
285286
286287
public function onAuthenticationFailure(Request $request, AuthenticationException $exception)
287288
{
288-
return new Response("Authentication Failed.", 403);
289+
return new Response("Authentication Failed.", 401);
289290
}
290291
}
291292
@@ -353,7 +354,7 @@ using the ``simple_preauth`` and ``provider`` keys respectively:
353354
354355
firewalls:
355356
secured_area:
356-
pattern: ^/admin
357+
pattern: ^/api
357358
stateless: true
358359
simple_preauth:
359360
authenticator: apikey_authenticator
@@ -376,7 +377,7 @@ using the ``simple_preauth`` and ``provider`` keys respectively:
376377
<!-- ... -->
377378
378379
<firewall name="secured_area"
379-
pattern="^/admin"
380+
pattern="^/api"
380381
stateless="true"
381382
provider="api_key_user_provider"
382383
>
@@ -396,7 +397,7 @@ using the ``simple_preauth`` and ``provider`` keys respectively:
396397
$container->loadFromExtension('security', array(
397398
'firewalls' => array(
398399
'secured_area' => array(
399-
'pattern' => '^/admin',
400+
'pattern' => '^/api',
400401
'stateless' => true,
401402
'simple_preauth' => array(
402403
'authenticator' => 'apikey_authenticator',
@@ -411,6 +412,44 @@ using the ``simple_preauth`` and ``provider`` keys respectively:
411412
),
412413
));
413414
415+
If you have defined ``access_control``, make sure to add a new entry:
416+
417+
.. configuration-block::
418+
419+
.. code-block:: yaml
420+
421+
# app/config/security.yml
422+
security:
423+
# ...
424+
425+
access_control:
426+
- { path: ^/api, roles: ROLE_API }
427+
428+
.. code-block:: xml
429+
430+
<!-- app/config/security.xml -->
431+
<?xml version="1.0" encoding="UTF-8"?>
432+
<srv:container xmlns="http://symfony.com/schema/dic/security"
433+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
434+
xmlns:srv="http://symfony.com/schema/dic/services"
435+
xsi:schemaLocation="http://symfony.com/schema/dic/services
436+
http://symfony.com/schema/dic/services/services-1.0.xsd">
437+
438+
<rule path="^/api" role="ROLE_API" />
439+
</srv:container>
440+
441+
.. code-block:: php
442+
443+
// app/config/security.php
444+
$container->loadFromExtension('security', array(
445+
'access_control' => array(
446+
array(
447+
'path' => '^/api',
448+
'role' => 'ROLE_API',
449+
),
450+
),
451+
));
452+
414453
That's it! Now, your ``ApiKeyAuthenticator`` should be called at the beginning
415454
of each request and your authentication process will take place.
416455

@@ -443,7 +482,7 @@ configuration or set it to ``false``:
443482
444483
firewalls:
445484
secured_area:
446-
pattern: ^/admin
485+
pattern: ^/api
447486
stateless: false
448487
simple_preauth:
449488
authenticator: apikey_authenticator
@@ -466,7 +505,7 @@ configuration or set it to ``false``:
466505
<!-- ... -->
467506
468507
<firewall name="secured_area"
469-
pattern="^/admin"
508+
pattern="^/api"
470509
stateless="false"
471510
provider="api_key_user_provider"
472511
>
@@ -485,7 +524,7 @@ configuration or set it to ``false``:
485524
$container->loadFromExtension('security', array(
486525
'firewalls' => array(
487526
'secured_area' => array(
488-
'pattern' => '^/admin',
527+
'pattern' => '^/api',
489528
'stateless' => false,
490529
'simple_preauth' => array(
491530
'authenticator' => 'apikey_authenticator',

0 commit comments

Comments
 (0)