The jwt
vmod allows for the manipulation, creation, and verification of JWT and JWS tokens.
The following example verifies a token signed with the HS256
algorithm
that is in the Authorization
header.
vcl 4.1;
import jwt;
backend default none;
sub vcl_init {
new jwt_reader = jwt.reader();
}
sub vcl_recv {
if (!jwt_reader.parse(req.http.Authorization)) {
return (synth(401, "Invalid Authorization Token"));
}
if (!jwt_reader.set_key("secret")) {
return (synth(401, "Invalid Authorization Token"));
}
if (!jwt_reader.verify("HS256")) {
return (synth(401, "Invalid Authorization Token"));
}
# JWT token was valid
}
vcl 4.1;
import jwt;
backend default none;
sub vcl_init {
new jwt_writer = jwt.writer();
}
sub vcl_recv {
jwt_writer.set_sub(req.method + " " + req.http.host + req.url);
jwt_writer.set_alg("HS256");
# Shortlived token that allows 30 seconds clock skew.
jwt_writer.set_nbf(now - 30s);
jwt_writer.set_exp(now + 30s);
jwt_writer.set_iss(server.identity);
set req.http.jwt = jwt_writer.generate("secret");
if (jwt_writer.error()) {
# Something went wrong generating the new token.
}
}
The following example takes an existing token, verifies it, and
extends its expiration to 6 hours from the current time then sets the new token
on the request Authorization
header.
vcl 4.1;
import jwt;
backend default none;
sub vcl_init {
new jwt_reader = jwt.reader();
new jwt_writer = jwt.writer();
}
sub vcl_recv {
if (!jwt_reader.parse(req.http.Authorization)) {
return (synth(401, "Invalid Authorization Token"));
}
if (!jwt_reader.set_key("secret")) {
return (synth(401, "Invalid Authorization Token"));
}
if (!jwt_reader.verify("HS256")) {
return (synth(401, "Invalid Authorization Token"));
}
# JWT token was valid, extend the lifetime.
jwt_writer.from_string(jwt_reader.to_string());
jwt_writer.set_exp(now + 6h);
if (jwt_writer.error()) {
# Something went wrong extending the expiration date.
}
set req.http.Authorization = jwt_writer.generate("secret");
if (jwt_writer.error()) {
# Something went wrong generating the new token.
}
}
The JWT methods that have a key
parameter can accept an HMAC, RSA, or
ECDSA key. Care must be taken when using HMAC keys (secrets) that are
binary data. Such binary secrets can contain embedded null (\0
) bytes
and MUST NOT be passed as simple ascii strings. Passing HMAC binary secrets
as simple strings can cause a truncated secret to be used to generate a
token or signature, making it insecure. Likewise, a binary secret can cause
a truncated secret to be used when verifying a token or signature. It is
strongly suggested that HMAC binary secrets be encoded before passing as a
key. These JWT methods have an optional encoding
parameter to indicate
how the key
is encoded. If the encoding
is anything other than
ascii
(default), the key
will be decoded to its binary form before
generating or verifying the token or signature. Note that the encoding
parameter is ignored for RSA and ECDSA keys.
Valid encoding
values:
key
is a simple ascii string and no decoding is required. Default.key
is a base64url encoded string.OBJECT reader()
Creates a JWT Reader Object. must be called in sub vcl_init
.
Arguments: None
Type: Object
Returns: Object.
BOOL .parse(STRING token)
Parse a JWT/JWS token into the reader and return whether it was able to be parsed successfully. You should always check the return of this method before calling any of the methods below.
Arguments:
token
accepts type STRINGType: Method
Returns: Bool
BOOL .from_string(STRING token)
Parse a JWT/JWS token into the reader and return whether it was able to be
parsed successfully. Primarily an alias to keep convention with the
reader
/writer
to
and from
methods. You should always
check the return of this method before calling any of the other reader methods.
Arguments:
token
accepts type STRINGType: Method
Returns: Bool
STRING .get_alg()
Grabs the signature algorithm for the loaded JWT token, E.g.: HS256
Arguments: None
Type: Method
Returns: String
STRING .get_typ()
Gets the typ
parameter from the JWT token header, E.g.: JWT
Arguments: None
Type: Method
Returns: String
BOOL .get_b64()
Gets the b64
parameter from the JWT token header, E.g.: TRUE
Arguments: None
Type: Method
Returns: Bool
STRING .get_kid()
Gets the kid
parameter from the JWT token header. The value of the
kid
is application specific.
Arguments: None
Type: Method
Returns: String
BOOL .is_jwt()
Tell whether the token is a JWT or a JWS. A JWT token will either have the
typ
header parameter equal to JWT
or have a payload that is base64url
encoded JSON, if both of these requirements aren’t met it is a JWS and this
method will return false
.
Arguments: None
Type: Method
Returns: Bool
STRING .get_sub(STRING default = 0)
Get the sub
parameter of the JWT payload and return default
if
it does not exist.
Arguments:
default
accepts type STRING with a default value of 0
optional
Type: Method
Returns: String
STRING .get_iss(STRING default = 0)
Get the iss
parameter of the JWT payload and return default
if
it does not exist.
Arguments:
default
accepts type STRING with a default value of 0
optional
Type: Method
Returns: String
STRING .get_jti(STRING default = 0)
Get the jti
parameter of the JWT payload and return default
if
it does not exist.
Arguments:
default
accepts type STRING with a default value of 0
optional
Type: Method
Returns: String
TIME .get_exp()
Get the exp
parameter of the JWT payload. You must call reader.has_exp()
before calling this function to make sure you have an exp
to get.
Arguments: None
Type: Method
Returns: Time
BOOL .has_exp()
Get whether the JWT token has an exp
parameter. This must be called before
reader.get_exp()
to make sure you have an exp
to return.
Arguments: None
Type: Method
Returns: Bool
TIME .get_nbf()
Get the nbf
parameter of the JWT payload. You must call reader.has_nbf()
before calling this function to make sure you have an nbf
to get.
Arguments: None
Type: Method
Returns: Time
BOOL .has_nbf()
Get whether the JWT token has an nbf
parameter. This must be called before
reader.get_nbf()
to make sure you have an nbf
to return.
Arguments: None
Type: Method
Returns: Bool
TIME .get_iat()
Get the iat
parameter of the JWT payload. You must call reader.has_iat()
before calling this function to make sure you have an iat
to get.
Arguments: None
Type: Method
Returns: Time
BOOL .has_iat()
Get whether the JWT token has an iat
parameter. This must be called before
reader.get_iat()
to make sure you have an iat
to return.
Arguments: None
Type: Method
Returns: Bool
STRING .get_header()
Get the header of the JWT token as a JSON string.
Arguments: None
Type: Method
Returns: String
STRING .get_header_encoded()
Get the header of the JWT token as a JSON string that is base64url encoded.
Arguments: None
Type: Method
Returns: String
STRING .get_payload()
Get the payload of the JWT token as a JSON string.
Arguments: None
Type: Method
Returns: String
STRING .get_payload_encoded()
Get the payload of the JWT token as a JSON string that is base64url encoded.
Arguments: None
Type: Method
Returns: String
STRING .get_signature()
Get the signature of the JWT token.
Arguments: None
Type: Method
Returns: String
STRING .to_string()
Gets a string representation of the token to pass to the writer’s
from_string
method
Arguments: None
Type: Method
Returns: String
BOOL .set_key(STRING key, ENUM {ascii, base64url} encoding = ascii)
Set the HMAC secret, RSA public key, or ECDSA public key to use to verify the token or signature. The RSA and ECDSA keys must be in PEM format without newlines.
The optional encoding
parameter indicates whether the key
is encoded
with one of the defined methods. See Encoded Keys.
Arguments:
key
accepts type STRING
encoding
is an ENUM that accepts values of ascii
, and base64url
with a default value of ascii
optional
Type: Method
Returns: Bool
INT .set_jwk(STRING jwk)
Set the RSA or ECDSA Public key(s) for the token via a JWK. The jwk
can
be a single key or an array of keys
.
Each key must have a kty
and/or alg
field. The supported kty
values are RSA
and EC
. The supported alg
values are RS256
,
RS384
, RS512
, ES256
, ES384
, and ES512
. The key must also
have those fields specific to an RSA or ECDSA key.
A valid RSA key has these fields:
kty
(if present) is RSA
.alg
(if present) is RS256
, RS384
, or RS512
.n
and e
are required.A valid ECDSA key has these fields:
kty
(if present) is EC
.alg
(if present) is ES256
, ES384
, or ES512
.crv
is P-256
, P-384
, or P-521
.x
and y
are required.This returns a count of valid keys.
Arguments:
jwk
accepts type STRINGType: Method
Returns: Int
BOOL .verify(STRING expected_alg, ENUM {none, optional, required} check_kid = none, BOOL check_exp = 1, BOOL check_nbf = 1)
Verify the JWT/JWS token. This method supports verifying the signature with
algorithms none
, HS256
, HS384
, HS512
, RS256
, RS384
,
RS512
, ES256
, ES384
, and ES512
. The exp
, iat
, and
nbf
payload parameters are also checked when this method is called if they
are present.
If expected_alg
does not match the algorithm of the passed in token
verification will fail. DO NOT USE get_alg()
to populate this argument as
this will allow an attacker to forge tokens by claiming the algorithm of the
token is none
, which does not require signature verification. The possible
values for this argument are none
, HS256
, HS384
, HS512
,
RS256
, RS384
, RS512
, ES256
, ES384
, and ES512
.
The optional check_kid
parameter can be used with JWKs to indicate whether
the kid
field should be used for JWK selection. The request kid
comes
from the JWS/JWT header field or by calling .set_kid()
, and is compared
against the JWK kid
field. The kid
are case-sensitive strings and
compared accordingly. A JWK with a matching kid
must also have the expected
algorithm. If more than one JWK has the same kid
, each is tried until a
successful verifification result. The check_kid
parameter is ignored when
verifying with a public key string (instead of JWKs).
Valid check_kid
values:
kid
fields and do JWK selection using algorithm matching. Default.request kid
and a JWK has a kid
field, compare them. Otherwise, do JWK selection using algorithm matching.request kid
and a JWK must have a matching kid
field. If no matching kid
is found in the JWKs, .verify()
fails.If check_exp
is 1
(default), the exp
claim, if present, is validated
against the current time. Passing 0
causes the exp
check to be skipped.
If check_nbf
is 1
(default), the nbf
claim, if present, is validated
against the current time. Passing 0
causes the nbf
check to be skipped.
This returns true
if the expected_alg
matches the token’s
algorithm, the signature algorithm is one of the supported ones listed above,
the signature is valid, and the exp
and nbf
claims are valid if they are
present. If any of the aforementioned conditions are not met, the return value
will be false
.
Arguments:
expected_alg
accepts type STRING
check_exp
accepts type BOOL with a default value of 1
optional
check_nbf
accepts type BOOL with a default value of 1
optional
check_kid
is an ENUM that accepts values of none
, optional
, and required
with a default value of none
optional
Type: Method
Returns: Bool
BOOL .verify_raw(STRING alg, STRING data, STRING signature)
Verify the signature
of the data
. This method supports verifying
with a public key or with JWKs. A public key can be provided with
.set_key()
. JWKs can be provided with .set_jwk()
. This method
is useful when the alg
, data
, or signature
do not come from
the traditional JWT/JWS header, payload, or token.
The possible values for alg
are none
, HS256
, HS384
, HS512
,
RS256
, RS384
, RS512
, ES256
, ES384
, and ES512
.
The data
can be any arbitrary string and it is used as is.
The signature
is a base64url encoded signature.
This returns true
if the alg
is one of the supported ones listed
above and the signature
is valid. If any of the aforementioned
conditions are not met, the return value will be false
.
Arguments:
alg
accepts type STRING
data
accepts type STRING
signature
accepts type STRING
Type: Method
Returns: Bool
OBJECT writer()
Creates a JWT Writer Object. must be called in sub vcl_init
.
Arguments: None
Type: Object
Returns: Object.
VOID .parse(STRING token)
Set the header and payload the JWT/JWS token provided.
Arguments:
token
accepts type STRINGType: Method
Returns: None
VOID .from_string(STRING token)
Sets the header and payload from token provided. Primarily for use with the “to_string” method of the reader.
Arguments:
token
accepts type STRINGType: Method
Returns: None
VOID .set_header(STRING header)
Set the header and payload the JWT/JWS token provided. Primarily an alias of
parse
to keep convention with the reader
/writer
to
and
from
methods.
Arguments:
header
accepts type STRINGType: Method
Returns: None
VOID .set_payload(STRING payload)
Set the JSON payload on the JWT/JWS token.
Arguments:
payload
accepts type STRINGType: Method
Returns: None
VOID .set_header_encoded(STRING header)
Set the Base64 encoded JSON payload on the JWT/JWS token.
Arguments:
header
accepts type STRINGType: Method
Returns: None
VOID .set_payload_encoded(STRING payload)
Set the Base64 encoded JSON payload on the JWT/JWS token.
Arguments:
payload
accepts type STRINGType: Method
Returns: None
VOID .set_alg(STRING alg)
Set the signature algorithm on the token. This method supports alg
being
none
, HS256
, HS384
, HS512
, RS256
, RS384
, RS512
,
ES256
, ES384
, or ES512
.
Arguments:
alg
accepts type STRINGType: Method
Returns: None
VOID .set_typ(STRING typ)
Set the typ
parameter of the token header.
Arguments:
typ
accepts type STRINGType: Method
Returns: None
VOID .set_b64(BOOL is_b64)
Set the b64
parameter of the token header.
Arguments:
is_b64
accepts type BOOLType: Method
Returns: None
VOID .set_kid(STRING kid)
Set the kid
parameter of the token header. The value of the
kid
is application specific.
Arguments:
kid
accepts type STRINGType: Method
Returns: None
VOID .set_sub(STRING sub)
Set the sub
parameter of the token payload.
Arguments:
sub
accepts type STRINGType: Method
Returns: None
VOID .set_iss(STRING iss)
Set the iss
parameter of the token payload.
Arguments:
iss
accepts type STRINGType: Method
Returns: None
VOID .set_jti(STRING jti)
Set the jti
parameter of the token payload.
Arguments:
jti
accepts type STRINGType: Method
Returns: None
VOID .set_exp(TIME time)
Set the exp
parameter of the token payload.
Arguments:
time
accepts type TIMEType: Method
Returns: None
VOID .set_nbf(TIME time)
Set the nbf
parameter of the token payload.
Arguments:
time
accepts type TIMEType: Method
Returns: None
VOID .set_iat(TIME time)
Set the iat
parameter of the token payload.
Arguments:
time
accepts type TIMEType: Method
Returns: None
VOID .set_duration(DURATION duration)
Set the exp
parameter of the token payload duration
in the future from
the current time.
Arguments:
duration
accepts type DURATIONType: Method
Returns: None
VOID .delete_sub()
Sets the subject for the JWT token.
Arguments: None
Type: Method
Returns: None
VOID .delete_iss()
Sets the issuer for the JWT token.
Arguments: None
Type: Method
Returns: None
VOID .delete_jti()
Sets the JWT ID for the token.
Arguments: None
Type: Method
Returns: None
VOID .delete_exp()
Delete the exp
parameter of the token payload.
Arguments: None
Type: Method
Returns: None
VOID .delete_nbf()
Delete the nbf
parameter of the token payload.
Arguments: None
Type: Method
Returns: None
VOID .delete_iat()
Delete the iat
parameter of the token payload.
Arguments: None
Type: Method
Returns: None
STRING .generate(STRING key, ENUM {ascii, base64url} encoding = ascii)
Generate the full JWK/JWS token. This method supports creating with the
signature algorithms none
, HS256
, HS384
, HS512
, RS256
,
RS384
, RS512
, ES256
, ES384
, and ES512
.
The key
can be an HMAC secret, RSA private key, or ECDSA private key. The
RSA and ECDSA keys must be in PEM format without newlines.
The optional encoding
parameter indicates whether the key
is encoded
with one of the defined methods. See Encoded Keys.
Arguments:
key
accepts type STRING
encoding
is an ENUM that accepts values of ascii
, and base64url
with a default value of ascii
optional
Type: Method
Returns: String
STRING .to_string(STRING key, ENUM {ascii, base64url} encoding = ascii)
Generate the full JWK/JWS token. This method supports creating with the
signature algorithms none
, HS256
, HS384
, HS512
, RS256
,
RS384
, RS512
, ES256
, ES384
, and ES512
. Primarily an alias
of generate
to keep convention with the reader
/writer
, to
and
from
methods.
The key
can be an HMAC secret, RSA private key, or ECDSA private key. The
RSA and ECDSA keys must be in PEM format without newlines.
The optional encoding
parameter indicates whether the key
is encoded
with one of the defined methods. See Encoded Keys.
Arguments:
key
accepts type STRING
encoding
is an ENUM that accepts values of ascii
, and base64url
with a default value of ascii
optional
Type: Method
Returns: String
STRING .generate_raw(STRING alg, STRING data, STRING key, ENUM {ascii, base64url} encoding = ascii)
Generate a signature for the data
with the alg
and private key
.
This method is useful when the alg
or data
do not come from the
traditional JWT/JWS header or payload.
The possible values for alg
are none
, HS256
, HS384
, HS512
,
RS256
, RS384
, RS512
, ES256
, ES384
, and ES512
.
The data
can be any arbitrary string and it is used as-is.
The key
can be an HMAC secret, RSA private key, or ECDSA private key. The
RSA and ECDSA keys must be in PEM format without newlines.
The optional encoding
parameter indicates whether the key
is encoded
with one of the defined methods. See Encoded Keys.
This returns the signature as a base64url encoded string.
Arguments:
alg
accepts type STRING
data
accepts type STRING
key
accepts type STRING
encoding
is an ENUM that accepts values of ascii
, and base64url
with a default value of ascii
optional
Type: Method
Returns: String
VOID .reset()
Reset all data on the writer including the error
flag.
Arguments: None
Type: Method
Returns: None
BOOL .error()
Did any of the method calls above result in an error
Arguments: None
Type: Method
Returns: Bool
The jwt
VMOD is available in Varnish Enterprise version 6.0.6r2
and later.