Skip to content

Commit c037fff

Browse files
committed
Update RefreshOidcIdTokenHandler to improve decoupling
Signed-off-by: Hao <[email protected]>
1 parent 8f7b48e commit c037fff

File tree

3 files changed

+71
-14
lines changed

3 files changed

+71
-14
lines changed

config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OAuth2LoginConfigurer.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -395,8 +395,13 @@ public void init(B http) throws Exception {
395395
}
396396
http.authenticationProvider(this.postProcess(oidcAuthorizationCodeAuthenticationProvider));
397397

398-
RefreshOidcIdTokenHandler refreshOidcIdTokenHandler = new RefreshOidcIdTokenHandler(
399-
oidcAuthorizationCodeAuthenticationProvider);
398+
RefreshOidcIdTokenHandler refreshOidcIdTokenHandler = new RefreshOidcIdTokenHandler();
399+
if (this.getSecurityContextHolderStrategy() != null) {
400+
refreshOidcIdTokenHandler.setSecurityContextHolderStrategy(this.getSecurityContextHolderStrategy());
401+
}
402+
if (jwtDecoderFactory != null) {
403+
refreshOidcIdTokenHandler.setJwtDecoderFactory(jwtDecoderFactory);
404+
}
400405
registerDelegateApplicationListener(refreshOidcIdTokenHandler);
401406
}
402407
else {

oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/authentication/OidcAuthorizationCodeAuthenticationProvider.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,7 @@ public boolean supports(Class<?> authentication) {
232232
return OAuth2LoginAuthenticationToken.class.isAssignableFrom(authentication);
233233
}
234234

235-
protected OidcIdToken createOidcToken(ClientRegistration clientRegistration,
235+
private OidcIdToken createOidcToken(ClientRegistration clientRegistration,
236236
OAuth2AccessTokenResponse accessTokenResponse) {
237237
JwtDecoder jwtDecoder = this.jwtDecoderFactory.createDecoder(clientRegistration);
238238
Jwt jwt = getJwt(accessTokenResponse, jwtDecoder);

oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/authentication/RefreshOidcIdTokenHandler.java

Lines changed: 63 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,46 +16,98 @@
1616

1717
package org.springframework.security.oauth2.client.oidc.authentication;
1818

19+
import java.util.Map;
20+
1921
import org.springframework.context.ApplicationListener;
2022
import org.springframework.security.core.Authentication;
23+
import org.springframework.security.core.context.SecurityContext;
2124
import org.springframework.security.core.context.SecurityContextHolder;
25+
import org.springframework.security.core.context.SecurityContextHolderStrategy;
2226
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
2327
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
2428
import org.springframework.security.oauth2.client.event.OAuth2TokenRefreshedEvent;
29+
import org.springframework.security.oauth2.client.registration.ClientRegistration;
30+
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
31+
import org.springframework.security.oauth2.core.OAuth2Error;
2532
import org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;
2633
import org.springframework.security.oauth2.core.oidc.OidcIdToken;
2734
import org.springframework.security.oauth2.core.oidc.StandardClaimNames;
35+
import org.springframework.security.oauth2.core.oidc.endpoint.OidcParameterNames;
2836
import org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser;
2937
import org.springframework.security.oauth2.core.oidc.user.OidcUser;
38+
import org.springframework.security.oauth2.jwt.Jwt;
39+
import org.springframework.security.oauth2.jwt.JwtDecoder;
40+
import org.springframework.security.oauth2.jwt.JwtDecoderFactory;
41+
import org.springframework.security.oauth2.jwt.JwtException;
42+
import org.springframework.util.Assert;
3043

3144
/**
3245
* An {@link ApplicationListener} that listens for {@link OAuth2TokenRefreshedEvent}s
3346
*/
3447
public class RefreshOidcIdTokenHandler implements ApplicationListener<OAuth2TokenRefreshedEvent> {
3548

36-
private final OidcAuthorizationCodeAuthenticationProvider oidcAuthorizationCodeAuthenticationProvider;
49+
private static final String INVALID_ID_TOKEN_ERROR_CODE = "invalid_id_token";
3750

38-
public RefreshOidcIdTokenHandler(
39-
OidcAuthorizationCodeAuthenticationProvider oidcAuthorizationCodeAuthenticationProvider) {
40-
this.oidcAuthorizationCodeAuthenticationProvider = oidcAuthorizationCodeAuthenticationProvider;
41-
}
51+
private SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder
52+
.getContextHolderStrategy();
53+
54+
private JwtDecoderFactory<ClientRegistration> jwtDecoderFactory = new OidcIdTokenDecoderFactory();
4255

4356
@Override
4457
public void onApplicationEvent(OAuth2TokenRefreshedEvent event) {
4558
OAuth2AuthorizedClient authorizedClient = event.getAuthorizedClient();
4659
OAuth2AccessTokenResponse accessTokenResponse = event.getAccessTokenResponse();
47-
OidcIdToken refreshedOidcToken = this.oidcAuthorizationCodeAuthenticationProvider
48-
.createOidcToken(authorizedClient.getClientRegistration(), accessTokenResponse);
49-
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
60+
ClientRegistration clientRegistration = authorizedClient.getClientRegistration();
61+
OidcIdToken refreshedOidcToken = createOidcToken(clientRegistration, accessTokenResponse);
62+
Authentication authentication = this.securityContextHolderStrategy.getContext().getAuthentication();
5063
if (authentication instanceof OAuth2AuthenticationToken oauth2AuthenticationToken) {
5164
if (authentication.getPrincipal() instanceof DefaultOidcUser defaultOidcUser) {
5265
OidcUser oidcUser = new DefaultOidcUser(defaultOidcUser.getAuthorities(), refreshedOidcToken,
5366
defaultOidcUser.getUserInfo(), StandardClaimNames.SUB);
54-
SecurityContextHolder.getContext()
55-
.setAuthentication(new OAuth2AuthenticationToken(oidcUser, oidcUser.getAuthorities(),
56-
oauth2AuthenticationToken.getAuthorizedClientRegistrationId()));
67+
SecurityContext context = this.securityContextHolderStrategy.createEmptyContext();
68+
context.setAuthentication(new OAuth2AuthenticationToken(oidcUser, oidcUser.getAuthorities(),
69+
oauth2AuthenticationToken.getAuthorizedClientRegistrationId()));
70+
this.securityContextHolderStrategy.setContext(context);
5771
}
5872
}
5973
}
6074

75+
/**
76+
* Sets the {@link SecurityContextHolderStrategy} to use. The default action is to use
77+
* the {@link SecurityContextHolderStrategy} stored in {@link SecurityContextHolder}.
78+
*/
79+
public void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {
80+
this.securityContextHolderStrategy = securityContextHolderStrategy;
81+
}
82+
83+
/**
84+
* Sets the {@link JwtDecoderFactory} used for {@link OidcIdToken} signature
85+
* verification. The factory returns a {@link JwtDecoder} associated to the provided
86+
* {@link ClientRegistration}.
87+
* @param jwtDecoderFactory the {@link JwtDecoderFactory} used for {@link OidcIdToken}
88+
* signature verification
89+
*/
90+
public final void setJwtDecoderFactory(JwtDecoderFactory<ClientRegistration> jwtDecoderFactory) {
91+
Assert.notNull(jwtDecoderFactory, "jwtDecoderFactory cannot be null");
92+
this.jwtDecoderFactory = jwtDecoderFactory;
93+
}
94+
95+
private OidcIdToken createOidcToken(ClientRegistration clientRegistration,
96+
OAuth2AccessTokenResponse accessTokenResponse) {
97+
JwtDecoder jwtDecoder = this.jwtDecoderFactory.createDecoder(clientRegistration);
98+
Jwt jwt = getJwt(accessTokenResponse, jwtDecoder);
99+
return new OidcIdToken(jwt.getTokenValue(), jwt.getIssuedAt(), jwt.getExpiresAt(), jwt.getClaims());
100+
}
101+
102+
private Jwt getJwt(OAuth2AccessTokenResponse accessTokenResponse, JwtDecoder jwtDecoder) {
103+
try {
104+
Map<String, Object> parameters = accessTokenResponse.getAdditionalParameters();
105+
return jwtDecoder.decode((String) parameters.get(OidcParameterNames.ID_TOKEN));
106+
}
107+
catch (JwtException ex) {
108+
OAuth2Error invalidIdTokenError = new OAuth2Error(INVALID_ID_TOKEN_ERROR_CODE, ex.getMessage(), null);
109+
throw new OAuth2AuthenticationException(invalidIdTokenError, invalidIdTokenError.toString(), ex);
110+
}
111+
}
112+
61113
}

0 commit comments

Comments
 (0)