This bundle requires Symfony 3.4+ and the openssl extension.
Protip: Though the bundle doesn't enforce you to do so, it is highly recommended to use HTTPS.
Add lexik/jwt-authentication-bundle
to your composer.json file:
php composer.phar require "lexik/jwt-authentication-bundle"
Register the bundle in app/AppKernel.php:
public function registerBundles()
{
return array(
// ...
new Lexik\Bundle\JWTAuthenticationBundle\LexikJWTAuthenticationBundle(),
);
}Generate the SSH keys :
$ mkdir -p config/jwt # For Symfony3+, no need of the -p option
$ openssl genrsa -out config/jwt/private.pem -aes256 4096
$ openssl rsa -pubout -in config/jwt/private.pem -out config/jwt/public.pemIn case first openssl command forces you to input password use following to get the private key decrypted
$ openssl rsa -in config/jwt/private.pem -out config/jwt/private2.pem
$ mv config/jwt/private.pem config/jwt/private.pem-back
$ mv config/jwt/private2.pem config/jwt/private.pemConfigure the SSH keys path in your config/packages/lexik_jwt_authentication.yaml :
lexik_jwt_authentication:
secret_key: '%kernel.project_dir%/config/jwt/private.pem' # required for token creation
public_key: '%kernel.project_dir%/config/jwt/public.pem' # required for token verification
pass_phrase: 'your_secret_passphrase' # required for token creation, usage of an environment variable is recommended
token_ttl: 3600Configure your config/packages/security.yaml :
security:
# ...
firewalls:
login:
pattern: ^/api/login
stateless: true
anonymous: true
json_login:
check_path: /api/login_check
success_handler: lexik_jwt_authentication.handler.authentication_success
failure_handler: lexik_jwt_authentication.handler.authentication_failure
api:
pattern: ^/api
stateless: true
guard:
authenticators:
- lexik_jwt_authentication.jwt_token_authenticator
access_control:
- { path: ^/api/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/api, roles: IS_AUTHENTICATED_FULLY }Configure your config/routes.yaml :
api_login_check:
path: /api/login_checkThe first step is to authenticate the user using its credentials. A classical form_login on an anonymously accessible firewall will do perfect.
Just set the provided lexik_jwt_authentication.handler.authentication_success service as success handler to
generate the token and send it as part of a json response body.
Store it (client side), the JWT is reusable until its ttl has expired (3600 seconds by default).
Note: You can test getting the token with a simple curl command like this:
curl -X POST -H "Content-Type: application/json" http://localhost:8000/api/login_check -d '{"username":"johndoe","password":"test"}'If it works, you will receive something like this:
{
"token" : "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXUyJ9.eyJleHAiOjE0MzQ3Mjc1MzYsInVzZXJuYW1lIjoia29ybGVvbiIsImlhdCI6IjE0MzQ2NDExMzYifQ.nh0L_wuJy6ZKIQWh6OrW5hdLkviTs1_bau2GqYdDCB0Yqy_RplkFghsuqMpsFls8zKEErdX5TYCOR7muX0aQvQxGQ4mpBkvMDhJ4-pE4ct2obeMTr_s4X8nC00rBYPofrOONUOR4utbzvbd4d2xT_tj4TdR_0tsr91Y7VskCRFnoXAnNT-qQb7ci7HIBTbutb9zVStOFejrb4aLbr7Fl4byeIEYgp2Gd7gY"
}Simply pass the JWT on each request to the protected firewall, either as an authorization header or as a query parameter.
By default only the authorization header mode is enabled : Authorization: Bearer {token}
See configuration reference document to enable query string parameter mode or change the header value prefix.
See Functionally testing a JWT protected api document or the sandbox application (Symfony2 or Symfony4) for a fully working example.
Each request after token expiration will result in a 401 response. Redo the authentication process to obtain a new token.
Maybe you want to use a refresh token to renew your JWT. In this case you can check JWTRefreshTokenBundle.
This is more of a Symfony2 related topic, but see Working with CORS requests document to get a quick explanation on handling CORS requests.
Using form_login security factory is very straightforward but it involves cookies exchange, even if the stateless parameter is set to true.
This may not be a problem depending on the system that makes calls to your API (like a typical SPA). But if it is, take a look at the GfreeauGetJWTBundle, which provides a stateless replacement for form_login.
For impersonating users using JWT, see https://symfony.com/doc/current/security/impersonating_user.html
As stated in this link and this one, Apache server will strip any Authorization header not in a valid HTTP BASIC AUTH format.
If you intend to use the authorization header mode of this bundle (and you should), please add those rules to your VirtualHost configuration :
SetEnvIf Authorization "(.*)" HTTP_AUTHORIZATION=$1The following documents are available: