Search
Varnish Enterprise

JWT

Description

The jwt vmod allows for the manipulation, creation, and verification of JWT and JWS tokens.

Examples

Verifying

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
}

Creating

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.
  }
}

Modifying

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.
  }
}

API

Encoded Keys

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:

  • ascii: the key is a simple ascii string and no decoding is required. Default.
  • base64url: the key is a base64url encoded string.

reader

OBJECT reader()

Creates a JWT Reader Object. must be called in sub vcl_init.

Arguments: None

Type: Object

Returns: Object.

.parse

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 STRING

Type: Method

Returns: Bool

.from_string

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 STRING

Type: Method

Returns: Bool

.get_alg

STRING .get_alg()

Grabs the signature algorithm for the loaded JWT token, E.g.: HS256

Arguments: None

Type: Method

Returns: String

.get_typ

STRING .get_typ()

Gets the typ parameter from the JWT token header, E.g.: JWT

Arguments: None

Type: Method

Returns: String

.get_b64

BOOL .get_b64()

Gets the b64 parameter from the JWT token header, E.g.: TRUE

Arguments: None

Type: Method

Returns: Bool

.get_kid

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

.is_jwt

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

.get_sub

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

.get_iss

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

.get_jti

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

.get_exp

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

.has_exp

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

.get_nbf

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

.has_nbf

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

.get_iat

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

.has_iat

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

.get_header

STRING .get_header()

Get the header of the JWT token as a JSON string.

Arguments: None

Type: Method

Returns: String

.get_header_encoded

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

.get_payload

STRING .get_payload()

Get the payload of the JWT token as a JSON string.

Arguments: None

Type: Method

Returns: String

.get_payload_encoded

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

.get_signature

STRING .get_signature()

Get the signature of the JWT token.

Arguments: None

Type: Method

Returns: String

.to_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

.set_key

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

.set_jwk

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 STRING

Type: Method

Returns: Int

.verify

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:

  • none: ignore the kid fields and do JWK selection using algorithm matching. Default.
  • optional: if there is a request kid and a JWK has a kid field, compare them. Otherwise, do JWK selection using algorithm matching.
  • required: there must be a 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

.verify_raw

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

writer

OBJECT writer()

Creates a JWT Writer Object. must be called in sub vcl_init.

Arguments: None

Type: Object

Returns: Object.

.parse

VOID .parse(STRING token)

Set the header and payload the JWT/JWS token provided.

Arguments:

  • token accepts type STRING

Type: Method

Returns: None

.from_string

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 STRING

Type: Method

Returns: None

.set_header

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 STRING

Type: Method

Returns: None

.set_payload

VOID .set_payload(STRING payload)

Set the JSON payload on the JWT/JWS token.

Arguments:

  • payload accepts type STRING

Type: Method

Returns: None

.set_header_encoded

VOID .set_header_encoded(STRING header)

Set the Base64 encoded JSON payload on the JWT/JWS token.

Arguments:

  • header accepts type STRING

Type: Method

Returns: None

.set_payload_encoded

VOID .set_payload_encoded(STRING payload)

Set the Base64 encoded JSON payload on the JWT/JWS token.

Arguments:

  • payload accepts type STRING

Type: Method

Returns: None

.set_alg

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 STRING

Type: Method

Returns: None

.set_typ

VOID .set_typ(STRING typ)

Set the typ parameter of the token header.

Arguments:

  • typ accepts type STRING

Type: Method

Returns: None

.set_b64

VOID .set_b64(BOOL is_b64)

Set the b64 parameter of the token header.

Arguments:

  • is_b64 accepts type BOOL

Type: Method

Returns: None

.set_kid

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 STRING

Type: Method

Returns: None

.set_sub

VOID .set_sub(STRING sub)

Set the sub parameter of the token payload.

Arguments:

  • sub accepts type STRING

Type: Method

Returns: None

.set_iss

VOID .set_iss(STRING iss)

Set the iss parameter of the token payload.

Arguments:

  • iss accepts type STRING

Type: Method

Returns: None

.set_jti

VOID .set_jti(STRING jti)

Set the jti parameter of the token payload.

Arguments:

  • jti accepts type STRING

Type: Method

Returns: None

.set_exp

VOID .set_exp(TIME time)

Set the exp parameter of the token payload.

Arguments:

  • time accepts type TIME

Type: Method

Returns: None

.set_nbf

VOID .set_nbf(TIME time)

Set the nbf parameter of the token payload.

Arguments:

  • time accepts type TIME

Type: Method

Returns: None

.set_iat

VOID .set_iat(TIME time)

Set the iat parameter of the token payload.

Arguments:

  • time accepts type TIME

Type: Method

Returns: None

.set_duration

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 DURATION

Type: Method

Returns: None

.delete_sub

VOID .delete_sub()

Sets the subject for the JWT token.

Arguments: None

Type: Method

Returns: None

.delete_iss

VOID .delete_iss()

Sets the issuer for the JWT token.

Arguments: None

Type: Method

Returns: None

.delete_jti

VOID .delete_jti()

Sets the JWT ID for the token.

Arguments: None

Type: Method

Returns: None

.delete_exp

VOID .delete_exp()

Delete the exp parameter of the token payload.

Arguments: None

Type: Method

Returns: None

.delete_nbf

VOID .delete_nbf()

Delete the nbf parameter of the token payload.

Arguments: None

Type: Method

Returns: None

.delete_iat

VOID .delete_iat()

Delete the iat parameter of the token payload.

Arguments: None

Type: Method

Returns: None

.generate

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

.to_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

.generate_raw

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

.reset

VOID .reset()

Reset all data on the writer including the error flag.

Arguments: None

Type: Method

Returns: None

.error

BOOL .error()

Did any of the method calls above result in an error

Arguments: None

Type: Method

Returns: Bool

Availability

The jwt VMOD is available in Varnish Enterprise version 6.0.6r2 and later.