Implement OAuth 2.0 Token Exchange support - #2335
Merged
Merged
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.SubjectTokenTypesorOpenIddictServerOptions.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
TokenValidationParametersinstance 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.RequestedTokenTypescollection. The default requested token type can also be configured using the advancedOpenIddictServerOptions.DefaultRequestedTokenTypeoption.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.AuthorizationIdclaim 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.RefreshTokenLifetimeoptions and the per-clientSettings.TokenLifetimes.AccessToken,Settings.TokenLifetimes.IdentityTokenandSettings.TokenLifetimes.RefreshTokensettings are always used. For any other type of token, theOpenIddictServerOptions.IssuedTokenLifetimeandSettings.TokenLifetimes.IssuedTokenartifacts are used instead.When the issued token is an access or identity token, each claim must be granted the
access_tokenorid_tokendestination to be copied to the issued token. When it's any other type of token, the newissued_tokendestination must be used instead.Returning a refresh token in token exchange responses is allowed and only requires granting the
offline_accessscope, 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_tokenparameter, 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.