JWT Authentication

Authentication Source

Before configuring a JWT middleware, a JWT Authentication Source must be defined in the static configuration.

Below is an example of a minimal JWT Authentication Source that can be added to a static configuration:

[authSources]
  [authSources.jwtSource]
    [authSources.jwtSource.jwt]
      signingSecret = "super-secret"
authSources:
  jwtSource:
    jwt:
      signingSecret: super-secret

Note

To use base64-encoded signingSecrets, set the signingSecretBase64Encoded option to true.

Authentication Source Options

signingSecret

Optional (one of signingSecret, publicKey, jwksFile or jwksUrl must be set), Default=""

The signingSecret option can be set to the secret used for signing the JWT certificates. It is then used by the middleware to verify incoming requests.

[authSources]
  [authSources.jwtSource]
    [authSources.jwtSource.jwt]
      signingSecret = "super-secret"
authSources:
  jwtSource:
    jwt:
      signingSecret: super-secret

signingSecretBase64Encoded

Optional, Default=false

The signingSecretBase64Encoded option can be set to indicate that the signingSecret is base64-encoded. If set to true, the signingSecret is base64-decoded before being used.

[authSources]
  [authSources.jwtSource]
    [authSources.jwtSource.jwt]
      signingSecret = "c3VwZXItc2VjcmV0Cg=="
      signingSecretBase64Encoded = true
authSources:
  jwtSource:
    jwt:
      signingSecret: c3VwZXItc2VjcmV0Cg==
      signingSecretBase64Encoded: true

publicKey

Optional (one of signingSecret, publicKey, jwksFile or jwksUrl must be set), Default=""

The publicKey option can be used as an alternative to a signing secret to verify incoming requests. In that case, users should sign their token using a private key, and the public key can be used to verify the signature.

[authSources]
  [authSources.jwtSource]
    [authSources.jwtSource.jwt]
      publicKey = """
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnzyis1ZjfNB0bBgKFMSv
vkTtwlvBsaJq7S5wA+kzeVOVpVWwkWdVha4s38XM/pa/yr47av7+z3VTmvDRyAHc
aT92whREFpLv9cj5lTeJSibyr/Mrm/YtjCZVWgaOYIhwrXwKLqPr/11inWsAkfIy
tvHWTxZYEcXLgAXFuUuaS3uF9gEiNQwzGTU1v0FqkqTBr4B8nW3HCN47XUu0t8Y0
e+lf4s4OxQawWD79J9/5d3Ry0vbV3Am1FtGJiJvOwRsIfVChDpYStTcHTCMqtvWb
V6L11BWkpzGXSW4Hv43qa+GSYOD2QU68Mb59oSk2OB+BtOLpJofmbGEGgvmwyCI9
MwIDAQAB
-----END PUBLIC KEY-----
"""
authSources:
  jwtSource:
    jwt:
      publicKey: |-
        -----BEGIN PUBLIC KEY-----
        MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnzyis1ZjfNB0bBgKFMSv
        vkTtwlvBsaJq7S5wA+kzeVOVpVWwkWdVha4s38XM/pa/yr47av7+z3VTmvDRyAHc
        aT92whREFpLv9cj5lTeJSibyr/Mrm/YtjCZVWgaOYIhwrXwKLqPr/11inWsAkfIy
        tvHWTxZYEcXLgAXFuUuaS3uF9gEiNQwzGTU1v0FqkqTBr4B8nW3HCN47XUu0t8Y0
        e+lf4s4OxQawWD79J9/5d3Ry0vbV3Am1FtGJiJvOwRsIfVChDpYStTcHTCMqtvWb
        V6L11BWkpzGXSW4Hv43qa+GSYOD2QU68Mb59oSk2OB+BtOLpJofmbGEGgvmwyCI9
        MwIDAQAB
        -----END PUBLIC KEY-----

jwksFile

Optional (one of signingSecret, publicKey, jwksFile or jwksUrl must be set), Default=""

The jwksFile option can be used to define a set of JWK to be used to verify the signature of JWTs. More information on JWK can be found in the reference.

The option can either be a path to a file mounted on the proxies or directly the content of a JWK set file.

[authSources]
  [authSources.jwtSource]
    [authSources.jwtSource.jwt]
      jwksFile = /etc/config/jwks.json
