Skip to content

OAuth 2.0 Token Introspection

OAuth 2.0 Token Introspection allows Traefik Enterprise to retrieve metadata about an access token from an oAuth 2.0 server with the Token Introspection extension. The metadata can be used to restrict the access to applications. For more information please refer to the RFC.

Authentication Source

Before configuring an OAuth 2.0 Token Introspection middleware, an Authentication Source must be defined in the static configuration.

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

authSources:
  oAuthIntroSource:
    oAuthIntrospection:
      url: https://localhost/oauth2/introspect
      authorizationHeader: Basic dGVzdDp0ZXN0
[authSources]
  [authSources.oAuthIntroSource]
    [authSources.oAuthIntroSource.oAuthIntrospection]
      url = "https://localhost/oauth2/introspect"
      authorizationHeader = "Basic dGVzdDp0ZXN0"

Authentication Source Options

url

Required, Default=""

The url is the full introspection endpoint URL. It must include the scheme and path.

authSources:
  oAuthIntroSource:
    oAuthIntrospection:
      url: https://localhost/oauth2/introspect
[authSources]
  [authSources.oAuthIntroSource]
    [authSources.oAuthIntroSource.oAuthIntrospection]
        url = "https://localhost/oauth2/introspect"

tls

Optional

Defines the TLS configuration used for the secure connection to the introspection endpoint.

tls.caBundle

Optional, Default=""

An optional caBundle containing a PEM-encoded certificate bundle or a path to a file containing the certificate bundle used to establish a TLS connection with the introspection endpoint.

Using a File

Note that TraefikEE does not watch for file changes. If caBundle is set to a file path, its content will be read once when the middleware is initialized.

authSources:
  oAuthIntroSource:
    oAuthIntrospection:
      tls:
        caBundle: |-
          -----BEGIN CERTIFICATE-----
          MIIB9TCCAWACAQAwgbgxGTAXBgNVBAoMEFF1b1ZhZGlzIExpbWl0ZWQxHDAaBgNV
          BAsME0RvY3VtZW50IERlcGFydG1lbnQxOTA3BgNVBAMMMFdoeSBhcmUgeW91IGRl
          Y29kaW5nIG1lPyAgVGhpcyBpcyBvbmx5IGEgdGVzdCEhITERMA8GA1UEBwwISGFt
          aWx0b24xETAPBgNVBAgMCFBlbWJyb2tlMQswCQYDVQQGEwJCTTEPMA0GCSqGSIb3
          DQEJARYAMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCJ9WRanG/fUvcfKiGl
          EL4aRLjGt537mZ28UU9/3eiJeJznNSOuNLnF+hmabAu7H0LT4K7EdqfF+XUZW/2j
          RKRYcvOUDGF9A7OjW7UfKk1In3+6QDCi7X34RE161jqoaJjrm/T18TOKcgkkhRzE
          apQnIDm0Ea/HVzX/PiSOGuertwIDAQABMAsGCSqGSIb3DQEBBQOBgQBzMJdAV4QP
          Awel8LzGx5uMOshezF/KfP67wJ93UW+N7zXY6AwPgoLj4Kjw+WtU684JL8Dtr9FX
          ozakE+8p06BpxegR4BR3FMHf6p+0jQxUEAkAyb/mVgm66TyghDGC6/YkiKoZptXQ
          98TwDIK/39WEB/V607As+KoYazQG8drorw==
          -----END CERTIFICATE-----
authSources:
  oAuthIntroSource:
    oAuthIntrospection:
      tls:
        caBundle: /etc/tls/ca-bundle.pem
