Add ID token support for token exchange#19076
Add ID token support for token exchange#19076bkoragan wants to merge 2 commits intospring-projects:mainfrom
Conversation
|
@jgrandja Please review and share if any feedback. Note: reference to our proposal discussion :#19048 (comment) |
|
Great PR. @bkoragan
Correct me if my questions are invalid. |
|
@kpur-sbab Good Points! Thanks for the review/feedback! I ve pushed updates for both comments - below are the details.. Please take another look and let me know if you have any further observations. Thanks! 1# Added ID_TOKEN_JWK_SET_URL to ConfigurationSettingNames. Client with a typed getter and builder on ClientSettings, following the same pattern as the existing JWK_SET_URL. here is sample configuration: // Client registration - set the IdP's JWKS URL
RegisteredClient client = RegisteredClient.withId(UUID.randomUUID().toString())
.clientId("cicd-client")
.clientSettings(ClientSettings.builder()
.idTokenJwkSetUrl("https://gitlab.com/oauth/discovery/keys")
.build())
.build();
// Resolver - no factory required, reads from ClientSettings
@Bean
OidcIdTokenSubjectTokenResolver subjectTokenResolver() {
return new OidcIdTokenSubjectTokenResolver();
}The custom JwtDecoderFactory constructor is still available for advanced cases like multi-issuer routing or custom validation.. 2# only the sub claim flows automatically into the access token. other ID token claims like email, groups, or name, etc are intentionally not copied automatically for two reasons/concerns: a) security - blindly copying external IdP claims into access tokens could leak information the authorization server shouldn't be asserting. The external IdP's claim schema may not match the authorization server's resource model. Instead, the provider stores all ID token claims as a well-known authorization attribute (SUBJECT_TOKEN_CLAIMS_ATTRIBUTE), giving users/clients full control via the standard OAuth2TokenCustomizer - the same extension point already used for other grant types. Here is example : mapping specific claims in your application @Bean
OAuth2TokenCustomizer<JwtEncodingContext> jwtCustomizer() {
return (context) -> {
if (AuthorizationGrantType.TOKEN_EXCHANGE.equals(
context.getAuthorizationGrantType())) {
Map<String, Object> idTokenClaims = context.getAuthorization()
.getAttribute(OAuth2TokenExchangeAuthenticationProvider
.SUBJECT_TOKEN_CLAIMS_ATTRIBUTE);
if (idTokenClaims != null) {
context.getClaims().claim("email", idTokenClaims.get("email"));
context.getClaims().claim("groups", idTokenClaims.get("groups"));
}
}
};
}This approch follows the same principle as the existing token exchange flow so that the framework provides the data, the client/application decides what to expose.. ++ @jgrandja I'd appreciate feedback on the following on above changes:
|
Introduce support for exchanging externally-issued OIDC ID tokens for access tokens via the OAuth 2.0 Token Exchange Grant per RFC 8693 Section 3. - Add urn:ietf:params:oauth:token-type:id_token as a supported token type in the converter - Add OAuth2TokenExchangeSubjectTokenResolver strategy interface for resolving external subject tokens - Add OidcIdTokenSubjectTokenResolver as the default implementation using JwtDecoderFactory - Modify OAuth2TokenExchangeAuthenticationProvider to delegate to the resolver before falling back to the authorization service - Auto-wire the resolver bean in the configurer Closes spring-projectsgh-19048 Signed-off-by: Bapuji Koraganti <bapuk.2008@gmail.com>
- Add ID_TOKEN_JWK_SET_URL to ConfigurationSettingNames and typed getter/builder to ClientSettings - Add default no-arg constructor to resolver that reads idTokenJwkSetUrl from ClientSettings automatically - Store subject token claims as authorization attribute so OAuth2TokenCustomizer can map ID token claims to the generated access token Closes spring-projectsgh-19048 Signed-off-by: Bapuji Koraganti <bapuk.2008@gmail.com>
|
@bkoragan Perfect thanks for the detailed explanation |
|
@bkoragan Thanks for the PR. Just a heads up that I have a few high priority items I need to complete before we release Please give me a couple of weeks and then I can do an initial review. I've scheduled this for Thanks for your patience. |
I discussed this approach and received positive feedback. This PR implements the proposed design.
Summary
Add support for exchanging externally-issued OIDC ID tokens for access tokens via the OAuth 2.0 Token Exchange Grant per RFC 8693 - sec 3.
New classes:
OAuth2TokenExchangeSubjectTokenResolver— strategy interface for resolving external subject tokensOAuth2TokenExchangeSubjectTokenContext— context object with resolved principal, claims, and scopesOidcIdTokenSubjectTokenResolver— default implementation usingJwtDecoderFactory<RegisteredClient>Modified classes:
OAuth2TokenExchangeAuthenticationConverter— acceptid_tokenas supported token typeOAuth2TokenExchangeAuthenticationProvider— delegate to resolver before falling back to authorization serviceOAuth2TokenEndpointConfigurer— auto-wire resolver beanDesign Decisions
final; only token resolution is pluggablenullfor unsupported types (chain-of-responsibility, same pattern asAuthenticationConverter)JwtDecoderFactory<RegisteredClient>follows the same pattern asJwtClientAssertionDecoderFactorygetOptionalBean(same as OAuth2AuthorizationService,OAuth2TokenGenerator`)Configuration Example