BoxLang 🚀 A New JVM Dynamic Language Learn More...
jwt-cfml is a CFML (Lucee and ColdFusion) library for encoding and decoding JSON Web Tokens.
It supports the following algorithms:
In the case of the RS
and ES
algorithms,
asymmetric keys are expected to be provided in unencrypted PEM or JWK
format (in the latter case first deserialize the JWK to a CFML
struct). When using PEM, private keys need to be encoded in PKCS#8 format.
If your private key is not currently in this format, conversion should be straightforward:
$ openssl pkcs8 -topk8 -nocrypt -in privatekey.pem -out privatekey.pk8
When decoding tokens, either a public key or certificate can be provided. (If a certificate is provided, the public key will be extracted from it.)
You can pre-parse your encoded keys and pass the returned Java
classes to the encode()
and decode()
methods, to avoid having them parsed on every method call. See Parsing
Asymmetric Keys below.
Installation is done via CommandBox:
$ box install jwt-cfml
jwt-cfml
will be installed into a jwtcfml
package directory by default.
Alternatively the git repository can be cloned into the desired directory.
Once the library has been installed, the core jwt
component can be instantiated directly:
jwt = new path.to.jwtcfml.models.jwt();
You can make use of the library via the injection DSL: jwt@jwtcfml
payload = {'key': 'value'};
secret = 'secret';
token = jwt.encode(payload, secret, 'HS256');
pemPrivateKey = '
-----BEGIN PRIVATE KEY-----
...
-----END PRIVATE KEY-----
';
token = jwt.encode(payload, pemPrivateKey, 'RS256');
jwk = {
"alg": "RS256",
"d": "...",
"dp": "...",
"dq": "...",
"e": "AQAB",
"kty": "RSA",
"n": "...",
"p": "...",
"q": "...",
"qi": "..."
};
token = jwt.encode(payload, jwk, 'RS256');
When a token is encoded, a header is automatically included
containing "typ"
set to
"JWT"
and "alg"
set to
the passed in algorithm. If you need to add additional headers a
fourth argument, headers
, is available for this:
token = jwt.encode(payload, pemPrivateKey, 'RS256', {'kid': 'abc123'});
If your token payload contains "iat"
,
"exp"
, or "nbf"
claims,
you can set these to CFML date objects, and they will automatically be
converted to UNIX timestamps in the generated token for you.
payload = {'iat': now()};
token = jwt.encode(payload, secret, 'HS256');
token = 'eyJ0e...';
secret = 'secret';
payload = jwt.decode(token, secret, 'HS256');
token = 'eyJ0e...';
pemPublicKey = '
-----BEGIN PUBLIC KEY-----
...
-----END PUBLIC KEY-----
';
payload = jwt.decode(token, pemPublicKey, 'RS256');
token = 'eyJ0e...';
pemCertificate = '
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
';
payload = jwt.decode(token, pemCertificate, 'RS256');
token = 'eyJ0e...';
jwk = {
"e": "AQAB",
"kty": "RSA",
"alg": "RS256",
"n": "...",
"use": "sig"
}
payload = jwt.decode(token, jwk, 'RS256');
Note: This library does not rely solely on the algorithm specified
in the token header. You must specify the allowed
algorithms (either as a string or an array) when calling
decode()
. The algorithm in the token header must match
one of the allowed algorithms.
If the decoded payload contains "iat"
,
"exp"
, or "nbf"
claims,
they will be automatically converted from UNIX timestamps to CFML date
objects for you.
If you need to get the token header before decoding (e.g. you need a
"kid"
from it), you can use the
jwt.getHeader()
method. This will return the token header
as a struct.
token = 'eyJ0e...';
header = jwt.getHeader(token);
If a token signature is invalid, the jwt.decode()
method
will throw an error. Further, if the payload contains a
"exp"
or "nbf"
claim
these will be verified as well.
If you also wish to verify an audience or issuer claim, you can pass valid claims into the decode method:
claims = {
"iss": "somissuer",
"aud": "someaudience" // this can also be an array
};
payload = jwt.decode(token, pemCertificate, 'RS256', claims);
This argument can also be used to ignore the
"exp"
and "nbf"
claims
or to validate them against a timestamp other than the current time:
claims = {
// `exp` will be validated against 1 min in the past instead of the current time
"exp": dateAdd('n', -1, now()),
// `nbf` will be ignored
"nbf": false
};
payload = jwt.decode(token, pemCertificate, 'RS256', claims);
If you need to get the payload without doing any verification at all
you can pass verify=false
into the decode method:
jwt.decode(token = token, verify = false);
Every time a PEM key or JWK is passed into encode()
and
decode()
it must be converted to binary data and then the
appropriate Java class created. You can avoid this (minor) overhead by
parsing your key upfront, and then passing the generated Java key
class directly into encode()
and decode()
:
pemCertificate = '
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
';
publicKey = jwt.parsePEMEncodedKey(pemCertificate);
payload = jwt.decode(token, publicKey, 'RS256');
jwk = {
"e": "AQAB",
"kty": "RSA",
"alg": "RS256",
"n": "AN...",
"use": "sig"
};
publicKey = jwt.parseJWK(jwk);
payload = jwt.decode(token, publicKey, 'RS256');
$
box install jwt-cfml