Token exchange

Promote a user's token from a public client to one minted for your confidential backend (RFC 8693).

POST /oauth/token

RFC 8693 token exchange, used to promote a user’s token: a backend holding a confidential client trades a token minted for a public client (the browser’s) for one minted to itself. Same user, tenant and session; aud becomes the confidential client and azp names the public client it came from.

Authorization

The confidential client’s secret. See Client authentication. The user’s access token goes in the body as subject_token.

Body

Form-encoded (application/x-www-form-urlencoded).

FieldTypeRequiredDescription
grant_typestringYesurn:ietf:params:oauth:grant-type:token-exchange
subject_tokenstringYesA valid user access token from this org.
subject_token_typestringYesurn:ietf:params:oauth:token-type:access_token
requested_token_typestringNoIf sent, urn:ietf:params:oauth:token-type:access_token.
client_idstringNoRequired with client_secret_post; taken from the Authorization header with client_secret_basic.
client_secretstringNoRequired with client_secret_post.

Response

200, with Cache-Control: no-store. There is no refresh token, and the new token never outlives the subject token: when it lapses, promote the subject again.

{
  "token_type": "Bearer",
  "access_token": "eyJ…",
  "expires_in": 3600,
  "issued_token_type": "urn:ietf:params:oauth:token-type:access_token",
  "expires_at": "2026-10-18T13:00:00.000Z"
}

Errors

Every refusal is 400 with an RFC 6749 code and nothing else.

StatuserrorWhen
400invalid_requestsubject_token_type or requested_token_type is not the access-token type, subject_token is missing, or client_id is malformed.
400invalid_clientUnknown or public client, a wrong secret, or a disallowed IP.
400invalid_grantsubject_token does not authenticate on this org, or is not a user token.
400invalid_requestThe host is not an org.
400unsupported_grant_typegrant_type is not one of the five the endpoint accepts.

Example

curl -X POST https://acme.loginwith.dev/oauth/token \
  -u cli_backend…:secret \
  -d grant_type=urn:ietf:params:oauth:grant-type:token-exchange \
  -d subject_token=eyJ… \
  -d subject_token_type=urn:ietf:params:oauth:token-type:access_token