Skip to main content

Integration Token Exchange

Token exchange trades an ordinary session Access Token you already hold for a restricted one, without a fresh login. It is the natural fit when your application or backend already has a signed-in user and now needs to call an integration on that user's behalf.

Read Integration Scopes first — it describes what the resulting token contains and how a tenant admin configures one.

Not the Google/Apple token exchange

This page is about downscoping an azuma doa session token. Trading a Google or Apple ID token for an azuma doa session token is a different feature — see Social Token Exchange.

Both doors below require the scope to list the Token exchange mode and the tenant switch Integration Scopes: Token Exchange to be on.

From an application

POST deviceBinding/token/v1/mobile/{applicationId}/exchange

A device-bound request, signed with the bound device's key, consuming a login challenge from the challenge API — the same credentials an ID login requires. Two credentials matter here and both are checked: the device assertion proves the caller holds the device, and the subject token proves there is a live session to downscope.

The inner payload:

FieldDescription
idThe user's account ID. Must match the subject token's subject.
subjectTokenThe session Access Token being downscoped.
scopeThe configured scope to mint for, on its own.
requestChallengeA login challenge.
deviceDataOptional device information.
deviceBoundIntegrityVerificationDataRequired only when the tenant has integrity validation enabled for token exchange.

From a backend

POST token/v1/exchange on the Admin API, authenticated with client credentials carrying the tenant_id_admin scope — see Client Credentials.

Use this when your own server received a session token from one of your users and needs a narrower token to pass onward, rather than forwarding the user's full session token.

The subject token must be a device-binding session token

Both doors accept only a Device Binding session token. A token from the Authorization Code flow is refused with AuthUnauthorized, as is an integration token — there is no exchanging an exchanged token.

IP filtering applies

This route sits behind the tenant's admin IP allowlist. If your tenant has IP filtering enabled, add your backend's egress addresses to it or every call is refused with Forbidden.

FieldDescription
applicationIdThe application the subject token was issued for.
idThe user's account ID. Must match the subject token's subject.
subjectTokenThe session Access Token being downscoped.
scopeThe configured scope to mint for.
The tenant comes from your client

There is no tenant field in the request, deliberately. The tenant is taken from the client you authenticated as, and the user is then resolved inside that tenant — so a backend cannot mint a token for another tenant's user.

No ePA server-side

The health-id-epa-integration scope is refused on this door. See Health-ID ePA Integration.

The response

Both doors return the same shape:

{
"accessToken": "eyJhbGciOiJFUzUxMiIsImtpZCI6...",
"expiresIn": 660,
"scope": "partner-acme",
"tokenType": "bearer",
"issuedTokenType": "urn:ietf:params:oauth:token-type:access_token"
}
There is no refresh token

The response has no refreshToken field, by design. An integration token is re-minted, never renewed — and an integration token can never itself be exchanged again.

Read expiresIn rather than assuming the configured lifespan: an exchanged token receives whichever is shorter, the scope's configured lifespan or the remaining life of the token it came from. Nothing derived outlives its source.

Lifetime and revocation

An exchanged token is tied to the session it was derived from, which governs when it disappears:

EventEffect on the integration token
Logout of the session it was minted fromRevoked with it.
Logout of a different session of the same userSurvives.
Global logoutRevoked, along with every other token for that user.
Revoking the integration token itselfEnds the whole parent session — its Access Token and its Refresh Token. Revoking any token in the family revokes the family.
Removing the scope from the tenant configurationNo new tokens are minted; tokens already issued live until they expire.
Revoking an integration token logs the user out

An exchanged token shares the identity of the session it came from, so revocation cannot be aimed at one half of the pair. If you revoke an integration token — say because it leaked — the user's session ends with it and they must sign in again. When you only want the integration token gone, prefer letting it expire: they are short-lived by design and are re-minted rather than renewed.

Logout is not a hard guarantee for an exchange in flight

An exchange that is already in progress when the logout lands may still produce a token, because the new token is written after its parent has been read. Such a token is bounded by the cap — it can outlive the logout by at most the parent's remaining lifetime — but do not treat a successful logout as proof that no integration token for that session exists. Where it matters, verify at introspection rather than assuming.

Errors

CodeMeaning
ScopeNotAllowedThe scope is not configured, not configured for this door, the tenant switch is off, or more than one scope was requested.
AuthUnauthorizedThe subject token is not valid for this user, this application and this tenant, or it is not an ordinary device-binding session token.
AuthLoginIntegrityDataMissingThe tenant requires integrity data for token exchange and none was supplied.
ForbiddenBackend door only — the caller is not a client-credentials client, the client has no tenant, the user does not belong to that tenant, or the request came from an address outside the tenant's IP allowlist.
TokenExchangeFailedThe exchange could not be completed.

Requesting health-id-epa-integration can additionally fail with HealthIdKvnrBindingMissing or HealthIdEpaTokenIssuanceFailed — see Health-ID ePA Integration.

See the Admin-API and Device-Binding-API Swagger documentation (linked from the azuma doa overview) for the full request and response schemas.