kleijnweb / jwt-bundle
Symfony JWT authentication with support for asymmetric keys and externally loaded secrets
Installs: 1 046
Dependents: 0
Suggesters: 0
Security: 0
Stars: 5
Watchers: 3
Forks: 2
Open Issues: 6
Type:symfony-bundle
Requires
- php: ^7.0.0
- symfony/config: >=2.8.30
- symfony/dependency-injection: >=2.8.30
- symfony/event-dispatcher: >=2.8.30
- symfony/finder: >=2.8.30
- symfony/http-foundation: >=2.8.30
- symfony/http-kernel: >=2.8.30
- symfony/security: >=2.8.30
- symfony/security-bundle: >=2.8.30
- symfony/var-dumper: >=2.8.30
- symfony/yaml: >=2.8.30
Requires (Dev)
- mikey179/vfsstream: ^1.6
- phpunit/phpunit: ^6.5
- satooshi/php-coveralls: <1.0
- squizlabs/php_codesniffer: ^3.2
- symfony/browser-kit: >=2.8.30
- symfony/form: >=2.8.30
- symfony/framework-bundle: >=2.8.30
- symfony/monolog-bundle: ^3.1
README
Integrate JWT API tokens for authentication.
Go to the release page to find details about the latest release.
For an example see swagger-bundle-example.
NOTE: Looking for PHP <7.0 and Symfony <2.8.7 support? Use a 0.x version.
Install
Install using composer (composer require kleijnweb/jwt-bundle
). You want to check out the release page to ensure you are getting what you want and optionally verify your download.
Authentication
The token is validated using standard (reserved) JWT claims:
Name | Type | Description |
---|---|---|
exp |
int [1] | Expiration time must be omitted [3] or be smaller than time() + leeway [2]. |
nbf |
int [1] | "Not before", token validity start time, must be omitted [3] or greater than or equal to time() - leeway [2]. |
iat |
int [1] | The time the token was issued, must be omitted [3] or smaller than configured minIssueTime + leeway . Required when minIssueTime configured. |
iss |
string | Issuer of the token, must match configured issuer . Required when issuer configured. |
aud |
string | JWT "audience", must be omitted [3] or match configured audience if configured. Required when audience configured. |
sub |
string | JWT "subject". Used as username for Symfony Security integration. Always required (or its alias), without it the "Resource Owner cannot be identified. |
prn |
string | JWT "principle". Deprecated alias for sub , used in older versions of the JWT RFC. |
jti |
string | JWT "ID". Not used, will be ignored. |
typ |
string | Not used, will be ignored. |
- [1] Unix time
- [2] The
leeway
allows a difference in seconds between the issuer of the token and the server running your app with JwtBundle. Keep at a low number, defaults to 0. - [3] Mark any claim required, including custom (non-reserved) ones, using the
require
configuration option.
All other claims encountered are ignored. The JWT header is checked for kid
(see below) and alg
, which must match the type
value of the key configuration.
Keys
The authenticator supports multiple keys, and allows all options to be configured per kid
(key ID, which must be included in the JWT header when more than 1 key is configured):
jwt: keys: keyOne: # Only one key, 'kid' is optional (but must match when provided) issuer: http://api.server.com/oauth2/token # OAuth2 example, but could be any string value audience: ~ # NULL, accept any minIssueTime: 1442132949 # Reject 'old' tokens, regardless of 'exp' require: [nbf, exp, my-claim] # Mark claims as required leeway: 5 # Allow 5 seconds of time de-synchronization (both ways) between this server and api.server.com
JwtBundle and the issuer must share a secret in order for JwtBundle to be able to verify tokens. You can choose between a pre shared key (PSK) or asymmetric keys.
jwt: keys: keyOne: # Must match 'kid' issuer: http://api.server1.com/oauth2/token secret: 'A Pre-Shared Key' # type: Defaults to HS256 (HMACSHA256). All options: HS256, HS512, RS256 and RS512 keyTwo: # Must match 'kid' issuer: http://api.server2.com/oauth2/token type: RS256 # RSA SHA256, needed for asymmetric keys secret: | -----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwND1VMVJ3BC/aM38tQRH 2GDHecXE8EsGoeAeBR5dFt3QC1/Eoub/F2kee3RBtI6I+kDBjrSDz5lsqh3Sm7N/ 47fTKZLvdBaHbCuYXVBQ2tZeEiUBESnsY2HUzXDlqSyDWohuiYeeL6gewxe1CnSE 0l8gYZ0Tx4ViPFYulva6siew0f4tBuSEwSPiKZQnGcssQYJ/VevTD6L4wGoDhkXV VvJ+qiNgmXXssgCl5vHs22y/RIgeOnDhkj81aB9Evx9iR7DOtyRBxnovrbN5gDwX m6IDw3fRhZQrVwZ816/eN+1sqpIMZF4oo4kRA4b64U04ex67A/6BwDDQ3LH0mD4d EwIDAQAB -----END PUBLIC KEY-----
To use asymmetric keys, type
MUST be set to RS256
or RS512
. The secret in this case is the public key of the issuer.
Loading Secrets From An External Source
Instead of configuring secrets statically, they can also be loaded dynamically, using any data available in the JWT token. Example configuration:
jwt: keys: keyThree: # Must match 'kid' issuer: http://api.server1.com/oauth2/token loader: 'my.loader.di.key'
The loader must implement KleijnWeb\JwtBundle\Authenticator\SecretLoader
. A simple example that loads the secret from an ambiguous data store:
use KleijnWeb\JwtBundle\Authenticator\JwtToken; use KleijnWeb\JwtBundle\Authenticator\SecretLoader; class SimpleSecretLoader implements SecretLoader { /** * @var DataStore */ private $store; /** * @param DataStore $store */ public function __construct(DataStore $store) { $this->store = $store; } /** * @param JwtToken $token * * @return string */ public function load(JwtToken $token) { return $this->store->loadSecretByUsername($token->getClaims()['sub']); } }
You could use any information available in the token, such as the kid
, alg
or any custom claims. You cannot configure both secret
and loader
. Be sure to throw an AuthenticationException
when appropriate (eg missing claims needed for loading secret).
Integration Into Symfony Security
Synopsis:
security: firewalls: default: stateless: true jwt: header: X-Header-Name # Defaults to "Authorization", in which case encountered "Bearer" prefixes are stripped provider: jwt providers: jwt: id: jwt.user_provider
Using the bundled user provider is optional. This will produce user objects from the token data alone with roles produced from the aud
claim (and IS_AUTHENTICATED_FULLY
whether aud
was set or not).
Assigning audience to user roles using an alternate UserProvider
JwtBundle can assign the audience claims in the JwtToken to the User objects user roles properties. Ideally, this is done in the UserProvider, so that the groups cannot be modified.
If this is an acceptable risk, you do not want to use JwtUser/JwtUserProvider, but do want JwtBundle to copy aud
claims to user roles, you can have your User class implement the KleijnWeb\JwtBundle\User\UnsafeGroupsUserInterface
interface, and JwtBundle will add the roles after the user is loaded from the provider.
This behavior may be removed in future versions.
NOTE: This function only copies the the roles from the token.
Issuing Token
Issuing tokens is currently limited to HS256
. To create a token string:
$token = new JwtToken([ 'header' => [ 'alg' => 'HS256', 'typ' => 'JWT', 'kid' => 'Optional Key ID' ], 'claims' => [ /* Array of claims */ ], 'secret' => 'Your Secret' ]); $token->getTokenString();
License
KleijnWeb\JwtBundle is made available under the terms of the LGPL, version 3.0.