Skip to main content

OAuth 2.0 Token Exchange Authentication

Early Access

This feature is currently in early access.

The OAuth 2.0 Token Exchange middleware lets Hub API Gateway exchange an incoming subject token for a new access token scoped to a downstream service, following RFC 8693. It also supports Microsoft Entra ID's On-Behalf-Of flow, which uses a dedicated grant type instead of the RFC 8693 grant.

Every application brings its subject token to Hub API Gateway using one of the following sources:

  • A header (with a scheme if the token is provided using the Authorization header, the default),
  • A query parameter,
  • A cookie.

Hub API Gateway then calls the authorization server to exchange the subject token for a new access token, and forwards the request upstream with the exchanged token in the Authorization header.

RFC 8693 Token Exchange

In this mode, Hub API Gateway exchanges the subject token by sending the RFC 8693 urn:ietf:params:oauth:grant-type:token-exchange grant to the authorization server, as described in the diagram below.


To exchange a JWT subject token read from the Authorization header for a new access token, apply the following configuration after adjusting it to your needs:

---
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: oauth-token-exchange
namespace: apps
spec:
plugin:
oAuthTokenExchange:
method: rfc8693
url: https://tenant.auth0.com/oauth/token
clientID: "urn:k8s:secret:oauth-client:client_id"
clientSecret: "urn:k8s:secret:oauth-client:client_secret"
rfc8693:
subjectTokenType: "urn:ietf:params:oauth:token-type:jwt"
requestedTokenType: "urn:ietf:params:oauth:token-type:access_token"
audience: https://api.example.com
Keycloak compatibility

Keycloak requires rfc8693.subjectTokenType: "urn:ietf:params:oauth:token-type:access_token". Use this value instead of jwt when the authorization server is Keycloak.

Advanced Configuration

Advanced options are described in the reference page.

For example, you can find how to add a cache layer using a Redis store so Hub API Gateway does not exchange the same subject token on every request.

Pinning the client authentication method

By default, Hub API Gateway auto-detects whether the authorization server expects client credentials in an HTTP Basic header or in the request body, and reuses that detection for the exchange. If the authorization server rejects HTTP Basic, this detection adds one extra request to the authorization server on every token exchange.

Set rfc8693.clientAuthMethod to skip detection and send credentials the right way on the first try:

  • in_header — send credentials using HTTP Basic authentication.
  • in_params — send credentials in the request body.
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: oauth-token-exchange
namespace: apps
spec:
plugin:
oAuthTokenExchange:
method: rfc8693
url: https://tenant.auth0.com/oauth/token
clientID: "urn:k8s:secret:oauth-client:client_id"
clientSecret: "urn:k8s:secret:oauth-client:client_secret"
rfc8693:
subjectTokenType: "urn:ietf:params:oauth:token-type:jwt"
requestedTokenType: "urn:ietf:params:oauth:token-type:access_token"
audience: https://api.example.com
clientAuthMethod: in_params
No fallback once pinned

Auto-detection (the default) tries HTTP Basic first and falls back to the request body if the authorization server rejects it. Pinning rfc8693.clientAuthMethod removes that fallback along with the extra request. If you pin the wrong method for your authorization server, every exchange fails instead of falling back. Confirm which style your authorization server expects before pinning it in production.

Microsoft Entra ID On-Behalf-Of

Microsoft Entra ID diverges from RFC 8693 with its own On-Behalf-Of grant. Set method: entraID to use it. Entra ID requires client authentication, either with a client secret or with a client certificate.

Using a client secret

apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: oauth-token-exchange-entraid
namespace: apps
spec:
plugin:
oAuthTokenExchange:
method: entraID
url: https://login.microsoftonline.com/YOUR-TENANT-ID/oauth2/v2.0/token
clientID: "urn:k8s:secret:oauth-client:client_id"
clientSecret: "urn:k8s:secret:oauth-client:client_secret"
entraID:
scopes:
- api://downstream-app/.default

Using a client certificate

Instead of a client secret, Entra ID can authenticate the client with a signed JWT assertion built from an RSA certificate and private key:

apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: oauth-token-exchange-entraid-cert
namespace: apps
spec:
plugin:
oAuthTokenExchange:
method: entraID
url: https://login.microsoftonline.com/YOUR-TENANT-ID/oauth2/v2.0/token
clientID: "urn:k8s:secret:oauth-client:client_id"
entraID:
scopes:
- api://downstream-app/.default
clientCertificate:
certificate: "urn:k8s:secret:oauth-client-cert:certificate"
privateKey: "urn:k8s:secret:oauth-client-cert:privateKey"
RSA certificates only

Microsoft Entra ID only accepts RSA certificate credentials signed with PS256. PKCS1 and PKCS8 RSA private keys are supported; other key types are rejected.