Skip to content

Conversation

kevinchalet
Copy link
Member

Fixes #1249.

This PR introduces full support for OAuth 2.0 Token Exchange in both the client and server stacks.
A few design choices were already mentioned here, but here's some additional details:

  • Only access, identity and refresh tokens can be used as subject or actor tokens: other types of internal codes or tokens (e.g authorization or device codes) CANNOT be added to OpenIddictServerOptions.SubjectTokenTypes or OpenIddictServerOptions.ActorTokenTypes.

  • It is possible to customize the list of allowed subject and actor token types to support arbitrary JWT assertions or custom token types, but in both cases, a custom ValidateToken(Context) event handler
    will be required to populate the TokenValidationParameters instance needed to process these assertions/tokens.

  • By default, only access tokens can be requested but it's possible to add other types of tokens to the OpenIddictServerOptions.RequestedTokenTypes collection. The default requested token type can also be configured using the advanced OpenIddictServerOptions.DefaultRequestedTokenType option.

  • The specification doesn't mandate any specific token validation routine but OpenIddict enforces the following rules:

    • For access and identity tokens used as subject or actor tokens, the caller MUST be listed either as a valid audience if the token is receiver-bound (i.e has at least one audience attached) and/or a valid presenter if the token is sender-bound (i.e has at least one presenter listed). If the token doesn't any audience and/or presenter attached, the token is assumed to be usuable by any client that was granted the token exchange grant permission.

    • For refresh tokens, the caller MUST be listed as a valid presenter if the token is sender-bound (i.e has at least one presenter listed). If the token doesn't any presenter attached, the token is assumed to be usuable by any client that was granted the token exchange grant permission.

    • For tokens that are not natively supported (e.g custom token types or tokens issued by third-party services), it is up to the user to implement his own rules via a custom ValidateToken(Context) event handler.

  • When refresh tokens are used as subject or actor tokens, they are not marked as redeemed even if the rolling refresh tokens option is enforced.

  • Unlike the refresh token grant, OpenIddict doesn't force flowing the internal claims (including the authorization identifier) from the subject token to the issued token (and refresh token, if one is returned in the token exchange response), meaning it will - by default - create a new ad-hoc authorization, completely separate from the subject token used in the request: users who prefer attaching the resulting token to the same authorization as the subject token used in the request can flow the Claims.Private.AuthorizationId claim from the subject token principal to the principal instance used for the SignIn() operation.

  • When the issued token is an access, identity or refresh token, the OpenIddictServerOptions.AccessTokenLifetime/OpenIddictServerOptions.IdentityTokenLifetime/OpenIddictServerOptions.RefreshTokenLifetime options and the per-client Settings.TokenLifetimes.AccessToken, Settings.TokenLifetimes.IdentityToken and Settings.TokenLifetimes.RefreshToken settings are always used. For any other type of token, the OpenIddictServerOptions.IssuedTokenLifetime and Settings.TokenLifetimes.IssuedToken artifacts are used instead.

  • When the issued token is an access or identity token, each claim must be granted the access_token or id_token destination to be copied to the issued token. When it's any other type of token, the new issued_token destination must be used instead.

  • Returning a refresh token in token exchange responses is allowed and only requires granting the offline_access scope, as for any other grant.

  • When the issued token is already a refresh token, OpenIddict won't generate and return a second refresh token via the refresh_token parameter, as generating two refresh tokens granting the same access level would be pointless.

  • Support for registered audiences/resources and per-client audiences and resources permissions will be added in a separate PR.

@kevinchalet kevinchalet added this to the 7.0.0-preview4 milestone Jun 3, 2025
@kevinchalet kevinchalet self-assigned this Jun 3, 2025
@kevinchalet kevinchalet merged commit 8c94cb7 into openiddict:dev Jun 3, 2025
6 checks passed
@kevinchalet kevinchalet deleted the token_exchange branch June 3, 2025 04:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Implement built-in delegation/impersonation support (RFC8693)

1 participant