[authSources]
  [authSources.oAuthIntroSource]
    [authSources.oAuthIntroSource.oAuthIntrospection.tls]
      caBundle = """
-----BEGIN CERTIFICATE-----
MIIB9TCCAWACAQAwgbgxGTAXBgNVBAoMEFF1b1ZhZGlzIExpbWl0ZWQxHDAaBgNV
BAsME0RvY3VtZW50IERlcGFydG1lbnQxOTA3BgNVBAMMMFdoeSBhcmUgeW91IGRl
Y29kaW5nIG1lPyAgVGhpcyBpcyBvbmx5IGEgdGVzdCEhITERMA8GA1UEBwwISGFt
aWx0b24xETAPBgNVBAgMCFBlbWJyb2tlMQswCQYDVQQGEwJCTTEPMA0GCSqGSIb3
DQEJARYAMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCJ9WRanG/fUvcfKiGl
EL4aRLjGt537mZ28UU9/3eiJeJznNSOuNLnF+hmabAu7H0LT4K7EdqfF+XUZW/2j
RKRYcvOUDGF9A7OjW7UfKk1In3+6QDCi7X34RE161jqoaJjrm/T18TOKcgkkhRzE
apQnIDm0Ea/HVzX/PiSOGuertwIDAQABMAsGCSqGSIb3DQEBBQOBgQBzMJdAV4QP
Awel8LzGx5uMOshezF/KfP67wJ93UW+N7zXY6AwPgoLj4Kjw+WtU684JL8Dtr9FX
ozakE+8p06BpxegR4BR3FMHf6p+0jQxUEAkAyb/mVgm66TyghDGC6/YkiKoZptXQ
98TwDIK/39WEB/V607As+KoYazQG8drorw==
-----END CERTIFICATE-----
"""
[authSources]
  [authSources.oAuthIntroSource]
    [authSources.oAuthIntroSource.oAuthIntrospection.tls]
      caBundle = "/etc/tls/ca-bundle.pem"
tls.insecureSkipVerify

Optional, Default=false

Disables TLS certificate verification when communicating with the introspection endpoint. Useful for testing purposes but strongly discouraged for production.

authSources:
  oAuthIntroSource:
    oAuthIntrospection:
      tls:
        insecureSkipVerify: true
[authSources]
  [authSources.oAuthIntroSource]
    [authSources.oAuthIntroSource.oAuthIntrospection.tls]
      insecureSkipVerify = true

timeout

Optional, Default="5s"

This option controls the time before giving up requests to introspection endpoint.

authSources:
  oAuthIntroSource:
    oAuthIntrospection:
      timeout: 15s
[authSources]
  [authSources.oAuthIntroSource.oAuthIntrospection]
    timeout = "15s"

maxRetries

Optional, Default=3

The number of retries for requests to introspection endpoint that fail.

authSources:
  oAuthIntroSource:
    oAuthIntrospection:
      maxRetries: 5
[authSources]
  [authSources.oAuthIntroSource.oAuthIntrospection]
    maxRetries = 5

authorizationHeader (deprecated)

Deprecated, Default=""

The authorizationHeader is the authentication used to access the introspection server endpoint. The value is used as the Authorization header sent to the introspection server.

Deprecated

This option is deprecated, please use customHeaders instead.

authSources:
  oAuthIntroSource:
    oAuthIntrospection:
      authorizationHeader: Basic dGVzdDp0ZXN0
[authSources]
  [authSources.oAuthIntroSource]
    [authSources.oAuthIntroSource.oAuthIntrospection]
        authorizationHeader = "Basic dGVzdDp0ZXN0"

customHeaders

Optional, Default=""

The customHeaders are user defined headers to send in every introspection request. Values can be plain strings or a valid Go template.

Currently, a variable of type Request corresponding to the request being introspected is accessible in templates.

authSources:
  oAuthIntroSource:
    oAuthIntrospection:
      customHeaders:
        Authorization: Basic dGVzdDp0ZXN0
        Source: traefikee
        Request-Host: {{ .Request.Host }}
        Request-Header-Foo: {{ .Request.Header.Get "Foo" }}
        Request-Query-Bar: {{ .Request.URL.Query.Get "bar" }}
