-
Notifications
You must be signed in to change notification settings - Fork 109
Description
I'd like to add support for the OAuth 2.0 Token Exchange specification (RFC 8693).
Token Exchange allows clients or backend services to exchange an existing access token for a new one with modified characteristics like:
- A narrowed set of scopes (downscoping)
- A different target audience (resource server)
- A shorter TTL
- (Optionally) an “actor token” representing service-to-service delegation. Leave this out for now since we don't have such a concept in the library.
Use Cases
- Public clients that want to obtain short-lived, least-privilege tokens for specific flows
- Backends minting temporary delegated tokens for downstream APIs on behalf of a user
- Federated environments that need audience-restricted tokens between multiple microservices
Why?
Right now there’s no clean way to mint derived tokens. A client would need to go through the authorization flow a second time to get a new token with narrower scopes.
How?
1. Expand the tokenEndpoint (RFC 8693)
In addition to the existing grant types, add support for urn:ietf:params:oauth:grant-type:token-exchange. The body should contain a subject_token (the user's current issued token) and optionally new scope or audience.
Example Request:
POST /token/exchange
Content-Type: application/x-www-form-urlencoded
Authorization: Basic <client creds>
grant_type=urn:ietf:params:oauth:grant-type:token-exchange&
subject_token=<access_token>&
subject_token_type=urn:ietf:params:oauth:token-type:access_token&
scope=user:read
2. Add a new exchangeToken method to OAuthHelpers
const shortToken = await exchangeToken({
subjectToken: accessToken,
scope: ["user:read"], // Optional
aud: "serviceB" // Optional
expiresIn: 60 // Optional (have a default built in)
});
3. Verify
exchangeToken first uses unwrapToken from #114 to validate the subject_token that was passed in and pull its grant info.
It then validates that the requested changes are allowed by the original grant:
if (!requestedScopes.every(scope => tokenSummary.grant.scope.includes(scope))) {
return createErrorResponse("invalid_scope", "Requested scope exceeds parent grant");
}
4. Mint new token
If the request is kosher, mint a new token in the same way handleAuthorizationCodeGrant does.
Other Details
- Just like we have an
allowImplicitFlowflag, I would add anallowTokenExchangeGrantflag for those who don't want this capability on the public API.
/**
* Controls whether OAuth 2.0 Token Exchange (RFC 8693) is allowed.
* When false, the token exchange grant type will not be advertised in metadata
* and token exchange requests will be rejected.
* Defaults to false.
*/
allowTokenExchangeGrant?: boolean;
- RFC 6749 should be implemented too, which allows tokens to be minted with a subset of the scopes authorized by the grant, in both the auth code and refresh flows.
For Maintainers
@kentonv @mattzcarey @threepointone
Implemented in #120
