Skip to content

Commit 888715b

Browse files
tinolazregSteve Riesenberg
authored andcommitted
Add tests for unknown KID error
Issue gh-11621
1 parent 13feb87 commit 888715b

File tree

1 file changed

+80
-0
lines changed

1 file changed

+80
-0
lines changed

oauth2/oauth2-jose/src/test/java/org/springframework/security/oauth2/jwt/NimbusJwtDecoderTests.java

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,16 @@
3636

3737
import javax.crypto.SecretKey;
3838

39+
import com.nimbusds.jose.JOSEException;
3940
import com.nimbusds.jose.JOSEObjectType;
4041
import com.nimbusds.jose.JWSAlgorithm;
4142
import com.nimbusds.jose.JWSHeader;
4243
import com.nimbusds.jose.JWSSigner;
4344
import com.nimbusds.jose.crypto.MACSigner;
4445
import com.nimbusds.jose.crypto.RSASSASigner;
46+
import com.nimbusds.jose.jwk.JWKSet;
47+
import com.nimbusds.jose.jwk.RSAKey;
48+
import com.nimbusds.jose.jwk.gen.RSAKeyGenerator;
4549
import com.nimbusds.jose.jwk.source.JWKSource;
4650
import com.nimbusds.jose.proc.BadJOSEException;
4751
import com.nimbusds.jose.proc.DefaultJOSEObjectTypeVerifier;
@@ -82,6 +86,7 @@
8286
import static org.mockito.ArgumentMatchers.eq;
8387
import static org.mockito.BDDMockito.given;
8488
import static org.mockito.Mockito.mock;
89+
import static org.mockito.Mockito.times;
8590
import static org.mockito.Mockito.verify;
8691
import static org.mockito.Mockito.verifyNoInteractions;
8792
import static org.mockito.Mockito.verifyNoMoreInteractions;
@@ -660,6 +665,81 @@ public void decodeWhenCacheThenRetrieveFromCache() {
660665
verifyNoInteractions(restOperations);
661666
}
662667

668+
@Test
669+
public void decodeWhenCacheAndUnknownKidShouldTriggerFetchOfJwkSet() throws JOSEException {
670+
RestOperations restOperations = mock(RestOperations.class);
671+
672+
Cache cache = mock(Cache.class);
673+
given(cache.get(eq(JWK_SET_URI), any(Callable.class))).willReturn(JWK_SET);
674+
675+
RSAKey rsaJWK = new RSAKeyGenerator(2048)
676+
.keyID("new_kid")
677+
.generate();
678+
String jwkSetWithNewKid = new JWKSet(rsaJWK).toPublicJWKSet().toString();
679+
given(restOperations.exchange(any(RequestEntity.class), eq(String.class)))
680+
.willReturn(new ResponseEntity<>(jwkSetWithNewKid, HttpStatus.OK));
681+
682+
// @formatter:off
683+
NimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withJwkSetUri(JWK_SET_URI)
684+
.cache(cache)
685+
.restOperations(restOperations)
686+
.build();
687+
// @formatter:on
688+
689+
// Decode JWT with new KID
690+
JWSSigner signer = new RSASSASigner(rsaJWK);
691+
JWTClaimsSet claimsSet = new JWTClaimsSet.Builder()
692+
.expirationTime(Date.from(Instant.now().plusSeconds(60)))
693+
.build();
694+
SignedJWT signedJWT = new SignedJWT(new JWSHeader.Builder(JWSAlgorithm.RS256).keyID(rsaJWK.getKeyID()).build(), claimsSet);
695+
signedJWT.sign(signer);
696+
String token = signedJWT.serialize();
697+
698+
jwtDecoder.decode(token);
699+
700+
ArgumentCaptor<RequestEntity> requestEntityCaptor = ArgumentCaptor.forClass(RequestEntity.class);
701+
verify(restOperations).exchange(requestEntityCaptor.capture(), eq(String.class));
702+
verifyNoMoreInteractions(restOperations);
703+
assertThat(requestEntityCaptor.getValue().getHeaders().getAccept()).contains(MediaType.APPLICATION_JSON, APPLICATION_JWK_SET_JSON);
704+
}
705+
706+
@Test
707+
public void decodeWithoutCacheSpecifiedAndUnknownKidShouldTriggerFetchOfJwkSet() throws JOSEException {
708+
RestOperations restOperations = mock(RestOperations.class);
709+
710+
RSAKey rsaJWK = new RSAKeyGenerator(2048)
711+
.keyID("new_kid")
712+
.generate();
713+
String jwkSetWithNewKid = new JWKSet(rsaJWK).toPublicJWKSet().toString();
714+
given(restOperations.exchange(any(RequestEntity.class), eq(String.class)))
715+
.willReturn(new ResponseEntity<>(JWK_SET, HttpStatus.OK), new ResponseEntity<>(jwkSetWithNewKid, HttpStatus.OK));
716+
717+
// @formatter:off
718+
NimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withJwkSetUri(JWK_SET_URI)
719+
.restOperations(restOperations)
720+
.build();
721+
// @formatter:on
722+
jwtDecoder.decode(SIGNED_JWT);
723+
724+
// Decode JWT with new KID
725+
JWSSigner signer = new RSASSASigner(rsaJWK);
726+
JWTClaimsSet claimsSet = new JWTClaimsSet.Builder()
727+
.expirationTime(Date.from(Instant.now().plusSeconds(60)))
728+
.build();
729+
SignedJWT signedJWT = new SignedJWT(new JWSHeader.Builder(JWSAlgorithm.RS256).keyID(rsaJWK.getKeyID()).build(), claimsSet);
730+
signedJWT.sign(signer);
731+
String token = signedJWT.serialize();
732+
733+
jwtDecoder.decode(token);
734+
735+
ArgumentCaptor<RequestEntity> requestEntityCaptor = ArgumentCaptor.forClass(RequestEntity.class);
736+
verify(restOperations, times(2)).exchange(requestEntityCaptor.capture(), eq(String.class));
737+
verifyNoMoreInteractions(restOperations);
738+
List<RequestEntity> requestEntities = requestEntityCaptor.getAllValues();
739+
assertThat(requestEntities.get(0).getHeaders().getAccept()).contains(MediaType.APPLICATION_JSON, APPLICATION_JWK_SET_JSON);
740+
assertThat(requestEntities.get(1).getHeaders().getAccept()).contains(MediaType.APPLICATION_JSON, APPLICATION_JWK_SET_JSON);
741+
}
742+
663743
@Test
664744
public void decodeWhenCacheIsConfiguredAndValueLoaderErrorsThenThrowsJwtException() {
665745
Cache cache = new ConcurrentMapCache("test-jwk-set-cache");

0 commit comments

Comments
 (0)