[authSources]
  [authSources.jwtSource]
    [authSources.jwtSource.jwt]
      jwksFile = """
{
  "keys": [
  {
    "use": "sig",
    "kty": "EC",
    "kid": "key-id",
    "crv": "P-256",
    "alg": "ES256",
    "x": "EVs_o5-uQbTjL3chynL4wXgUg2R9q9UU8I5mEovUf84",
    "y": "kGe5DgSIycKp8w9aJmoHhB1sB3QTugfnRWm5nU_TzsY"
  }
  ]
}
"""
authSources:
  jwtSource:
    jwt:
      jwksFile: /etc/config/jwks.json
authSources:
  jwtSource:
    jwt:
      jwksFile: |-
        {
          "keys": [
          {
            "use": "sig",
            "kty": "EC",
            "kid": "key-id",
            "crv": "P-256",
            "alg": "ES256",
            "x": "EVs_o5-uQbTjL3chynL4wXgUg2R9q9UU8I5mEovUf84",
            "y": "kGe5DgSIycKp8w9aJmoHhB1sB3QTugfnRWm5nU_TzsY"
          }
          ]
        }

JWT Header Key ID

If the JWT header contains a kid header, the middleware expects to find a JWK. If a JWK cannot be found, it returns a 401 Unauthorized error.

jwksUrl

Optional (one of signingSecret, publicKey, jwksFile or jwksUrl must be set), Default=""

The jwksUrl option can be used as an alternative to a signing secret to verify incoming requests. It is the URL of the host serving a JWK set. More information on JWK can be found in the reference. The keys are cached if the HTTP Cache Control allows for caching.

