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.
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/packages/security.yaml
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_email
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
guard:
authenticators:
- lexik_jwt_authentication.jwt_token_authenticator
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 ] }
Want to test the routes of your JWT-authentication-protected API?
# api/config/packages/api_platform.yaml
api_platform:
swagger:
api_keys:
apiKey:
name: Authorization
type: header
And the “Authorize” button will automatically appear in Swagger UI.
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
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 App\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('[email protected]');
$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.
Made with love by
Les-Tilleuls.coop can help you design and develop your APIs and web projects, and train your teams in API Platform, Symfony, Next.js, Kubernetes and a wide range of other technologies.
Learn more