private NimbusJwtDecoder makeDelegate() { NimbusJwtDecoder delegate = new NimbusJwtDecoder(this.jwtProcessorBuilder.build()); delegate.setClaimSetConverter(this.claimSetConverter); delegate.setJwtValidator(this.jwtValidator); return delegate; }
@Override public Jwt decode(String token) throws JwtException { return this.delegate.decode(token); }
/** * Decode and validate the JWT from its compact claims representation format * * @param token the JWT value * @return a validated {@link Jwt} * @throws JwtException */ @Override public Jwt decode(String token) throws JwtException { JWT jwt = parse(token); if (jwt instanceof SignedJWT) { Jwt createdJwt = createJwt(token, jwt); return validateJwt(createdJwt); } throw new JwtException("Unsupported algorithm of " + jwt.getHeader().getAlgorithm()); }
/** * Creates a {@link JwtDecoder} using the provided * <a href="http://openid.net/specs/openid-connect-core-1_0.html#IssuerIdentifier">Issuer</a> by making an * <a href="https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfigurationRequest">OpenID Provider * Configuration Request</a> and using the values in the * <a href="https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfigurationResponse">OpenID * Provider Configuration Response</a> to initialize the {@link JwtDecoder}. * * @param oidcIssuerLocation the <a href="http://openid.net/specs/openid-connect-core-1_0.html#IssuerIdentifier">Issuer</a> * @return a {@link JwtDecoder} that was initialized by the OpenID Provider Configuration. */ public static JwtDecoder fromOidcIssuerLocation(String oidcIssuerLocation) { Map<String, Object> openidConfiguration = getOpenidConfiguration(oidcIssuerLocation); String metadataIssuer = "(unavailable)"; if (openidConfiguration.containsKey("issuer")) { metadataIssuer = openidConfiguration.get("issuer").toString(); } if (!oidcIssuerLocation.equals(metadataIssuer)) { throw new IllegalStateException("The Issuer \"" + metadataIssuer + "\" provided in the OpenID Configuration " + "did not match the requested issuer \"" + oidcIssuerLocation + "\""); } OAuth2TokenValidator<Jwt> jwtValidator = JwtValidators.createDefaultWithIssuer(oidcIssuerLocation); NimbusJwtDecoder jwtDecoder = new NimbusJwtDecoder( withJwkSetUri(openidConfiguration.get("jwks_uri").toString()).build()); jwtDecoder.setJwtValidator(jwtValidator); return jwtDecoder; }
@Test public void decodeWhenJwkResponseIsMalformedThenReturnsStockException() { NimbusJwtDecoder jwtDecoder = new NimbusJwtDecoder(withSigning(MALFORMED_JWK_SET)); assertThatCode(() -> jwtDecoder.decode(SIGNED_JWT)) .isInstanceOf(JwtException.class) .hasMessage("An error occurred while attempting to decode the Jwt: Malformed Jwk set"); }
/** * Use this {@link Jwt} Validator * * @param jwtValidator - the Jwt Validator to use */ public void setJwtValidator(OAuth2TokenValidator<Jwt> jwtValidator) { Assert.notNull(jwtValidator, "jwtValidator cannot be null"); this.jwtValidator = jwtValidator; this.delegate.setJwtValidator(jwtValidator); }
@Test public void decodeWhenJwtFailsValidationThenReturnsCorrespondingErrorMessage() { OAuth2Error failure = new OAuth2Error("mock-error", "mock-description", "mock-uri"); OAuth2TokenValidator<Jwt> jwtValidator = mock(OAuth2TokenValidator.class); when(jwtValidator.validate(any(Jwt.class))) .thenReturn(OAuth2TokenValidatorResult.failure(failure)); this.jwtDecoder.setJwtValidator(jwtValidator); assertThatCode(() -> this.jwtDecoder.decode(SIGNED_JWT)) .isInstanceOf(JwtValidationException.class) .hasMessageContaining("mock-description"); }
@Bean JwtDecoder decoder() throws Exception { RSAPublicKey publicKey = (RSAPublicKey) KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(this.spec)); return new NimbusJwtDecoder(withPublicKey(publicKey).build()); } }
@Test public void decodeWhenUsingSignedJwtThenReturnsClaimsGivenByClaimSetConverter() { Converter<Map<String, Object>, Map<String, Object>> claimSetConverter = mock(Converter.class); when(claimSetConverter.convert(any(Map.class))) .thenReturn(Collections.singletonMap("custom", "value")); this.jwtDecoder.setClaimSetConverter(claimSetConverter); Jwt jwt = this.jwtDecoder.decode(SIGNED_JWT); assertThat(jwt.getClaims().size()).isEqualTo(1); assertThat(jwt.getClaims().get("custom")).isEqualTo("value"); }
/** * Use the following {@link Converter} for manipulating the JWT's claim set * * @param claimSetConverter the {@link Converter} to use */ public final void setClaimSetConverter(Converter<Map<String, Object>, Map<String, Object>> claimSetConverter) { Assert.notNull(claimSetConverter, "claimSetConverter cannot be null"); this.claimSetConverter = claimSetConverter; this.delegate.setClaimSetConverter(claimSetConverter); }
@Override public JwtDecoder createDecoder(ClientRegistration clientRegistration) { Assert.notNull(clientRegistration, "clientRegistration cannot be null"); return this.jwtDecoders.computeIfAbsent(clientRegistration.getRegistrationId(), key -> { if (!StringUtils.hasText(clientRegistration.getProviderDetails().getJwkSetUri())) { OAuth2Error oauth2Error = new OAuth2Error( MISSING_SIGNATURE_VERIFIER_ERROR_CODE, "Failed to find a Signature Verifier for Client Registration: '" + clientRegistration.getRegistrationId() + "'. Check to ensure you have configured the JwkSet URI.", null ); throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString()); } String jwkSetUri = clientRegistration.getProviderDetails().getJwkSetUri(); NimbusJwtDecoder jwtDecoder = new NimbusJwtDecoder(withJwkSetUri(jwkSetUri).build()); OAuth2TokenValidator<Jwt> jwtValidator = this.jwtValidatorFactory.apply(clientRegistration); jwtDecoder.setJwtValidator(jwtValidator); return jwtDecoder; }); }
@Test public void decodeWhenSignedThenOk() { NimbusJwtDecoder jwtDecoder = new NimbusJwtDecoder(withSigning(JWK_SET)); Jwt jwt = jwtDecoder.decode(SIGNED_JWT); assertThat(jwt.containsClaim(JwtClaimNames.EXP)).isNotNull(); }
@Test public void setJwtValidatorWhenNullThenThrowIllegalArgumentException() { Assertions.assertThatThrownBy(() -> this.jwtDecoder.setJwtValidator(null)) .isInstanceOf(IllegalArgumentException.class); }
@Test public void decodeWhenJwtValidationHasTwoErrorsThenJwtExceptionMessageShowsFirstError() { OAuth2Error firstFailure = new OAuth2Error("mock-error", "mock-description", "mock-uri"); OAuth2Error secondFailure = new OAuth2Error("another-error", "another-description", "another-uri"); OAuth2TokenValidatorResult result = OAuth2TokenValidatorResult.failure(firstFailure, secondFailure); OAuth2TokenValidator<Jwt> jwtValidator = mock(OAuth2TokenValidator.class); when(jwtValidator.validate(any(Jwt.class))).thenReturn(result); this.jwtDecoder.setJwtValidator(jwtValidator); assertThatCode(() -> this.jwtDecoder.decode(SIGNED_JWT)) .isInstanceOf(JwtValidationException.class) .hasMessageContaining("mock-description") .hasFieldOrPropertyWithValue("errors", Arrays.asList(firstFailure, secondFailure)); }
@Test public void constructorWhenJwtProcessorIsNullThenThrowIllegalArgumentException() { assertThatThrownBy(() -> new NimbusJwtDecoder(null)) .isInstanceOf(IllegalArgumentException.class); }
@Test public void setClaimSetConverterWhenIsNullThenThrowsIllegalArgumentException() { assertThatCode(() -> this.jwtDecoder.setClaimSetConverter(null)) .isInstanceOf(IllegalArgumentException.class); }
@Test public void decodeWhenJwkEndpointIsUnresponsiveThenReturnsJwtException() throws Exception { try ( MockWebServer server = new MockWebServer() ) { String jwkSetUri = server.url("/.well-known/jwks.json").toString(); NimbusJwtDecoder jwtDecoder = new NimbusJwtDecoder( withJwkSetUri(jwkSetUri).build()); server.shutdown(); assertThatCode(() -> jwtDecoder.decode(SIGNED_JWT)) .isInstanceOf(JwtException.class) .hasMessageContaining("An error occurred while attempting to decode the Jwt"); } }
@Override protected void configure(HttpSecurity http) throws Exception { this.jwtDecoder.setJwtValidator(this.jwtValidator); // @formatter:off http .oauth2ResourceServer() .jwt(); // @formatter:on }
@Bean NimbusJwtDecoder jwtDecoder() { JWTProcessor<SecurityContext> jwtProcessor = JwtProcessors.withJwkSetUri("https://example.org/.well-known/jwks.json") .restOperations(this.rest).build(); return new NimbusJwtDecoder(jwtProcessor); } }
@Test public void decodeWhenJwtIsMalformedThenReturnsStockException() { assertThatCode(() -> this.jwtDecoder.decode(MALFORMED_JWT)) .isInstanceOf(JwtException.class) .hasMessage("An error occurred while attempting to decode the Jwt: Malformed payload"); }