Implement OAuth 2.0 Token Exchange support #2335
Merged
+5,940
−1,494
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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
orOpenIddictServerOptions.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 handlerwill 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 advancedOpenIddictServerOptions.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 theSignIn()
operation.When the issued token is an access, identity or refresh token, the
OpenIddictServerOptions.AccessTokenLifetime
/OpenIddictServerOptions.IdentityTokenLifetime
/OpenIddictServerOptions.RefreshTokenLifetime
options and the per-clientSettings.TokenLifetimes.AccessToken
,Settings.TokenLifetimes.IdentityToken
andSettings.TokenLifetimes.RefreshToken
settings are always used. For any other type of token, theOpenIddictServerOptions.IssuedTokenLifetime
andSettings.TokenLifetimes.IssuedToken
artifacts are used instead.When the issued token is an access or identity token, each claim must be granted the
access_token
orid_token
destination to be copied to the issued token. When it's any other type of token, the newissued_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.