diff --git a/docs/configuration.md b/docs/configuration.md index cfa6badf..387ffde3 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -9,45 +9,53 @@ It's meant for: * Providing custom implementation for the [extension points](extending-the-library.md) * Retrieving components (encoder, decoder, parser, validator, and builder) -## Usage - -In order to use it, you must: - -1. Initialise the configuration object -1. Customise the configuration object -1. Retrieve components - -### Configuration initialisation +## Initialisation The `Lcobucci\JWT\Signer\Key\InMemory` object is used for symmetric/asymmetric signature. To initialise it, you can pass the key content as a plain text: ```php +setBuilderFactory( + static function (ClaimsFormatter $formatter): Builder { + // This assumes `MyCustomBuilder` is an existing class + return new MyCustomBuilder(new JoseEncoder(), $formatter); + } +); +``` + +### Parser + +It configures how the token parser should be created. +It's useful when you want to provide a [custom Parser](extending-the-library.md#parser). + +```php +setParser(new MyParser()); +``` + +### Validator + +It configures how the token validator should be created. +It's useful when you want to provide a [custom Validator](extending-the-library.md#validator). + +```php +setValidator(new MyValidator()); +``` + +### Validation constraints + +It configures which are the base constraints to be used during validation. + +```php +setValidationConstraints( + new SignedWith($configuration->signer(), $configuration->signingKey()), + new StrictValidAt(SystemClock::fromUTC()), + new IssuedBy('https://api.my-awesome-company.com') +); +``` -### Retrieve components +## Retrieve components Once you've made all the necessary configuration you can pass the configuration object around your code and use the getters to retrieve the components: diff --git a/docs/issuing-tokens.md b/docs/issuing-tokens.md index ac242b5c..2ffdb6dd 100644 --- a/docs/issuing-tokens.md +++ b/docs/issuing-tokens.md @@ -1,59 +1,82 @@ # Issuing tokens -!!! Note - The examples here fetch the configuration object from a hypothetical dependency injection container. - You can create it in the same script or require it from a different file. It basically depends on how your system is bootstrapped. - -To issue new tokens you must create a new token a builder (easier when using the [configuration object](configuration.md)), customise it, and ask it to build the token: +To issue new tokens you must create a new token a builder, customise it, and ask it to build the token: ```php -use Lcobucci\JWT\Configuration; +get(Configuration::class); -assert($config instanceof Configuration); +require 'vendor/autoload.php'; + +$tokenBuilder = (new Builder(new JoseEncoder(), ChainedFormatter::default())); +$algorithm = new Sha256(); +$signingKey = InMemory::plainText(random_bytes(32)); $now = new DateTimeImmutable(); -$token = $config->builder() - // Configures the issuer (iss claim) - ->issuedBy('http://example.com') - // Configures the audience (aud claim) - ->permittedFor('http://example.org') - // Configures the id (jti claim) - ->identifiedBy('4f1g23a12aa') - // Configures the time that the token was issue (iat claim) - ->issuedAt($now) - // Configures the time that the token can be used (nbf claim) - ->canOnlyBeUsedAfter($now->modify('+1 minute')) - // Configures the expiration time of the token (exp claim) - ->expiresAt($now->modify('+1 hour')) - // Configures a new claim, called "uid" - ->withClaim('uid', 1) - // Configures a new header, called "foo" - ->withHeader('foo', 'bar') - // Builds a new token - ->getToken($config->signer(), $config->signingKey()); +$token = $tokenBuilder + // Configures the issuer (iss claim) + ->issuedBy('http://example.com') + // Configures the audience (aud claim) + ->permittedFor('http://example.org') + // Configures the id (jti claim) + ->identifiedBy('4f1g23a12aa') + // Configures the time that the token was issue (iat claim) + ->issuedAt($now) + // Configures the time that the token can be used (nbf claim) + ->canOnlyBeUsedAfter($now->modify('+1 minute')) + // Configures the expiration time of the token (exp claim) + ->expiresAt($now->modify('+1 hour')) + // Configures a new claim, called "uid" + ->withClaim('uid', 1) + // Configures a new header, called "foo" + ->withHeader('foo', 'bar') + // Builds a new token + ->getToken($algorithm, $signingKey); + +echo $token->toString(); ``` Once you've created a token, you're able to retrieve its data and convert it to its string representation: ```php -use Lcobucci\JWT\Configuration; +get(Configuration::class); -assert($config instanceof Configuration); +use Lcobucci\JWT\Encoding\ChainedFormatter; +use Lcobucci\JWT\Encoding\JoseEncoder; +use Lcobucci\JWT\Signer\Key\InMemory; +use Lcobucci\JWT\Signer\Hmac\Sha256; +use Lcobucci\JWT\Token\Builder; -$token = $config->builder() - ->issuedBy('http://example.com') - ->withClaim('uid', 1) - ->withHeader('foo', 'bar') - ->getToken($config->signer(), $config->signingKey()); +require 'vendor/autoload.php'; + +$tokenBuilder = (new Builder(new JoseEncoder(), ChainedFormatter::default())); +$algorithm = new Sha256(); +$signingKey = InMemory::plainText(random_bytes(32)); + +$token = $tokenBuilder + ->issuedBy('http://example.com') + ->withClaim('uid', 1) + ->withHeader('foo', 'bar') + ->getToken($algorithm, $signingKey); $token->headers(); // Retrieves the token headers $token->claims(); // Retrieves the token claims -echo $token->headers()->get('foo'); // will print "bar" -echo $token->claims()->get('iss'); // will print "http://example.com" -echo $token->claims()->get('uid'); // will print "1" +echo $token->headers()->get('foo'), PHP_EOL; // will print "bar" +echo $token->claims()->get('iss'), PHP_EOL; // will print "http://example.com" +echo $token->claims()->get('uid'), PHP_EOL; // will print "1" + +echo $token->toString(), PHP_EOL; // The string representation of the object is a JWT string -echo $token->toString(); // The string representation of the object is a JWT string ``` + +!!! Note + Some systems make use of components to handle dependency injection. + If your application follows that practice, using a [configuration object](configuration.md) might simplify the wiring of this library. diff --git a/docs/parsing-tokens.md b/docs/parsing-tokens.md index 0388d461..b154cb0d 100644 --- a/docs/parsing-tokens.md +++ b/docs/parsing-tokens.md @@ -1,29 +1,37 @@ # Parsing tokens -!!! Note - The examples here fetch the configuration object from a hypothetical dependency injection container. - You can create it in the same script or require it from a different file. It basically depends on how your system is bootstrapped. - -To parse a token you must create a new parser (easier when using the [configuration object](configuration.md)) and ask it to parse a string: +To parse a token you must create a new parser and ask it to parse a string: ```php -use Lcobucci\JWT\Configuration; +get(Configuration::class); -assert($config instanceof Configuration); +require 'vendor/autoload.php'; -$token = $config->parser()->parse( - 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.' - . 'eyJzdWIiOiIxMjM0NTY3ODkwIn0.' - . '2gSBz9EOsQRN9I-3iSxJoFt7NtgV6Rm0IL6a8CAwl3Q' -); +$parser = new Parser(new JoseEncoder()); +try { + $token = $parser->parse( + 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.' + . 'eyJzdWIiOiIxMjM0NTY3ODkwIn0.' + . '2gSBz9EOsQRN9I-3iSxJoFt7NtgV6Rm0IL6a8CAwl3Q' + ); +} catch (CannotDecodeContent | InvalidTokenStructure | UnsupportedHeaderFound $e) { + echo 'Oh no, an error: ' . $e->getMessage(); +} assert($token instanceof UnencryptedToken); -$token->headers(); // Retrieves the token headers -$token->claims(); // Retrieves the token claims +echo $token->claims()->get('sub'), PHP_EOL; // will print "1234567890" + ``` -!!! Important - In case of parsing errors the Parser will throw an exception of type `InvalidArgumentException`. +!!! Note + Some systems make use of components to handle dependency injection. + If your application follows that practice, using a [configuration object](configuration.md) might simplify the wiring of this library. diff --git a/docs/supported-algorithms.md b/docs/supported-algorithms.md new file mode 100644 index 00000000..9c7f632b --- /dev/null +++ b/docs/supported-algorithms.md @@ -0,0 +1,65 @@ +# Supported Algorithms + +This library supports signing and verifying tokens with both symmetric and asymmetric algorithms. +Encryption is not yet supported. + +Each algorithm will produce signature with different length. +If you have constraints regarding the length of the issued tokens, choose the algorithms that generate shorter output (`HS256`, `RS256`, `ES256`, and `BLAKE2B`). + +## Symmetric algorithms + +Symmetric algorithms perform signature creation and verification using the very same key/secret. +They're usually recommended for scenarios where these operations are handled by the very same component. + +| Name | Description | Class | Key length req. | +|-----------|--------------------|------------------------------------|-----------------| +| `HS256` | HMAC using SHA-256 | `\Lcobucci\JWT\Signer\Hmac\Sha256` | `>= 256 bits` | +| `HS384` | HMAC using SHA-384 | `\Lcobucci\JWT\Signer\Hmac\Sha384` | `>= 384 bits` | +| `HS512` | HMAC using SHA-512 | `\Lcobucci\JWT\Signer\Hmac\Sha512` | `>= 512 bits` | +| `BLAKE2B` | Blake2b keyed Hash | `\Lcobucci\JWT\Signer\Blake2b` | `>= 256 bits` | + +!!! Warning + Although `BLAKE2B` is fantastic due to its performance, it's not [JWT standard] and won't necessarily be offered by other libraries. + +### Deprecated items + +In `v4.2.0`, we introduced key length validation and added a way for users to still use non-recommended keys. +The following implementations will be **removed** in `v5.0.0` (use them carefully): + +| Name | Description | Class | Key length req. | +|---------|--------------------|------------------------------------------|-----------------| +| `HS256` | HMAC using SHA-256 | `\Lcobucci\JWT\Signer\Hmac\UnsafeSha256` | `>= 1 bit` | +| `HS384` | HMAC using SHA-384 | `\Lcobucci\JWT\Signer\Hmac\UnsafeSha384` | `>= 1 bit` | +| `HS512` | HMAC using SHA-512 | `\Lcobucci\JWT\Signer\Hmac\UnsafeSha512` | `>= 1 bit` | + + +## Asymmetric algorithms + +Asymmetric algorithms perform signature creation with private/secret keys and verification with public keys. +They're usually recommended for scenarios where creation is handled by a component and verification by many others. + +| Name | Description | Class | Key length req. | +|---------|---------------------------------|-------------------------------------|-----------------| +| `ES256` | ECDSA using P-256 and SHA-256 | `\Lcobucci\JWT\Signer\Ecdsa\Sha256` | `== 256 bits` | +| `ES384` | ECDSA using P-384 and SHA-384 | `\Lcobucci\JWT\Signer\Ecdsa\Sha384` | `== 384 bits` | +| `ES512` | ECDSA using P-521 and SHA-512 | `\Lcobucci\JWT\Signer\Ecdsa\Sha512` | `== 521 bits` | +| `RS256` | RSASSA-PKCS1-v1_5 using SHA-256 | `\Lcobucci\JWT\Signer\Rsa\Sha256` | `>= 2048 bits` | +| `RS384` | RSASSA-PKCS1-v1_5 using SHA-384 | `\Lcobucci\JWT\Signer\Rsa\Sha384` | `>= 2048 bits` | +| `RS512` | RSASSA-PKCS1-v1_5 using SHA-512 | `\Lcobucci\JWT\Signer\Rsa\Sha512` | `>= 2048 bits` | +| `EdDSA` | EdDSA signature algorithms | `\Lcobucci\JWT\Signer\Eddsa` | `>= 256 bits` | + +### Deprecated items + +In `v4.2.0`, we introduced key length validation and added a way for users to still use non-recommended keys. +The following implementations will be **removed** in `v5.0.0` (use them carefully): + +| Name | Description | Class | Key length req. | +|---------|---------------------------------|-------------------------------------------|-----------------| +| `ES256` | ECDSA using P-256 and SHA-256 | `\Lcobucci\JWT\Signer\Ecdsa\UnsafeSha256` | `>= 1 bit` | +| `ES384` | ECDSA using P-384 and SHA-384 | `\Lcobucci\JWT\Signer\Ecdsa\UnsafeSha384` | `>= 1 bit` | +| `ES512` | ECDSA using P-521 and SHA-512 | `\Lcobucci\JWT\Signer\Ecdsa\UnsafeSha512` | `>= 1 bit` | +| `RS256` | RSASSA-PKCS1-v1_5 using SHA-256 | `\Lcobucci\JWT\Signer\Rsa\UnsafeSha256` | `>= 1 bit` | +| `RS384` | RSASSA-PKCS1-v1_5 using SHA-384 | `\Lcobucci\JWT\Signer\Rsa\UnsafeSha384` | `>= 1 bit` | +| `RS512` | RSASSA-PKCS1-v1_5 using SHA-512 | `\Lcobucci\JWT\Signer\Rsa\UnsafeSha512` | `>= 1 bit` | + +[JWT standard]: https://www.iana.org/assignments/jose/jose.xhtml#web-signature-encryption-algorithms diff --git a/docs/validating-tokens.md b/docs/validating-tokens.md index 807f46c7..7111a310 100644 --- a/docs/validating-tokens.md +++ b/docs/validating-tokens.md @@ -1,36 +1,36 @@ # Validating tokens -!!! Note - The examples here fetch the configuration object from a hypothetical dependency injection container. - You can create it in the same script or require it from a different file. It basically depends on how your system is bootstrapped. - -To validate a token you must create a new validator (easier when using the [configuration object](configuration.md)) and assert or validate a token. +To validate a token you must create a new validator and assert or validate a token. ## Using `Lcobucci\JWT\Validator#assert()` -!!! Warning - You **MUST** provide at least one constraint, otherwise `\Lcobucci\JWT\Validation\NoConstraintsGiven` exception will be thrown. - This method goes through every single constraint in the set, groups all the violations, and throws an exception with the grouped violations: ```php -use Lcobucci\JWT\Configuration; -use Lcobucci\JWT\UnencryptedToken; +get(Configuration::class); -assert($config instanceof Configuration); +$parser = new Parser(new JoseEncoder()); -$token = $config->parser()->parse('...'); -assert($token instanceof UnencryptedToken); +$token = $parser->parse( + 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.' + . 'eyJzdWIiOiIxMjM0NTY3ODkwIn0.' + . '2gSBz9EOsQRN9I-3iSxJoFt7NtgV6Rm0IL6a8CAwl3Q' +); -// validation constraints can be set in the configuration with -// the setValidationConstraints() setter -// see the Customisation part of the Configuration documentation -$constraints = $config->validationConstraints(); +$validator = new Validator(); try { - $config->validator()->assert($token, ...$constraints); + $validator->assert($token, new RelatedTo('1234567891')); // doesn't throw na exception + $validator->assert($token, new RelatedTo('1234567890')); } catch (RequiredConstraintsViolated $e) { // list of constraints violation exceptions: var_dump($e->violations()); @@ -39,31 +39,43 @@ try { ## Using `Lcobucci\JWT\Validator#validate()` -!!! Warning - You **MUST** provide at least one constraint, otherwise `\Lcobucci\JWT\Validation\NoConstraintsGiven` exception will be thrown. - The difference here is that we'll always get a `boolean` result and stop in the very first violation: ```php -use Lcobucci\JWT\Configuration; -use Lcobucci\JWT\UnencryptedToken; +get(Configuration::class); -assert($config instanceof Configuration); +$token = $parser->parse( + 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.' + . 'eyJzdWIiOiIxMjM0NTY3ODkwIn0.' + . '2gSBz9EOsQRN9I-3iSxJoFt7NtgV6Rm0IL6a8CAwl3Q' +); -$token = $config->parser()->parse('...'); -assert($token instanceof UnencryptedToken); +$validator = new Validator(); -// validation constraints can be set in the configuration with -// the setValidationConstraints() setter -// see the Customisation part of the Configuration documentation -$constraints = $config->validationConstraints(); +if (! $validator->validate($token, new RelatedTo('1234567891'))) { + echo 'Invalid token (1)!', PHP_EOL; // will print this +} -if (! $config->validator()->validate($token, ...$constraints)) { - throw new RuntimeException('No way!'); +if (! $validator->validate($token, new RelatedTo('1234567890'))) { + echo 'Invalid token (2)!', PHP_EOL; // will not print this } ``` +!!! Note + Some systems make use of components to handle dependency injection. + If your application follows that practice, using a [configuration object](configuration.md) might simplify the wiring of this library. + + ## Available constraints This library provides the following constraints: @@ -75,14 +87,6 @@ This library provides the following constraints: * `Lcobucci\JWT\Validation\Constraint\SignedWith`: verifies if the token was signed with the expected signer and key * `Lcobucci\JWT\Validation\Constraint\StrictValidAt`: verifies presence and validity of the claims `iat`, `nbf`, and `exp` (supports leeway configuration) * `Lcobucci\JWT\Validation\Constraint\LooseValidAt`: verifies the claims `iat`, `nbf`, and `exp`, when present (supports leeway configuration) -* `Lcobucci\JWT\Validation\Constraint\HasClaimWithValue`: verifies that a custom claim has the expected value (not recommended when comparing cryptographic hashes) - -Example code for adding a constraint to the configuration object: - -```php -use Lcobucci\JWT\Validation\Constraint\PermittedFor; - -$config->setValidationConstraints(new PermittedFor('your-aud-claim')); -``` +* `Lcobucci\JWT\Validation\Constraint\HasClaimWithValue`: verifies that a **custom claim** has the expected value (not recommended when comparing cryptographic hashes) You may also create your [own validation constraints](extending-the-library.md#validation-constraints). diff --git a/mkdocs.yml b/mkdocs.yml index f4dd9f6e..8dbba334 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -6,11 +6,12 @@ nav: - 'index.md' - 'installation.md' - 'quick-start.md' + - 'supported-algorithms.md' - Usage: - - 'configuration.md' - 'issuing-tokens.md' - 'parsing-tokens.md' - 'validating-tokens.md' + - 'configuration.md' - Guides: - 'extending-the-library.md' - 'upgrading.md'