JSON Web Token (JWT) and JSON Web Signature (JWS) validation using WEB Cryptography API
npm install ez-jwtjwtValidate(jwt) returns JWT claims
jwsValidate(jws) returns JWS (JOSE) header and JWS payload
bash
npm i ez-jwt
`
Usage
The main function to validate a JWT and obtain the JWT claims is jwtValidate(). Let's use RFC 7515 example JWT.
`typescript
// JWS header is { typ: 'JWT', alg: 'HS256' }
// JWT claims is { iss: 'joe', exp: 1300819380, 'http://example.com/is_root': true }
const token =
'eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJqb2UiLA0KICJle' +
'HAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnV' +
'lfQ.dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk';
`
This token has no information about the key to validate it, so we need to provide the key explicitly. The public key to validate the token is present in the same example.
`typescript
const jwk = {
kty: 'oct',
k: 'AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow'
};
`
Use jwtValidate() and provide the key to validate the token, get JWT claims.
`typescript
import { jwtValidate } from 'ez-jwt';
jwtValidate(token, {keys: jwk})
.then((claims) => {
// JWT is valid
// claims value is { iss: 'joe', exp: 1300819380, 'http://example.com/is_root': true }
});
`
We don't need to provide any keys if a token JWS header has information about the key to validate it. See Key providers.
JWT/JWS validation
Input JWT/JWS should be in JWS Compact Serialization format: a string of three BASE64URL-encoded parts (header, payload, and signature), separated by . symbols.
Any digitally signed JWT is a JWS with a well-defined payload, which is JWT Claims.
| Compact Serialization Format | BASE64URL(JSON( x )) | . | BASE64URL( x ) | . | BASE64URL( x ) |
|:---|:---:|:---:|:---:|:---:|:---:|
| JWS | JWS Header | | ArrayBuffer | | JWS Signature |
| JWT | JWS Header | | JSON(JWT Claims) | | JWS Signature |
JWT validation process performed by jwtValidate():
* Validate JWT as JWS using jwsValidate().
- Split the input JWS into three parts: header, payload, signature.
- BASE64URL_DECODE() all the parts.
- UTF-8 decode and JSON parse the header, get JWS header.
- Check JWS header parameter crit (should be undefined).
- Check JWS header parameter alg (should be one of the standard algorithms).
- Apply the key provider function to the header and get an array of keys.
- Filter key array by key ID, algorithm, usage, and operations.
- Use the remaining key(s) to verify JWS signature.
- Return JWS header and payload.
* Check JWS header parameters typ (should be undefined or equals 'JWT') and cty (should be undefined).
* UTF-8 decode and JSON parse JWS payload, get JWT claims object.
* Validate JWT claims exp and nbf (if present).
* Return JWT claims.
Keys providers
To validate a JWS Signature the correct public key is required. The key should be provided to jwsValidate() function. A token issuer can provide the public key to validate the token in several ways and different formats.
$3
This is the main format of key data supported by the package.
* A JWK is provided in JWS header (parameter jwk).
* One or more JWK is known to the application.
* URI of JWKS endpoint is provided in JWS header (parameter jku).
* URI of JWKS endpoint is known to the application (for example .well-known/jwks.json for OAuth2 applications).
The default key provider jwsHeaderKeysProvider() gets jwk parameter key and adds all the keys from JWKS endpoint defined by jku parameter. One can add some more application keys by passing them as an argument for jwsHeaderKeysProvider(appKeys). appKeys value can be JWK, JWK[], or JWKS object.
Two application keys providers jwkAppKeysProvider() and jwkAppUriProvider() ignore JWS header parameters and provide application keys only.
One can design a custom key provider function and pass it to jwsValidate().
Key provider is a function that takes JWS header and returns keys to validate the JWS.
`typescript
type JwsKeyProvider = (h: JWSHeader) => Promise;
`
Key provider functions summary:
| Key provider | JWS header jkw | JWS header jku | Application JWK/JWK[]/JWKS | Application JWKS endpoint |
|---|:---:|:---:|:---:|:---:|
|jwsHeaderKeysProvider()| + | + | | |
|jwsHeaderKeysProvider(appKeys)| + | + | appKeys | |
|jwkAppKeysProvider(appKeys)| | | appKeys | |
|jwkAppUriProvider(jwks_uri)| | | | jwks_uri |
Note: one can download all the keys from JWKS endpoint to appKeys variable in advance and use jwkAppKeysProvider(appKeys) (or jwsHeaderKeysProvider(appKeys)) provider instead of jwkAppUriProvider().
$3
A token issuer JWKS endpoint returns all the public keys the issuer uses to sign tokens. One can filter the keys by kid or try to use all the keys. For example, Google's OpenID Connect services JWKS endpoint returns the following two keys:
`json
{
"keys": [
{
"kid": "0a7dc12664590c957ffaebf7b6718297b864ba91",
"use": "sig",
"kty": "RSA",
"alg": "RS256",
"e": "AQAB",
"n": "7NfiTQcshWgrEdKbHC2e..............eNVz39274ippJSQ"
},
{
"kid": "bc49530e1ff9083dd5eeaa06be2ce437f49c905e",
"use": "sig",
"kty": "RSA",
"alg": "RS256",
"e": "AQAB",
"n": "xPXUFDnAQQ5daLQTcQsV..............Na3BbnAhj7miR0w"
}
]
}
`
$3
Public key data can be provided X.509 certificate as a SubjectPublicKeyInfo object. JWS header parameters x5u, x5c, x5t, x5t#S256` provide key data in form of an X.509 certificate.