From dbd8bb9b4c37cb3691f8b06fc53e6b8834f105da Mon Sep 17 00:00:00 2001 From: gondo Date: Sat, 16 Jan 2016 17:50:20 +0100 Subject: [PATCH 1/3] fixes `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 --- cookbook/security/api_key_authentication.rst | 57 +++++++++++++++++--- 1 file changed, 49 insertions(+), 8 deletions(-) diff --git a/cookbook/security/api_key_authentication.rst b/cookbook/security/api_key_authentication.rst index 745a5792830..b21243f1939 100644 --- a/cookbook/security/api_key_authentication.rst +++ b/cookbook/security/api_key_authentication.rst @@ -54,6 +54,11 @@ value and then a User object is created:: ); } + public function supportsToken(TokenInterface $token, $providerKey) + { + return $token instanceof PreAuthenticatedToken && $token->getProviderKey() === $providerKey; + } + public function authenticateToken(TokenInterface $token, UserProviderInterface $userProvider, $providerKey) { if (!$userProvider instanceof ApiKeyUserProvider) { @@ -83,11 +88,6 @@ value and then a User object is created:: $user->getRoles() ); } - - public function supportsToken(TokenInterface $token, $providerKey) - { - return $token instanceof PreAuthenticatedToken && $token->getProviderKey() === $providerKey; - } } Once you've :ref:`configured ` everything, @@ -177,7 +177,7 @@ The ``$userProvider`` might look something like this:: null, // the roles for the user - you may choose to determine // these dynamically somehow based on the user - array('ROLE_USER') + array('ROLE_API') ); } @@ -249,6 +249,7 @@ would allow you to have custom data on the ``User`` object. Finally, just make sure that ``supportsClass()`` returns ``true`` for User objects with the same class as whatever user you return in ``loadUserByUsername()``. + If your authentication is stateless like in this example (i.e. you expect the user to send the API key with every request and so you don't save the login to the session), then you can simply throw the ``UnsupportedUserException`` @@ -262,7 +263,7 @@ exception in ``refreshUser()``. Handling Authentication Failure ------------------------------- -In order for your ``ApiKeyAuthenticator`` to correctly display a 403 +In order for your ``ApiKeyAuthenticator`` to correctly display a 401 http status when either bad credentials or authentication fails you will need to implement the :class:`Symfony\\Component\\Security\\Http\\Authentication\\AuthenticationFailureHandlerInterface` on your Authenticator. This will provide a method ``onAuthenticationFailure`` which @@ -285,7 +286,7 @@ you can use to create an error ``Response``. public function onAuthenticationFailure(Request $request, AuthenticationException $exception) { - return new Response("Authentication Failed.", 403); + return new Response("Authentication Failed.", 401); } } @@ -411,6 +412,46 @@ using the ``simple_preauth`` and ``provider`` keys respectively: ), )); +If you have defined `access_control`, make sure to add new entry: + +.. configuration-block:: + + .. code-block:: yaml + + # app/config/security.yml + security: + # ... + + access_control: + - { path: ^/admin, roles: ROLE_API } + + .. code-block:: xml + + + + + + + + + .. code-block:: php + + // app/config/security.php + $container->loadFromExtension('security', array( + 'access_control' => array( + array( + 'path' => '^/admin', + 'role' => 'ROLE_API', + ), + ), + )); + That's it! Now, your ``ApiKeyAuthenticator`` should be called at the beginning of each request and your authentication process will take place. From 81dd5e7dbb013479cba6cf4b27bb077687672c5d Mon Sep 17 00:00:00 2001 From: gondo Date: Sat, 16 Jan 2016 17:54:04 +0100 Subject: [PATCH 2/3] removed message from BadCredentialsException removed message from BadCredentialsException as defining custom message is confusing, because `onAuthenticationFailure` is using `getMessageKey()` instead of `getMessage()` --- cookbook/security/api_key_authentication.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cookbook/security/api_key_authentication.rst b/cookbook/security/api_key_authentication.rst index b21243f1939..b4412d234f1 100644 --- a/cookbook/security/api_key_authentication.rst +++ b/cookbook/security/api_key_authentication.rst @@ -41,7 +41,7 @@ value and then a User object is created:: // $apiKey = $request->headers->get('apikey'); if (!$apiKey) { - throw new BadCredentialsException('No API key found'); + throw new BadCredentialsException(); // or to just skip api key authentication // return null; From f0088194b3ac63598394abedf32b9c7dd399dde5 Mon Sep 17 00:00:00 2001 From: WouterJ Date: Sat, 2 Jul 2016 13:59:02 +0200 Subject: [PATCH 3/3] Use a more realistic /api instead of /admin --- cookbook/security/api_key_authentication.rst | 24 +++++++++----------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/cookbook/security/api_key_authentication.rst b/cookbook/security/api_key_authentication.rst index b4412d234f1..a33e30449f9 100644 --- a/cookbook/security/api_key_authentication.rst +++ b/cookbook/security/api_key_authentication.rst @@ -92,7 +92,7 @@ value and then a User object is created:: Once you've :ref:`configured ` everything, you'll be able to authenticate by adding an apikey parameter to the query -string, like ``http://example.com/admin/foo?apikey=37b51d194a7513e45b56f6524f2d51f2``. +string, like ``http://example.com/api/foo?apikey=37b51d194a7513e45b56f6524f2d51f2``. The authentication process has several steps, and your implementation will probably differ: @@ -354,7 +354,7 @@ using the ``simple_preauth`` and ``provider`` keys respectively: firewalls: secured_area: - pattern: ^/admin + pattern: ^/api stateless: true simple_preauth: authenticator: apikey_authenticator @@ -377,7 +377,7 @@ using the ``simple_preauth`` and ``provider`` keys respectively: @@ -397,7 +397,7 @@ using the ``simple_preauth`` and ``provider`` keys respectively: $container->loadFromExtension('security', array( 'firewalls' => array( 'secured_area' => array( - 'pattern' => '^/admin', + 'pattern' => '^/api', 'stateless' => true, 'simple_preauth' => array( 'authenticator' => 'apikey_authenticator', @@ -412,7 +412,7 @@ using the ``simple_preauth`` and ``provider`` keys respectively: ), )); -If you have defined `access_control`, make sure to add new entry: +If you have defined ``access_control``, make sure to add a new entry: .. configuration-block:: @@ -423,7 +423,7 @@ If you have defined `access_control`, make sure to add new entry: # ... access_control: - - { path: ^/admin, roles: ROLE_API } + - { path: ^/api, roles: ROLE_API } .. code-block:: xml @@ -435,9 +435,7 @@ If you have defined `access_control`, make sure to add new entry: xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> - + .. code-block:: php @@ -446,7 +444,7 @@ If you have defined `access_control`, make sure to add new entry: $container->loadFromExtension('security', array( 'access_control' => array( array( - 'path' => '^/admin', + 'path' => '^/api', 'role' => 'ROLE_API', ), ), @@ -484,7 +482,7 @@ configuration or set it to ``false``: firewalls: secured_area: - pattern: ^/admin + pattern: ^/api stateless: false simple_preauth: authenticator: apikey_authenticator @@ -507,7 +505,7 @@ configuration or set it to ``false``: @@ -526,7 +524,7 @@ configuration or set it to ``false``: $container->loadFromExtension('security', array( 'firewalls' => array( 'secured_area' => array( - 'pattern' => '^/admin', + 'pattern' => '^/api', 'stateless' => false, 'simple_preauth' => array( 'authenticator' => 'apikey_authenticator',