Accessing data using JWT Token Exchange

Data sources protected by Feide are normally configured in the customer portal. The customer portal is used to configure policies for access to data sources and access levels. The data are accessed using JSON Web Tokens (JWTs). The data consumer obtains an OpenID Connect (OIDC) access token. It exchanges it for another token tailored for the data source, carrying all information the source needs to determine what information the consumer may access. This token is a JWT. It is short lived and digitally signed, allowing the data source to verify that it was issued by Feide, intended for the source, and still valid. The exchange conforms to RFC 8693 - Oauth2 token exchange.

In order to access a data source, you need a Feide service registered in the customer portal. It may be a service that users log in to, or a headless service with no associated user. It needs an OpenID Connect configuration with rights to access the data source and the appropriate access levels within it.

The data owner must approve access to the data, unless they have chosen to make the data source public.

Access starts with an OIDC access token. This could be the token the service receives when the user logs in, or a token which a headless service obtains with the client credentials flow.

Next, the service presents the access token to Feide and, if successful, receives back a JWT token for the data source. The request to Feide is a token exchange request to the token endpoint. For this request, grant type urn:ietf:params:oauth:grant-type:token-exchange is used. The response from the token endpoint is a token exchange response, which, unless the request fails, includes a JWT access token.

With each request to a data source endpoint, the service includes the JWT as a bearer token in the Authorization header. The data source verifies the signature of the token and checks that it is still valid, issued by Feide and intended for the data source. Finally, it checks which access levels the token authorizes, and responds appropriately.

Token exchange request

A token exchange request includes audience and scope parameters. The audience must be the data source UUID registered in the customer portal, prefixed by https://n.feide.no/datasources/. Example: https://n.feide.no/datasources/02d0f79b-7fbc-422b-bb31-a4d22121f040. The valid values for scopes are the identifiers of the access levels which are registered in the customer portal for the data source.

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:

audience

The UUID of the data source the JWT is intended for, prefixed by https://n.feide.no/datasources/.

client_id

The ID of the application making the exchange request.

client_secret

The client secret of the application.

grant_type

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

scope

A space separated list of identifiers of desired access levels. It is up to the data source how to interpret them.

subject_token

An access token obtained from Feide. It should give the application access to the access levels given as scope in the data source given as audience.

subject_token_type

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

audience=https://n.feide.no/datasources/02d0f79b-7fbc-422b-bb31-a4d22121f040
&client_id=208335d4-e8c1-4910-8928-05b2e5b14127
&client_secret=5df85658-d0c1-4348-890a-a204b24eca2e
&grant_type=urn:ietf:params:oauth:grant-type:token-exchange
&scope=read append
&subject_token=96c5a3aa-1af8-45bd-a4f8-4b7fb07d393f
&subject_token_type=urn:ietf:params:oauth:token-type:access_token

Successful response

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

token_type

The type is Bearer.

issued_token_type

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

access_token

The JWT that was issued.

expires_in

Number of seconds until JWT expires.

scope

The scopes that were granted.

Here is an example:

{
    "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI...",
    "token_type": "Bearer",
    "issued_token_type": "urn:ietf:params:oauth:token-type:jwt",
    "expires_in": 299,
    "scope": "read append"
}

JWT access token

The token is an ASCII string. It is intended for a third party data source. 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 containing claims.

If the token authenticates a user, it may contain claims about the user. A claim is only included if the service that requested the token and the data source both are authorized to access the claim.

The following claims are always included in the token:

aud

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

iss

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

iat

Time of issue. This and other time attributes are given in seconds since 1970-01-01T0:0:0 UTC.

exp

Expiration time.

nbf

Not valid before time. Protects against clock skew.

client_id

ID of the application that requested the token.

sub

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

scope

The scopes that were granted.

act

Actor. It represents a chain of delegation. E.g., an application could authorize a data source 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 client_id in the token.

The following user claims may be included:

name

Name of user who the token authenticates.

picture

Picture of user.

https://n.feide.no/claims/userid_sec

Secondary user ID of user.

https://n.feide.no/claims/eduPersonPrincipalName

eduPersonPrincipalName of user. Only for users who authenticated to the Feide IDP.

https://n.feide.no/claims/nin

Norwegian national identity number of user. Only for users who authenticated to ID-porten.

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

{
    "aud": "https://n.feide.no/datasources/02d0f79b-7fbc-422b-bb31-a4d22121f040",
    "iss": "https://auth.dataporten.no",
    "exp": 1610448035,
    "iat": 1610447735,
    "nbf": 1610447735,
    "client_id": "208335d4-e8c1-4910-8928-05b2e5b14127",
    "sub": "208335d4-e8c1-4910-8928-05b2e5b14127",
    "scope": "read append",
    "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. Furthermore, the token is only valid if the current time is in the interval between the iat and exp timestamps.

Token lifetimes

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