[authSources]
  [authSources.oAuthIntroSource]
    [authSources.oAuthIntroSource.oAuthIntrospection]
      [authSources.oAuthIntroSource.oAuthIntrospection.customHeaders]
        Authorization = "Basic dGVzdDp0ZXN0"
        Source = "traefikee"
        Request-Host = "{{ .Request.Host }}"
        Request-Header-Foo = "{{ .Request.Header.Get \"Host\" }}"
        Request-Query-Bar = "{{ .Request.URL.Query.Get \"bar\" }}"

OAuth 2.0 Token Introspection Middleware

After declaring an Authentication Source in the static configuration of the cluster, OAuth 2.0 Token Introspection middleware can be added to routers in the dynamic configuration:

labels:
  - "traefik.http.middlewares.test-oauth-intro.plugin.oAuthIntrospection.source=oAuthIntroSource"
apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
  name: test-oauth-intro
spec:
  plugin:
    oAuthIntrospection:
      source: oAuthIntroSource
- "traefik.http.middlewares.test-oauth-intro.plugin.oAuthIntrospection.source=oAuthIntroSource"
"labels": {
  "traefik.http.middlewares.test-oauth-intro.plugin.oAuthIntrospection.source": "oAuthIntroSource"
}
labels:
  - "traefik.http.middlewares.test-oauth-intro.plugin.oAuthIntrospection.source=oAuthIntroSource"
http:
  middlewares:
    test-oauth-intro:
      plugin:
        oAuthIntrospection:
          source: oAuthIntroSource
[http.middlewares]
  [http.middlewares.test-oauth-intro.plugin.oAuthIntrospection]
    source = "oAuthIntroSource"

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-oauth-intro.plugin.oAuthIntrospection.source=oAuthIntroSource"
apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
  name: test-oauth-intro
spec:
  plugin:
    oAuthIntrospection:
      source: oAuthIntroSource
- "traefik.http.middlewares.test-oauth-intro.plugin.oAuthIntrospection.source=oAuthIntroSource"
"labels": {
  "traefik.http.middlewares.test-oauth-intro.plugin.oAuthIntrospection.source": "oAuthIntroSource"
}
labels:
  - "traefik.http.middlewares.test-oauth-intro.plugin.oAuthIntrospection.source=oAuthIntroSource"
http:
  middlewares:
    test-oauth-intro:
      plugin:
        oAuthIntrospection:
          source: oAuthIntroSource
[http.middlewares]
  [http.middlewares.test-oauth-intro.plugin.oAuthIntrospection]
    source = "oAuthIntroSource"

tokenQueryKey

Optional, Default=""

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

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

tokenTypeHint

Optional, Default=""

The tokenTypeHint is a hint to the introspection server as to what type of token is being introspected. Please refer to the official documentation for more details.

labels:
  - "traefik.http.middlewares.test-oauth-intro.plugin.oAuthIntrospection.tokenTypeHint=access_token"
apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
  name: test-oauth-intro
spec:
  plugin:
    oAuthIntrospection:
      tokenTypeHint: access_token
- "traefik.http.middlewares.test-oauth-intro.plugin.oAuthIntrospection.tokenTypeHint=access_token"
"labels": {
  "traefik.http.middlewares.test-oauth-intro.plugin.oAuthIntrospection.tokenTypeHint": "access_token"
}
labels:
  - "traefik.http.middlewares.test-oauth-intro.plugin.oAuthIntrospection.tokenTypeHint=access_token"
http:
  middlewares:
    test-oauth-intro:
      plugin:
        oAuthIntrospection:
          tokenTypeHint: access_token
[http.middlewares]
  [http.middlewares.test-oauth-intro.plugin.oAuthIntrospection]
    tokenTypeHint = "access_token"

forwardAuthorization

Optional, Default=false

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

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

forwardHeaders

Optional, Default=None

The forwardHeaders option sets the HTTP headers to add to requests and populates them with values extracted from an introspection response.

Note

Claims to be forwarded that are not found in the JWT result in empty headers.

