JWT Token Exchange

With the API gatekeeper, each API call has to pass through the gatekeeper, which checks if the call is allowed. To avoid this bottleneck, Feide has implemented proof of concept support for RFC 8693 - Oauth2 token exchange. Here, the client receives a short lived, digitally signed token which it can pass directly to the API. The token is a JSON Web Token (JWT). The API can decode and parse it, verify the signature and validity times, and decide whether or not to allow the operation without involving a gatekeeper.


This is a proof of concept implementation. It may be changed or discontinued at short notice.

To obtain a JWT, a client makes a token exchange request to the token endpoint using the grant type urn:ietf:params:oauth:grant-type:token-exchange. It receives a response, which if succssful includes a JWT access token.

API configuration

APIs which are to consume JWT access tokens are configured in the API gatekeeper tab of the Dataporten Dashboard, just like APIs which use the API gatekeeper. A token exchange request includes audience and scope parameters. The audience must be the ID of an API registered with the gatekeeper, prefixed by https://n.feide.no/datasources/. Example: https://n.feide.no/datasources/coolapi. Scopes must be registered subscopes of the API. The dashboard is used to configure policies for access to scopes and APIs.

Scope values are given from different perspectives in the dashboard and in a token exchange request. If the API coolapi has the subscopes foo and bar, they are called gk_coolapi_foo and gk_coolapi_bar in the dashboard. In the token exchange request, they are named from the point of view of the API, and are simply called foo and bar.

Token exchange request

A token exchange request is made to the token endpoint using the HTTP “POST” method. Parameters are included in the HTTP request entity-body using the application/x-www-form-urlencoded format. The parameters are:


The value urn:ietf:params:oauth:grant-type:token-exchange selects Oauth2 token exchange.


The ID of the client making the exchange request.


The client secret of the client.


The API the JWT is intended for, prefixed by https://n.feide.no/datasources/.


A space separated list of desired scopes. It is up to the API how to interpret these scopes.


An access token obtained from Feide. It should give the client access to the scopes given as scope in the API given as audience.


Must be urn:ietf:params:oauth:token-type:access_token.

Here is an example:

POST https://auth.dataporten.no/oauth/token
content-type: application/x-www-form-urlencoded

&scope=foo bar

Successful response

A successful response has status 200 and an application/json body with the following attributes:


The type is Bearer.


The type is urn:ietf:params:oauth:token-type:jwt.


The JWT that was issued.


Number of seconds until JWT expires.


The scopes that were granted.

Here is an example:

    "token_type": "Bearer",
    "issued_token_type": "urn:ietf:params:oauth:token-type:jwt",
    "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI...",
    "expires_in": 299,
    "scope": "foo bar"

JWT access token

The token is an ASCII string. It is intended for a third party API. It consists of a header, a payload and a signature, with a . between them. Each are base64url encoded. See RFC 7519 - JSON Web Token (JWT). The payload is a json object with the following attributes:


Audience. The API should only accept the token if it is the intended audience.


Issuer. Value is https://auth.dataporten.no if token was issued by Feide.


Expiration time (in seconds since 1970-01-01T0:0:0 UTC).


Not valid before time. Protects against clock skew.


ID of the client that requested the token.


Subject - the identity which the token authenticates. Can be a dataporten user ID or a client ID.


The scopes that were granted.


Actor. It represents a chain of delegation. E.g., a client could authorize an API to access another on its behalf. We do not currently support delegation in JWT tokens, so the chain is only one level deep. It is a json object with a single attribute: sub, with the same value as sub in the token.

Here is an example of what the payload may look like after decoding:

    "aud": "https://n.feide.no/datasources/coolapi",
    "iss": "https://auth.dataporten.no",
    "exp": 1610448035,
    "nbf": 1610447735,
    "client_id": "208335d4-e8c1-4910-8928-05b2e5b14127",
    "sub": "208335d4-e8c1-4910-8928-05b2e5b14127",
    "scope": "foo bar",
    "act": {
        "sub": "208335d4-e8c1-4910-8928-05b2e5b14127",

Validating a JWT access token

The consumer must validate the access token. In particular, iss has to be auth.dataporten.no and aud has to be the same as in the request. See the JSON Web Token (JWT) Profile for OAuth 2.0 Access Tokens for full details about JWT access token validation.

Token lifetimes

A JWT cannot be reliably revoked. Therefore, the lifetime is only 5 minutes. If the client needs access after the token has expired, it must obtain a new one.