JWT Authentication
JSON Web Token (JWT) is a JSON-based open standard (RFC 7519) for creating access tokens that assert some number of claims. For example, a server could generate a token that has the claim "logged in as admin" and provide that to a client. The client could then use that token to prove that he/she is logged in as admin. The tokens are signed by the server's key, so the server is able to verify that the token is legitimate. The tokens are designed to be compact, URL-safe and usable especially in web browser single sign-on (SSO) context.
API Platform allows to easily add a JWT-based authentication to your API using LexikJWTAuthenticationBundle. To install this bundle, just follow its documentation.
Installing LexikJWTAuthenticationBundle
LexikJWTAuthenticationBundle
requires your application to have a properly configured user provider.
You can either use the Doctrine user provider provided
by Symfony (recommended), create a custom user provider
or use API Platform's FOSUserBundle integration.
Here's a sample configuration using the data provider provided by FOSUserBundle:
# app/config/security.yml
security:
encoders:
FOS\UserBundle\Model\UserInterface: bcrypt
role_hierarchy:
ROLE_READER: ROLE_USER
ROLE_ADMIN: ROLE_READER
providers:
fos_userbundle:
id: fos_user.user_provider.username
firewalls:
login:
pattern: ^/login
stateless: true
anonymous: true
provider: fos_userbundle
json_login:
check_path: /login_check
username_path: email
password_path: password
success_handler: lexik_jwt_authentication.handler.authentication_success
failure_handler: lexik_jwt_authentication.handler.authentication_failure
main:
pattern: ^/
provider: fos_userbundle
stateless: true
anonymous: true
lexik_jwt: ~
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
access_control:
- { path: ^/login, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/books, roles: [ ROLE_READER ] }
- { path: ^/, roles: [ ROLE_READER ] }
Documenting the Authentication Mechanism with Swagger/Open API
Want to test the routes of your JWT-authentication-protected API?
Configuring API Platform
# app/config/config.yml
api_platform:
swagger:
api_keys:
apiKey:
name: Authorization
type: header
And the "Authorize" button will automatically appear in Swagger UI.
Adding a New API Key
All you have to do is configuring the API key in the value
field.
By default, only the authorization header mode is enabled in LexikJWTAuthenticationBundle.
You must set the JWT token as below and click on the "Authorize" button.
Bearer MY_NEW_TOKEN
Testing with Behat
Let's configure Behat to automatically send an Authorization
HTTP header containing a valid JWT token when a scenario is marked with a @login
annotation. Edit features/bootstrap/FeatureContext.php
and add the following methods:
<?php
// features/bootstrap/FeatureContext.php
use AppBundle\Entity\User;
use Behat\Behat\Hook\Scope\BeforeScenarioScope;
use Behatch\Context\RestContext;
class FeatureContext implements Context, SnippetAcceptingContext
{
// ...
// Must be after createDatabase() and dropDatabase() functions (the order matters)
/**
* @BeforeScenario
* @login
*
* @see https://symfony.com/doc/current/security/entity_provider.html#creating-your-first-user
*/
public function login(BeforeScenarioScope $scope)
{
$user = new User();
$user->setUsername('admin');
$user->setPassword('ATestPassword');
$user->setEmail('test@test.com');
$this->manager->persist($user);
$this->manager->flush();
$token = $this->jwtManager->create($user);
$this->restContext = $scope->getEnvironment()->getContext(RestContext::class);
$this->restContext->iAddHeaderEqualTo('Authorization', "Bearer $token");
}
/**
* @AfterScenario
* @logout
*/
public function logout() {
$this->restContext->iAddHeaderEqualTo('Authorization', '');
}
}
Then, update behat.yml
to inject the lexik_jwt_authentication.jwt_manager
:
# behat.yml
default:
# ...
suites:
default:
contexts:
- FeatureContext: { doctrine: '@doctrine', 'jwtManager': '@lexik_jwt_authentication.jwt_manager' }
- Behat\MinkExtension\Context\MinkContext
- Behatch\Context\RestContext
- Behatch\Context\JsonContext
# ...
Finally, mark your scenarios with the @login
annotation to automatically add a valid Authorization
header, and with @logout
to be sure to destroy the token after this scenario.