labels:
  - "traefik.http.middlewares.test-oauth-intro.plugin.oAuthIntrospection.forwardHeaders.Group=grp"
  - "traefik.http.middlewares.test-oauth-intro.plugin.oAuthIntrospection.forwardHeaders.Scope=scope"
apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
  name: test-oauth-intro
spec:
  plugin:
    oAuthIntrospection:
      forwardHeaders:
        Group: grp
        Scope: scope
- "traefik.http.middlewares.test-oauth-intro.plugin.oAuthIntrospection.forwardHeaders.Group=grp"
- "traefik.http.middlewares.test-oauth-intro.plugin.oAuthIntrospection.forwardHeaders.Scope=scope"
"labels": {
  "traefik.http.middlewares.test-oauth-intro.plugin.oAuthIntrospection.forwardHeaders.Group": "grp",
  "traefik.http.middlewares.test-oauth-intro.plugin.oAuthIntrospection.forwardHeaders.Scope": "scope"
}
labels:
  - "traefik.http.middlewares.test-oauth-intro.plugin.oAuthIntrospection.forwardHeaders.Group=grp"
  - "traefik.http.middlewares.test-oauth-intro.plugin.oAuthIntrospection.forwardHeaders.Scope=scope"
http:
  middlewares:
    test-oauth-intro:
      plugin:
        oAuthIntrospection:
          forwardHeaders:
            Group: grp
            Scope: scope
[http.middlewares]
  [http.middlewares.test-oauth-intro.plugin.oAuthIntrospection]
    [http.middlewares.test-oauth-intro.plugin.oAuthIntrospection.forwardHeaders]
      Group = "grp"
      Scope = "scope"

username

Optional, Default=""

The username option sets the claim that will be evaluated to populate the clientusername in the accessLog.

labels:
  - "traefik.http.middlewares.test--oauth-intro.plugin.oAuthIntrospection.username=userId"
apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
  name: test--oauth-intro
spec:
  plugin:
    oAuthIntrospection:
      username: userId
- "traefik.http.middlewares.test--oauth-intro.plugin.oAuthIntrospection.username=userId"
"labels": {
    "traefik.http.middlewares.test--oauth-intro.plugin.oAuthIntrospection.username": "userId"
}
labels:
  - "traefik.http.middlewares.test--oauth-intro.plugin.oAuthIntrospection.username=userId"
http:
  middlewares:
    test-jwt:
      plugin:
        oAuthIntrospection:
          username: userId
[http.middlewares]
  [http.middlewares.test--oauth-intro.plugin.oAuthIntrospection]
    username = userId

claims

Optional, Default=""

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

labels:
  - "traefik.http.middlewares.test-oauth-intro.plugin.oAuthIntrospection.claims=Equals(`grp`, `admin`)"
apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
  name: test-oauth-intro
spec:
  plugin:
    oAuthIntrospection:
      claims: Equals(`grp`, `admin`)
- "traefik.http.middlewares.test-oauth-intro.plugin.oAuthIntrospection.claims=Equals(`grp`, `admin`)"
"labels": {
  "traefik.http.middlewares.test-oauth-intro.plugin.oAuthIntrospection.claims": "Equals(`grp`, `admin`)"
}
labels:
  - "traefik.http.middlewares.test-oauth-intro.plugin.oAuthIntrospection.claims=Equals(`grp`, `admin`)"
http:
  middlewares:
    test-oauth-intro:
      plugin:
        oAuthIntrospection:
          claims: Equals(`grp`, `admin`)