This option can either be set to a full URL (e.g.: https://www.googleapis.com/oauth2/v3/certs) or to a path (e.g.: /oauth2/v3/certs). In the first case, the middleware simply fetches the keys located at the URL to verify the token. In the second case, the middleware builds the full URL using the iss property found in the JWT claims. It does so by concatenating the host defined by iss and the path set by jwksURL.

[authSources]
  [authSources.jwtSource]
    [authSources.jwtSource.jwt]
      jwksUrl = "https://www.googleapis.com/oauth2/v3/certs"
authSources:
  jwtSource:
    jwt:
      jwksUrl: https://www.googleapis.com/oauth2/v3/certs

JWT Header Key ID

If the JWT header contains a kid header, the middleware expects to find a JWK. If a JWK cannot be found, it returns a 401 Unauthorized error.

JWT Issuer Claim

If jwksUrl is set to a path and the iss property is missing in the JWT it's trying to verify, the middleware returns a 401 Unauthorized error.

JWT Middleware

After declaring a JWT Authentication Source in the static configuration of the cluster, JWT middlewares can be added to routers in the dynamic configuration.

The JWT middleware verifies that a token is provided in the Authorization header (Authorization: Bearer <JWT>) of incoming requests or in the query parameters of the request (jwt=<JWT>).

JWT Query Parameter

The JWT query parameter is configurable with the tokenQueryKey option.

With no specific configuration, a JWT middleware only validates the signature of a JWT and checks the nbf, exp and iat standard claims (if they are present). Custom claim validation can be configured with Custom Claims Validation.

Middleware Options

source

Required, Default=""

The source option should contain the name of the Authentication Source used by the middleware.

labels:
  - "traefik.http.middlewares.test-jwtAuth.plugin.jwtAuth.source=jwtSource"
apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
  name: test-jwtAuth
spec:
  plugin:
    jwtAuth:
      source: jwtSource
- "traefik.http.middlewares.test-jwtAuth.plugin.jwtAuth.source=jwtSource"
"labels": {
    "traefik.http.middlewares.test-jwtAuth.plugin.jwtAuth.source": "jwtSource"
}
labels:
  - "traefik.http.middlewares.test-jwtAuth.plugin.jwtAuth.source=jwtSource"
[http.middlewares]
  [http.middlewares.test-jwtAuth.plugin.jwtAuth]
    source = "jwtSource"
http:
  middlewares:
    test-jwt:
      plugin:
        jwtAuth:
          source: jwtSource

forwardAuthorization

Optional, Default=false

The forwardAuthorization option determines if the authorization headers have to be forwarded or stripped from a request after it has been approved by the middleware.

labels:
  - "traefik.http.middlewares.test-jwtAuth.plugin.jwtAuth.forwardAuthorization=true"
apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
  name: test-jwtAuth
spec:
  plugin:
    jwtAuth:
      forwardAuthorization: true
- "traefik.http.middlewares.test-jwtAuth.plugin.jwtAuth.forwardAuthorization=true"
"labels": {
    "traefik.http.middlewares.test-jwtAuth.plugin.jwtAuth.forwardAuthorization": "true"
}
labels:
  - "traefik.http.middlewares.test-jwtAuth.plugin.jwtAuth.forwardAuthorization=true"
[http.middlewares]
  [http.middlewares.test-jwtAuth.plugin.jwtAuth]
    forwardAuthorization = true
http:
  middlewares:
    test-jwt:
      plugin:
        jwtAuth:
          forwardAuthorization: true

forwardHeaders

Optional, Default=None

The forwardHeaders option sets the HTTP headers to add to requests and populates them with values extracted from a JWT.

labels:
  - "traefik.http.middlewares.test-jwtAuth.plugin.jwtAuth.forwardHeaders.Group=grp"
  - "traefik.http.middlewares.test-jwtAuth.plugin.jwtAuth.forwardHeaders.Expires-At=exp"
apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
  name: test-jwtAuth
spec:
  plugin:
    jwtAuth:
      forwardHeaders:
        Group: grp
        Expires-At: exp
- "traefik.http.middlewares.test-jwtAuth.plugin.jwtAuth.forwardHeaders.Group=grp"
- "traefik.http.middlewares.test-jwtAuth.plugin.jwtAuth.forwardHeaders.Expires-At=exp"
"labels": {
    "traefik.http.middlewares.test-jwtAuth.plugin.jwtAuth.forwardHeaders.Group": "grp",
    "traefik.http.middlewares.test-jwtAuth.plugin.jwtAuth.forwardHeaders.Expires-At": "exp"
}
labels:
  - "traefik.http.middlewares.test-jwtAuth.plugin.jwtAuth.forwardHeaders.Group=grp"
  - "traefik.http.middlewares.test-jwtAuth.plugin.jwtAuth.forwardHeaders.Expires-At=exp"
[http.middlewares]
  [http.middlewares.test-jwtAuth.plugin.jwtAuth]
    [http.middlewares.test-jwtAuth.plugin.jwtAuth.forwardHeaders]
      Group = "grp"
      Expires-At = "exp"
http:
  middlewares:
    test-jwt:
      plugin:
        jwtAuth:
          forwardHeaders:
            Group: grp
            Expires-At: exp

tokenQueryKey

Optional, Default=""

The tokenQueryKey sets the middleware to look for the token to use in a specific query parameter if not found in the Authorization header. The middleware always look in the Authorization header first.

labels:
  - "traefik.http.middlewares.test-jwtAuth.plugin.jwtAuth.tokenQueryKey=tok"
apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
  name: test-jwtAuth
spec:
  plugin:
    jwtAuth:
      tokenQueryKey: tok
- "traefik.http.middlewares.test-jwtAuth.plugin.jwtAuth.tokenQueryKey=tok"
"labels": {
  "traefik.http.middlewares.test-jwtAuth.plugin.jwtAuth.tokenQueryKey": "tok"
}
labels:
  - "traefik.http.middlewares.test-jwtAuth.plugin.jwtAuth.tokenQueryKey=tok"
[http.middlewares]
  [http.middlewares.test-jwtAuth.plugin.jwtAuth]
    tokenQueryKey = "tok"
http:
  middlewares:
    test-jwtAuth:
      plugin:
        jwtAuth:
          tokenQueryKey: tok

claims

Optional, Default=""

The claims option sets claims to validate in order to authorize the request.

labels:
  - "traefik.http.middlewares.test-jwt-auth.plugin.jwtAuth.claims=Equals(`grp`, `admin`)"
apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
  name: test-jwt-auth
spec:
  plugin:
    jwtAuth:
      claims: Equals(`grp`, `admin`)
- "traefik.http.middlewares.test-jwt-auth.plugin.jwtAuth.claims=Equals(`grp`, `admin`)"
"labels": {
  "traefik.http.middlewares.test-jwt-auth.plugin.jwtAuth.claims": "Equals(`grp`, `admin`)"
}
labels:
  - "traefik.http.middlewares.test-jwt-auth.plugin.jwtAuth.claims=Equals(`grp`, `admin`)"
[http.middlewares]
  [http.middlewares.test-jwt-auth.plugin.jwtAuth]
    claims = "Equals(`grp`, `admin`)"
http:
  middlewares:
    test-jwt-auth:
      plugin:
        jwtAuth:
          claims: Equals(`grp`, `admin`)
Syntax

The following functions are supported in claims:

Function Description Example
Equals Validated the equality of the value in key with value. Equals(`grp`, `admin`)
Prefix Validates the value in key has the prefix of value. Prefix(`referrer`, `http://example.com`)
Contains (string) Validates the value in key contains value. Contains(`referrer`, `/foo/`)
Contains (array) Validates the key array contains the value. Contains(`areas`, `home`)
SplitContains Validates the value in key contains the value once split by the separator. SplitContains(`scope`, ` `, `writer`)
OneOf Validates the key array contains one of the values. OneOf(`areas`, `office`, `lab`)

All functions can be joined by boolean operands. The supported operands are:

Operand Description Example
&& Compares two functions and returns true only if both evaluate to true. Equals(`grp`, `admin`) && Equals(`active`, `true`)
|| Compares two functions and returns true if either evaluate to true. Equals(`grp`, `admin`) || Equals(`active`, `true`)
! Returns false if the function is true, otherwise returns true. !Equals(`grp`, `testers`)

All examples returns true for the following data structure:

{
  "active": true,
  "grp": "admin",
  "scope": "reader writer deploy",
  "referrer": "http://example.com/foo/bar",
  "areas": [
    "office",
    "home"
  ]
}
Nested claims

Nested claims are supported by using a . between keys. For example:

user.name
{
  "active": true,
  "grp": "admin",
  "scope": "reader writer deploy",
  "referrer": "http://example.com/foo/bar",
  "areas": [
    "office",
    "home"
  ],
  "user" {
    "name": "John Snow",
    "status": "undead"
  }
}
John Snow

Handling keys that contain a '.'

If the key contains a dot, the dot can be escaped using \..

Handling a key that contains a '\'

If the key contains a \, it needs to be doubled \\.

Advanced Configuration Example

Below is an advanced configuration example using custom claims validation and forward headers:

labels:
  - "traefik.http.middlewares.test-jwtAuth.plugin.jwtAuth.source=jwtSource"
  - "traefik.http.middlewares.test-jwtAuth.plugin.jwtAuth.forwardHeaders.Group=grp"
  - "traefik.http.middlewares.test-jwtAuth.plugin.jwtAuth.forwardHeaders.Expires-At=exp"
  - "traefik.http.middlewares.test-jwtAuth.plugin.jwtAuth.claims: Equals(`grp`, `admin`)"
apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
  name: test-jwtAuth
spec:
  plugin:
    jwtAuth:
      source: jwtSource
      forwardHeaders:
        Group: grp
        Expires-At: exp
      claims: Equals(`grp`, `admin`)
- "traefik.http.middlewares.test-jwtAuth.plugin.jwtAuth.source=jwtSource"
- "traefik.http.middlewares.test-jwtAuth.plugin.jwtAuth.forwardHeaders.Group=grp"
- "traefik.http.middlewares.test-jwtAuth.plugin.jwtAuth.forwardHeaders.Expires-At=exp"
- "traefik.http.middlewares.test-jwtAuth.plugin.jwtAuth.claims: Equals(`grp`, `admin`)"
"labels": {
    "traefik.http.middlewares.test-jwtAuth.plugin.jwtAuth.source": "jwtSource",
    "traefik.http.middlewares.test-jwtAuth.plugin.jwtAuth.forwardHeaders.Group": "grp",
    "traefik.http.middlewares.test-jwtAuth.plugin.jwtAuth.forwardHeaders.Expires-At": "exp",
    "traefik.http.middlewares.test-jwtAuth.plugin.jwtAuth.claims": "Equals(`grp`, `admin`)"
}
labels:
  - "traefik.http.middlewares.test-jwtAuth.plugin.jwtAuth.source=jwtSource"
  - "traefik.http.middlewares.test-jwtAuth.plugin.jwtAuth.forwardHeaders.Group=grp"
  - "traefik.http.middlewares.test-jwtAuth.plugin.jwtAuth.forwardHeaders.Expires-At=exp"
  - "traefik.http.middlewares.test-jwtAuth.plugin.jwtAuth.claims: Equals(`grp`, `admin`)"
[http.middlewares]
  [http.middlewares.test-jwtAuth.plugin.jwtAuth]
    source = "jwtSource"
    claims = "Equals(`grp`, `admin`)"
    [http.middlewares.test-jwtAuth.plugin.jwtAuth.forwardHeaders]
      Group = "grp"
      Expires-At = "exp"
http:
  middlewares:
    test-jwt:
      plugin:
        jwtAuth:
          source: jwtSource
          forwardHeaders:
            Group: grp
            Expires-At: exp
          claims: Equals(`grp`, `admin`)