Skip to content

Commit b6f3b5c

Browse files
cbilodeauupgradejgrandja
authored andcommitted
Fix generating ID token with null sid when refresh_token grant
Closes gh-1283
1 parent 5c2a2c0 commit b6f3b5c

File tree

2 files changed

+84
-2
lines changed

2 files changed

+84
-2
lines changed

oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/token/JwtGenerator.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,9 @@ public Jwt generate(OAuth2TokenContext context) {
134134
}
135135
} else if (AuthorizationGrantType.REFRESH_TOKEN.equals(context.getAuthorizationGrantType())) {
136136
OidcIdToken currentIdToken = context.getAuthorization().getToken(OidcIdToken.class).getToken();
137-
claimsBuilder.claim("sid", currentIdToken.getClaim("sid"));
137+
if (currentIdToken.hasClaim("sid")) {
138+
claimsBuilder.claim("sid", currentIdToken.getClaim("sid"));
139+
}
138140
claimsBuilder.claim(IdTokenClaimNames.AUTH_TIME, currentIdToken.<Date>getClaim(IdTokenClaimNames.AUTH_TIME));
139141
}
140142
}

oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2RefreshTokenAuthenticationProviderTests.java

Lines changed: 81 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
import org.springframework.security.oauth2.jwt.JoseHeaderNames;
4848
import org.springframework.security.oauth2.jwt.Jwt;
4949
import org.springframework.security.oauth2.jwt.JwtEncoder;
50+
import org.springframework.security.oauth2.jwt.JwtEncoderParameters;
5051
import org.springframework.security.oauth2.server.authorization.OAuth2Authorization;
5152
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;
5253
import org.springframework.security.oauth2.server.authorization.OAuth2TokenType;
@@ -250,7 +251,86 @@ public void authenticateWhenValidRefreshTokenThenReturnIdToken() {
250251
assertThat(idTokenContext.getJwsHeader()).isNotNull();
251252
assertThat(idTokenContext.getClaims()).isNotNull();
252253

253-
verify(this.jwtEncoder, times(2)).encode(any()); // Access token and ID Token
254+
ArgumentCaptor<JwtEncoderParameters> jwtEncoderParametersArgumentCaptor = ArgumentCaptor.forClass(JwtEncoderParameters.class);
255+
verify(this.jwtEncoder, times(2)).encode(jwtEncoderParametersArgumentCaptor.capture()); // Access token and ID Token
256+
JwtEncoderParameters jwtEncoderParameters = jwtEncoderParametersArgumentCaptor.getValue();
257+
assertThat(jwtEncoderParameters.getClaims().getClaims().get("sid")).isNotNull();
258+
259+
ArgumentCaptor<OAuth2Authorization> authorizationCaptor = ArgumentCaptor.forClass(OAuth2Authorization.class);
260+
verify(this.authorizationService).save(authorizationCaptor.capture());
261+
OAuth2Authorization updatedAuthorization = authorizationCaptor.getValue();
262+
263+
assertThat(accessTokenAuthentication.getRegisteredClient().getId()).isEqualTo(updatedAuthorization.getRegisteredClientId());
264+
assertThat(accessTokenAuthentication.getPrincipal()).isEqualTo(clientPrincipal);
265+
assertThat(accessTokenAuthentication.getAccessToken()).isEqualTo(updatedAuthorization.getAccessToken().getToken());
266+
assertThat(updatedAuthorization.getAccessToken()).isNotEqualTo(authorization.getAccessToken());
267+
OAuth2Authorization.Token<OidcIdToken> idToken = updatedAuthorization.getToken(OidcIdToken.class);
268+
assertThat(idToken).isNotNull();
269+
assertThat(accessTokenAuthentication.getAdditionalParameters())
270+
.containsExactly(entry(OidcParameterNames.ID_TOKEN, idToken.getToken().getTokenValue()));
271+
assertThat(accessTokenAuthentication.getRefreshToken()).isEqualTo(updatedAuthorization.getRefreshToken().getToken());
272+
// By default, refresh token is reused
273+
assertThat(updatedAuthorization.getRefreshToken()).isEqualTo(authorization.getRefreshToken());
274+
}
275+
276+
@Test
277+
public void authenticateWhenValidRefreshTokenThenReturnIdTokenWithoutSid() {
278+
RegisteredClient registeredClient = TestRegisteredClients.registeredClient().scope(OidcScopes.OPENID).build();
279+
OidcIdToken authorizedIdToken = OidcIdToken.withTokenValue("id-token")
280+
.issuer("https://provider.com")
281+
.subject("subject")
282+
.issuedAt(Instant.now())
283+
.expiresAt(Instant.now().plusSeconds(60))
284+
.claim(IdTokenClaimNames.AUTH_TIME, Date.from(Instant.now()))
285+
.build();
286+
OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient).token(authorizedIdToken).build();
287+
when(this.authorizationService.findByToken(
288+
eq(authorization.getRefreshToken().getToken().getTokenValue()),
289+
eq(OAuth2TokenType.REFRESH_TOKEN)))
290+
.thenReturn(authorization);
291+
292+
OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(
293+
registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret());
294+
OAuth2RefreshTokenAuthenticationToken authentication = new OAuth2RefreshTokenAuthenticationToken(
295+
authorization.getRefreshToken().getToken().getTokenValue(), clientPrincipal, null, null);
296+
297+
OAuth2AccessTokenAuthenticationToken accessTokenAuthentication =
298+
(OAuth2AccessTokenAuthenticationToken) this.authenticationProvider.authenticate(authentication);
299+
300+
ArgumentCaptor<JwtEncodingContext> jwtEncodingContextCaptor = ArgumentCaptor.forClass(JwtEncodingContext.class);
301+
verify(this.jwtCustomizer, times(2)).customize(jwtEncodingContextCaptor.capture());
302+
// Access Token context
303+
JwtEncodingContext accessTokenContext = jwtEncodingContextCaptor.getAllValues().get(0);
304+
assertThat(accessTokenContext.getRegisteredClient()).isEqualTo(registeredClient);
305+
assertThat(accessTokenContext.<Authentication>getPrincipal()).isEqualTo(authorization.getAttribute(Principal.class.getName()));
306+
assertThat(accessTokenContext.getAuthorization()).isEqualTo(authorization);
307+
assertThat(accessTokenContext.getAuthorizedScopes()).isEqualTo(authorization.getAuthorizedScopes());
308+
assertThat(accessTokenContext.getTokenType()).isEqualTo(OAuth2TokenType.ACCESS_TOKEN);
309+
assertThat(accessTokenContext.getAuthorizationGrantType()).isEqualTo(AuthorizationGrantType.REFRESH_TOKEN);
310+
assertThat(accessTokenContext.<OAuth2AuthorizationGrantAuthenticationToken>getAuthorizationGrant()).isEqualTo(authentication);
311+
assertThat(accessTokenContext.getJwsHeader()).isNotNull();
312+
assertThat(accessTokenContext.getClaims()).isNotNull();
313+
Map<String, Object> claims = new HashMap<>();
314+
accessTokenContext.getClaims().claims(claims::putAll);
315+
assertThat(claims).flatExtracting(OAuth2ParameterNames.SCOPE)
316+
.containsExactlyInAnyOrder(OidcScopes.OPENID, "scope1");
317+
// ID Token context
318+
JwtEncodingContext idTokenContext = jwtEncodingContextCaptor.getAllValues().get(1);
319+
assertThat(idTokenContext.getRegisteredClient()).isEqualTo(registeredClient);
320+
assertThat(idTokenContext.<Authentication>getPrincipal()).isEqualTo(authorization.getAttribute(Principal.class.getName()));
321+
assertThat(idTokenContext.getAuthorization()).isNotEqualTo(authorization);
322+
assertThat(idTokenContext.getAuthorization().getAccessToken()).isNotEqualTo(authorization.getAccessToken());
323+
assertThat(idTokenContext.getAuthorizedScopes()).isEqualTo(authorization.getAuthorizedScopes());
324+
assertThat(idTokenContext.getTokenType().getValue()).isEqualTo(OidcParameterNames.ID_TOKEN);
325+
assertThat(idTokenContext.getAuthorizationGrantType()).isEqualTo(AuthorizationGrantType.REFRESH_TOKEN);
326+
assertThat(idTokenContext.<OAuth2AuthorizationGrantAuthenticationToken>getAuthorizationGrant()).isEqualTo(authentication);
327+
assertThat(idTokenContext.getJwsHeader()).isNotNull();
328+
assertThat(idTokenContext.getClaims()).isNotNull();
329+
330+
ArgumentCaptor<JwtEncoderParameters> jwtEncoderParametersArgumentCaptor = ArgumentCaptor.forClass(JwtEncoderParameters.class);
331+
verify(this.jwtEncoder, times(2)).encode(jwtEncoderParametersArgumentCaptor.capture()); // Access token and ID Token
332+
JwtEncoderParameters jwtEncoderParameters = jwtEncoderParametersArgumentCaptor.getValue();
333+
assertThat(jwtEncoderParameters.getClaims().getClaims().get("sid")).isNull();
254334

255335
ArgumentCaptor<OAuth2Authorization> authorizationCaptor = ArgumentCaptor.forClass(OAuth2Authorization.class);
256336
verify(this.authorizationService).save(authorizationCaptor.capture());

0 commit comments

Comments
 (0)