[http.middlewares]
  [http.middlewares.test-oauth-intro.plugin.oAuthIntrospection]
    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 will return 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-oauth-intro.plugin.oAuthIntrospection.source=oAuthIntroSource"
  - "traefik.http.middlewares.test-oauth-intro.plugin.oAuthIntrospection.tokenQueryKey=tok"
  - "traefik.http.middlewares.test-oauth-intro.plugin.oAuthIntrospection.tokenTypeHint=access_token"
  - "traefik.http.middlewares.test-oauth-intro.plugin.oAuthIntrospection.forwardHeaders.Group=grp"
  - "traefik.http.middlewares.test-oauth-intro.plugin.oAuthIntrospection.forwardHeaders.Expires-At=exp"
  - "traefik.http.middlewares.test-oauth-intro.plugin.oAuthIntrospection.claims=Equals(`grp`, `admin`)"
apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
  name: test-oauth-intro
spec:
  plugin:
    oAuthIntrospection:
      source: oAuthIntroSource
      tokenQueryKey: tok
      tokenTypeHint: access_token
      forwardHeaders:
        Group: grp
        Expires-At: exp
      claims: Equals(`grp`, `admin`)
- "traefik.http.middlewares.test-oauth-intro.plugin.oAuthIntrospection.source=oAuthIntroSource"
- "traefik.http.middlewares.test-oauth-intro.plugin.oAuthIntrospection.tokenQueryKey=tok"
- "traefik.http.middlewares.test-oauth-intro.plugin.oAuthIntrospection.tokenTypeHint=access_token"
- "traefik.http.middlewares.test-oauth-intro.plugin.oAuthIntrospection.forwardHeaders.Group=grp"
- "traefik.http.middlewares.test-oauth-intro.plugin.oAuthIntrospection.forwardHeaders.Expires-At=exp"
- "traefik.http.middlewares.test-oauth-intro.plugin.oAuthIntrospection.claims=Equals(`grp`, `admin`)"
"labels": {
  "traefik.http.middlewares.test-oauth-intro.plugin.oAuthIntrospection.source": "oAuthIntroSource"
  "traefik.http.middlewares.test-oauth-intro.plugin.oAuthIntrospection.tokenQueryKey": "tok"
  "traefik.http.middlewares.test-oauth-intro.plugin.oAuthIntrospection.tokenTypeHint": "access_token"
  "traefik.http.middlewares.test-oauth-intro.plugin.oAuthIntrospection.forwardHeaders.Group": "grp"
  "traefik.http.middlewares.test-oauth-intro.plugin.oAuthIntrospection.forwardHeaders.Expires-At": "exp"
  "traefik.http.middlewares.test-oauth-intro.plugin.oAuthIntrospection.claims": "Equals(`grp`, `admin`)"
}
labels:
  - "traefik.http.middlewares.test-oauth-intro.plugin.oAuthIntrospection.source=oAuthIntroSource"
  - "traefik.http.middlewares.test-oauth-intro.plugin.oAuthIntrospection.tokenQueryKey=tok"
  - "traefik.http.middlewares.test-oauth-intro.plugin.oAuthIntrospection.tokenTypeHint=access_token"
  - "traefik.http.middlewares.test-oauth-intro.plugin.oAuthIntrospection.forwardHeaders.Group=grp"
  - "traefik.http.middlewares.test-oauth-intro.plugin.oAuthIntrospection.forwardHeaders.Expires-At=exp"
  - "traefik.http.middlewares.test-oauth-intro.plugin.oAuthIntrospection.claims=Equals(`grp`, `admin`)"
http:
  middlewares:
    test-oauth-intro:
      plugin:
        oAuthIntrospection:
          source: oAuthIntroSource
          tokenQueryKey: tok
          tokenTypeHint: access_token
          forwardHeaders:
            Group: grp
            Expires-At: exp
          claims: Equals(`grp`, `admin`)
[http.middlewares]
  [http.middlewares.test-oauth-intro.plugin.oAuthIntrospection]
    source = "oAuthIntroSource"
    tokenQueryKey = "tok"
    tokenTypeHint = "access_token"
    claims = "Equals(`grp`, `admin`)"
    [http.middlewares.test-oauth-intro.plugin.oAuthIntrospection.forwardHeaders]
      Group = "grp"
      Expires-At = "exp"