Token Exchange
In HelseID, Token Exchange is based on the IETF specification RFC 8693: OAuth 2.0 Token Exchange, which describes a protocol for receiving Tokens from an Athorization Server for use of "impersonation" and "delegation".
This specification can be used in HelseID when APIs have the need to call other APIs, on behalf of an authenticated person and/or organization.
Principal terms
The concept of Token Exchange introduces some new terms:
subject_token
: An Access Token issued by HelseID, that can contain claims that identifies an authenticated user (person) and/or the organization that has legal rights to the Client.
subject client
: A Client that has received a subject_token
from HelseID.
actor client
: A Client that needs to exchange a subject_token
by the use of the Token Exchange mechanism. The purpose of this would be to receive an new Access Token, that can be used for authorization against another server. In most cases, Actor client
will be an API that wants to receive data from another API, which also is dependent upon HelseID for authorization.
The Token Exchange flow
The usage of Token Exchange in context of HelseID is arranged with a flow where a subject client
requests an Access Token from HelseID. Following that, the subject client
makes use of this Access Token against an API that contains an actor client
. The actor client
then performs the Token Exchange call itself, however, the total flow can be somewhat complex.
The complete flow (with one Token Exchange)
- The Subject Client requires authentication of a user, and requests access to API 1
- HelseID issues an Access Token (AT#1,
subject_token
) that will give access to API 1 - The Subject Client calls API 1, with AT#1 in the Authorization header
- API 1 needs data from API 2, and this requires an Access Token
- The Actor Client (API 1) performs a Token Exchange request against the Token endpoint in HelseID, and requests scope(s) for API 2
- In this request, AT#1 is the
subject_token
- In this request, AT#1 is the
- HelseId issues a new Access Token (AT#2), and sends it in the response to the Actor client (API 1)
- The Actor client (API 1) calls API 2 with AT#2 in the Authorization header
The request to HelseId (the call to the Token endpoint)
In order to be issued an Access Token by the Token Exchange mechanism, the Actor Client must use the Token endpoint in the same way as a "normal" HelseID-Client. However, some extra parameters must be set in the call:
grant_type
: must always beurn:ietf:params:oauth:grant-type:token-exchange
subject_token
: The Access Token that should be exchanged with a new Access Tokensubject_token_type
: must always beurn:ietf:params:oauth:token-type:access_token
An example of a POST-request to HelseID for Token Exchange:
POST /connection/token
grant_type=urn:ietf:params:oauth:grant-type:token-exchange&
scope=api1 api2&
subject_token=eyJhbGciOiJSUzI...lZ22kWJV4pHr8t&
subject_token_type=urn:ietf:params:oauth:token-type:access_token&
client_assertion=eyJhbGciOiJSUzI...lZ22kWJV4pHr8t&
client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer
client_id=3c349ff9-20ea-4026-bf8f-f2fb9ed1bb59&
Success response
If HelseID issues an Access Token, the response will be equal to the one specified in RFC8693 (Token Exchange).
Error messages from the Token endpoint
When an error occurs in the call to the Token endpoint, HelseID will issue an error message, with a more detailed error description. The table below indicates the most common error messages:
Error message | Error description | Interpretation of the error |
---|---|---|
invalid_request |
invalid subject_token - [detailed information] |
This error will occur when the subject Token is not issued by HelseID, or the Token lifetime has expired. |
invalid_request |
not permitted |
This error will occur when the subject client is not configured for the use of Token Exchange against the actor client . |
invalid_target |
invalid scopes requested |
This error will occur when the actor client requests an scopes that are spread across more than one API-resource. |
invalid_request |
subject_token exchanged too many times (5) |
This error will occur when the actor client tries to exchange a Token that has already been exchanged too many times. |
invalid_request |
The audience in the subject token and the client with client_id '[client_id]' have different configuration owners. |
The Actor Client is set up for another configuration owner than the one listed in the aud (audience) claim in the subject token . |
See also general error messages.
The issued Access Token
With a successful Token Exchange, the actor client
gets issued an Access Token.
The following claim will be changed in the new token:
aud
: audience for API 2
The following claim identifies the actor client
:
act
: A set of claims that identifies the organization and the Client that operates on behalf of this organization, and/or an authenticated person. Theact
claim is structured as follows:- iss: The HelseID Authorization Server that has authenticated the
actor client
- client_id: An identifier for the
actor client
helseid://claims/client/claims/orgnr_parent
: The parent organization numberhelseid://claims/client/claims/orgnr_child
: The child organization number
- iss: The HelseID Authorization Server that has authenticated the
For call chains where Token Exchange is used more than once, you will get a nested structure of act
claims. The innermost Actor Client will be the oldest, and the outermost Actor Client will be the newest, and as such, the active Actor Client for the relevant Access Token.
The following claim identifies the subject client
:
helseid://claims/client/original_client_id
: The Client ID from the firstsubject_token
in the call chain
Claims that will be copied from the subject_token
:
- All claim with the prefix
helseid://
sub
: A HelseID spesific identificator for an authenticated username
: The name of the authenticated usergiven_name
: The given name of the authenticated usermiddle_name
: The middle name of the authenticated userfamily_name
: The family name of the authenticated usersid
: The session ID for the authenticated useridp
: The IDP used for authenticating the useramr
: The methdod for logging on the userauth_time
: The time when the user was authenticated
⚠️ Consequences for this (for API owners):
Claims that do not exist in the incoming Token ( subject_token
), will not be present in the new Token, even if API 2 requires this information. An example of this kind of information might be claims which indicate the identity of a logged in user.
An example of an Access Token from Token exchange
{
"alg": "RS256",
"kid": "78667F90DC11BF04BD9467D1F9120C4A4340B4CF",
"x5t": "eGZ_kNwRvwS9lGfR-RIMSkNAtM8",
"typ": "JWT"
}.{
"iss": "https://helseid-sts.test.nhn.no",
"nbf": 1713888289,
"iat": 1713888289,
"exp": 1713888889,
"aud": "nhn:helseid-public-samplecode",
"cnf": {
"jkt": "aoezvIe32RdFpTP4FIxwBYb6VGX0dp3ecvoVjFkdXQk"
},
"scope": [
"nhn:helseid-public-samplecode/client-credentials"
],
"amr": [
"pwd"
],
"client_id": "helseid-sample-token-exchange-actor-client",
"helseid://claims/client/original_client_id": "helseid-sample-token-exchange-subject-client",
"client_amr": "private_key_jwt",
"helseid://claims/identity/pid": "11857998857",
"helseid://claims/identity/security_level": "4",
"name": "VIRKELIG KJELTRING",
"jti": "25488FC67BAB3419DC6E70C273FBB871",
"sid": "0FAB2BC0164BF60B39ECED460E2A56BA",
"act": {
"iss": "https://helseid-sts.test.nhn.no",
"client_id": "helseid-sample-token-exchange-actor-client",
"helseid://claims/client/claims/orgnr_parent": "999977774"
},
"helseid://claims/client/claims/orgnr_parent": "999977774",
"oldsub": "LHcSgmD3Q/SO/0dLxXvhKBnh1ebeq61C3ndgEIkptbo=",
"sub": "vJs8Xr2F58spTNEPHM/a07KdZtSBGLQN9EmHuBGLy/c=",
"auth_time": 1713888282,
"idp": "testidp-oidc",
"helseid://claims/client/amr": "rsa_private_key",
"helseid://claims/client/client_name": "helseid-sample-token-exchange-actor-client",
"helseid://claims/client/client_tenancy": "single-tenant"
}.[Signature]