Support symmetric key for JwtDecoder

Fixes gh-5465
This commit is contained in:
Joe Grandja
2019-04-11 05:05:16 -04:00
parent fc6b66fdb3
commit bed3371b80
15 changed files with 1024 additions and 161 deletions

View File

@@ -15,15 +15,14 @@
*/
package org.springframework.security.oauth2.client.oidc.authentication;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import org.springframework.security.oauth2.client.registration.ClientRegistration;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.core.OAuth2Error;
import org.springframework.security.oauth2.core.OAuth2TokenValidator;
import org.springframework.security.oauth2.core.oidc.OidcIdToken;
import org.springframework.security.oauth2.jose.jws.JwsAlgorithm;
import org.springframework.security.oauth2.jose.jws.MacAlgorithm;
import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.oauth2.jwt.JwtDecoder;
import org.springframework.security.oauth2.jwt.JwtDecoderFactory;
@@ -31,7 +30,15 @@ import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import static org.springframework.security.oauth2.jwt.NimbusJwtDecoder.withJwkSetUri;
import static org.springframework.security.oauth2.jwt.NimbusJwtDecoder.withSecretKey;
/**
* A {@link JwtDecoderFactory factory} that provides a {@link JwtDecoder}
@@ -47,14 +54,45 @@ import static org.springframework.security.oauth2.jwt.NimbusJwtDecoder.withJwkSe
*/
public final class OidcIdTokenDecoderFactory implements JwtDecoderFactory<ClientRegistration> {
private static final String MISSING_SIGNATURE_VERIFIER_ERROR_CODE = "missing_signature_verifier";
private static Map<JwsAlgorithm, String> jcaAlgorithmMappings = new HashMap<JwsAlgorithm, String>() {
{
put(MacAlgorithm.HS256, "HmacSHA256");
put(MacAlgorithm.HS384, "HmacSHA384");
put(MacAlgorithm.HS512, "HmacSHA512");
}
};
private final Map<String, JwtDecoder> jwtDecoders = new ConcurrentHashMap<>();
private Function<ClientRegistration, OAuth2TokenValidator<Jwt>> jwtValidatorFactory = OidcIdTokenValidator::new;
private Function<ClientRegistration, JwsAlgorithm> jwsAlgorithmResolver = clientRegistration -> SignatureAlgorithm.RS256;
@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())) {
NimbusJwtDecoder jwtDecoder = buildDecoder(clientRegistration);
OAuth2TokenValidator<Jwt> jwtValidator = this.jwtValidatorFactory.apply(clientRegistration);
jwtDecoder.setJwtValidator(jwtValidator);
return jwtDecoder;
});
}
private NimbusJwtDecoder buildDecoder(ClientRegistration clientRegistration) {
JwsAlgorithm jwsAlgorithm = this.jwsAlgorithmResolver.apply(clientRegistration);
if (jwsAlgorithm != null && SignatureAlgorithm.class.isAssignableFrom(jwsAlgorithm.getClass())) {
// https://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation
//
// 6. If the ID Token is received via direct communication between the Client
// and the Token Endpoint (which it is in this flow),
// the TLS server validation MAY be used to validate the issuer in place of checking the token signature.
// The Client MUST validate the signature of all other ID Tokens according to JWS [JWS]
// using the algorithm specified in the JWT alg Header Parameter.
// The Client MUST use the keys provided by the Issuer.
//
// 7. The alg value SHOULD be the default of RS256 or the algorithm sent by the Client
// in the id_token_signed_response_alg parameter during Registration.
String jwkSetUri = clientRegistration.getProviderDetails().getJwkSetUri();
if (!StringUtils.hasText(jwkSetUri)) {
OAuth2Error oauth2Error = new OAuth2Error(
MISSING_SIGNATURE_VERIFIER_ERROR_CODE,
"Failed to find a Signature Verifier for Client Registration: '" +
@@ -64,12 +102,42 @@ public final class OidcIdTokenDecoderFactory implements JwtDecoderFactory<Client
);
throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());
}
String jwkSetUri = clientRegistration.getProviderDetails().getJwkSetUri();
NimbusJwtDecoder jwtDecoder = withJwkSetUri(jwkSetUri).build();
OAuth2TokenValidator<Jwt> jwtValidator = this.jwtValidatorFactory.apply(clientRegistration);
jwtDecoder.setJwtValidator(jwtValidator);
return jwtDecoder;
});
return withJwkSetUri(jwkSetUri).jwsAlgorithm(jwsAlgorithm).build();
} else if (jwsAlgorithm != null && MacAlgorithm.class.isAssignableFrom(jwsAlgorithm.getClass())) {
// https://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation
//
// 8. If the JWT alg Header Parameter uses a MAC based algorithm such as HS256, HS384, or HS512,
// the octets of the UTF-8 representation of the client_secret
// corresponding to the client_id contained in the aud (audience) Claim
// are used as the key to validate the signature.
// For MAC based algorithms, the behavior is unspecified if the aud is multi-valued or
// if an azp value is present that is different than the aud value.
String clientSecret = clientRegistration.getClientSecret();
if (!StringUtils.hasText(clientSecret)) {
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 client secret.",
null
);
throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());
}
SecretKeySpec secretKeySpec = new SecretKeySpec(
clientSecret.getBytes(StandardCharsets.UTF_8), jcaAlgorithmMappings.get(jwsAlgorithm));
return withSecretKey(secretKeySpec).macAlgorithm((MacAlgorithm) jwsAlgorithm).build();
}
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 a valid JWS Algorithm: '" +
jwsAlgorithm + "'",
null
);
throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());
}
/**
@@ -82,4 +150,17 @@ public final class OidcIdTokenDecoderFactory implements JwtDecoderFactory<Client
Assert.notNull(jwtValidatorFactory, "jwtValidatorFactory cannot be null");
this.jwtValidatorFactory = jwtValidatorFactory;
}
/**
* Sets the resolver that provides the expected {@link JwsAlgorithm JWS algorithm}
* used for the signature or MAC on the {@link OidcIdToken ID Token}.
* The default resolves to {@link SignatureAlgorithm#RS256 RS256} for all {@link ClientRegistration clients}.
*
* @param jwsAlgorithmResolver the resolver that provides the expected {@link JwsAlgorithm JWS algorithm}
* for a specific {@link ClientRegistration client}
*/
public final void setJwsAlgorithmResolver(Function<ClientRegistration, JwsAlgorithm> jwsAlgorithmResolver) {
Assert.notNull(jwsAlgorithmResolver, "jwsAlgorithmResolver cannot be null");
this.jwsAlgorithmResolver = jwsAlgorithmResolver;
}
}

View File

@@ -20,6 +20,9 @@ import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.core.OAuth2Error;
import org.springframework.security.oauth2.core.OAuth2TokenValidator;
import org.springframework.security.oauth2.core.oidc.OidcIdToken;
import org.springframework.security.oauth2.jose.jws.JwsAlgorithm;
import org.springframework.security.oauth2.jose.jws.MacAlgorithm;
import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.oauth2.jwt.NimbusReactiveJwtDecoder;
import org.springframework.security.oauth2.jwt.ReactiveJwtDecoder;
@@ -27,10 +30,16 @@ import org.springframework.security.oauth2.jwt.ReactiveJwtDecoderFactory;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import static org.springframework.security.oauth2.jwt.NimbusReactiveJwtDecoder.withJwkSetUri;
import static org.springframework.security.oauth2.jwt.NimbusReactiveJwtDecoder.withSecretKey;
/**
* A {@link ReactiveJwtDecoderFactory factory} that provides a {@link ReactiveJwtDecoder}
* used for {@link OidcIdToken} signature verification.
@@ -45,14 +54,45 @@ import java.util.function.Function;
*/
public final class ReactiveOidcIdTokenDecoderFactory implements ReactiveJwtDecoderFactory<ClientRegistration> {
private static final String MISSING_SIGNATURE_VERIFIER_ERROR_CODE = "missing_signature_verifier";
private static Map<JwsAlgorithm, String> jcaAlgorithmMappings = new HashMap<JwsAlgorithm, String>() {
{
put(MacAlgorithm.HS256, "HmacSHA256");
put(MacAlgorithm.HS384, "HmacSHA384");
put(MacAlgorithm.HS512, "HmacSHA512");
}
};
private final Map<String, ReactiveJwtDecoder> jwtDecoders = new ConcurrentHashMap<>();
private Function<ClientRegistration, OAuth2TokenValidator<Jwt>> jwtValidatorFactory = OidcIdTokenValidator::new;
private Function<ClientRegistration, JwsAlgorithm> jwsAlgorithmResolver = clientRegistration -> SignatureAlgorithm.RS256;
@Override
public ReactiveJwtDecoder createDecoder(ClientRegistration clientRegistration) {
Assert.notNull(clientRegistration, "clientRegistration cannot be null");
return this.jwtDecoders.computeIfAbsent(clientRegistration.getRegistrationId(), key -> {
if (!StringUtils.hasText(clientRegistration.getProviderDetails().getJwkSetUri())) {
NimbusReactiveJwtDecoder jwtDecoder = buildDecoder(clientRegistration);
OAuth2TokenValidator<Jwt> jwtValidator = this.jwtValidatorFactory.apply(clientRegistration);
jwtDecoder.setJwtValidator(jwtValidator);
return jwtDecoder;
});
}
private NimbusReactiveJwtDecoder buildDecoder(ClientRegistration clientRegistration) {
JwsAlgorithm jwsAlgorithm = this.jwsAlgorithmResolver.apply(clientRegistration);
if (jwsAlgorithm != null && SignatureAlgorithm.class.isAssignableFrom(jwsAlgorithm.getClass())) {
// https://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation
//
// 6. If the ID Token is received via direct communication between the Client
// and the Token Endpoint (which it is in this flow),
// the TLS server validation MAY be used to validate the issuer in place of checking the token signature.
// The Client MUST validate the signature of all other ID Tokens according to JWS [JWS]
// using the algorithm specified in the JWT alg Header Parameter.
// The Client MUST use the keys provided by the Issuer.
//
// 7. The alg value SHOULD be the default of RS256 or the algorithm sent by the Client
// in the id_token_signed_response_alg parameter during Registration.
String jwkSetUri = clientRegistration.getProviderDetails().getJwkSetUri();
if (!StringUtils.hasText(jwkSetUri)) {
OAuth2Error oauth2Error = new OAuth2Error(
MISSING_SIGNATURE_VERIFIER_ERROR_CODE,
"Failed to find a Signature Verifier for Client Registration: '" +
@@ -62,12 +102,42 @@ public final class ReactiveOidcIdTokenDecoderFactory implements ReactiveJwtDecod
);
throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());
}
NimbusReactiveJwtDecoder jwtDecoder = new NimbusReactiveJwtDecoder(
clientRegistration.getProviderDetails().getJwkSetUri());
OAuth2TokenValidator<Jwt> jwtValidator = this.jwtValidatorFactory.apply(clientRegistration);
jwtDecoder.setJwtValidator(jwtValidator);
return jwtDecoder;
});
return withJwkSetUri(jwkSetUri).jwsAlgorithm(jwsAlgorithm).build();
} else if (jwsAlgorithm != null && MacAlgorithm.class.isAssignableFrom(jwsAlgorithm.getClass())) {
// https://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation
//
// 8. If the JWT alg Header Parameter uses a MAC based algorithm such as HS256, HS384, or HS512,
// the octets of the UTF-8 representation of the client_secret
// corresponding to the client_id contained in the aud (audience) Claim
// are used as the key to validate the signature.
// For MAC based algorithms, the behavior is unspecified if the aud is multi-valued or
// if an azp value is present that is different than the aud value.
String clientSecret = clientRegistration.getClientSecret();
if (!StringUtils.hasText(clientSecret)) {
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 client secret.",
null
);
throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());
}
SecretKeySpec secretKeySpec = new SecretKeySpec(
clientSecret.getBytes(StandardCharsets.UTF_8), jcaAlgorithmMappings.get(jwsAlgorithm));
return withSecretKey(secretKeySpec).macAlgorithm((MacAlgorithm) jwsAlgorithm).build();
}
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 a valid JWS Algorithm: '" +
jwsAlgorithm + "'",
null
);
throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());
}
/**
@@ -80,4 +150,17 @@ public final class ReactiveOidcIdTokenDecoderFactory implements ReactiveJwtDecod
Assert.notNull(jwtValidatorFactory, "jwtValidatorFactory cannot be null");
this.jwtValidatorFactory = jwtValidatorFactory;
}
/**
* Sets the resolver that provides the expected {@link JwsAlgorithm JWS algorithm}
* used for the signature or MAC on the {@link OidcIdToken ID Token}.
* The default resolves to {@link SignatureAlgorithm#RS256 RS256} for all {@link ClientRegistration clients}.
*
* @param jwsAlgorithmResolver the resolver that provides the expected {@link JwsAlgorithm JWS algorithm}
* for a specific {@link ClientRegistration client}
*/
public final void setJwsAlgorithmResolver(Function<ClientRegistration, JwsAlgorithm> jwsAlgorithmResolver) {
Assert.notNull(jwsAlgorithmResolver, "jwsAlgorithmResolver cannot be null");
this.jwsAlgorithmResolver = jwsAlgorithmResolver;
}
}

View File

@@ -21,13 +21,15 @@ import org.springframework.security.oauth2.client.registration.ClientRegistratio
import org.springframework.security.oauth2.client.registration.TestClientRegistrations;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.core.OAuth2TokenValidator;
import org.springframework.security.oauth2.jose.jws.JwsAlgorithm;
import org.springframework.security.oauth2.jose.jws.MacAlgorithm;
import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;
import org.springframework.security.oauth2.jwt.Jwt;
import java.util.function.Function;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;
/**
@@ -42,8 +44,6 @@ public class OidcIdTokenDecoderFactoryTests {
private OidcIdTokenDecoderFactory idTokenDecoderFactory;
private Function<ClientRegistration, OAuth2TokenValidator<Jwt>> defaultJwtValidatorFactory = OidcIdTokenValidator::new;
@Before
public void setUp() {
this.idTokenDecoderFactory = new OidcIdTokenDecoderFactory();
@@ -55,6 +55,12 @@ public class OidcIdTokenDecoderFactoryTests {
.isInstanceOf(IllegalArgumentException.class);
}
@Test
public void setJwsAlgorithmResolverWhenNullThenThrowIllegalArgumentException() {
assertThatThrownBy(() -> this.idTokenDecoderFactory.setJwsAlgorithmResolver(null))
.isInstanceOf(IllegalArgumentException.class);
}
@Test
public void createDecoderWhenClientRegistrationNullThenThrowIllegalArgumentException() {
assertThatThrownBy(() -> this.idTokenDecoderFactory.createDecoder(null))
@@ -62,9 +68,42 @@ public class OidcIdTokenDecoderFactoryTests {
}
@Test
public void createDecoderWhenJwkSetUriEmptyThenThrowOAuth2AuthenticationException() {
public void createDecoderWhenJwsAlgorithmDefaultAndJwkSetUriEmptyThenThrowOAuth2AuthenticationException() {
assertThatThrownBy(() -> this.idTokenDecoderFactory.createDecoder(this.registration.jwkSetUri(null).build()))
.isInstanceOf(OAuth2AuthenticationException.class);
.isInstanceOf(OAuth2AuthenticationException.class)
.hasMessage("[missing_signature_verifier] Failed to find a Signature Verifier " +
"for Client Registration: 'registration-id'. " +
"Check to ensure you have configured the JwkSet URI.");
}
@Test
public void createDecoderWhenJwsAlgorithmEcAndJwkSetUriEmptyThenThrowOAuth2AuthenticationException() {
this.idTokenDecoderFactory.setJwsAlgorithmResolver(clientRegistration -> SignatureAlgorithm.ES256);
assertThatThrownBy(() -> this.idTokenDecoderFactory.createDecoder(this.registration.jwkSetUri(null).build()))
.isInstanceOf(OAuth2AuthenticationException.class)
.hasMessage("[missing_signature_verifier] Failed to find a Signature Verifier " +
"for Client Registration: 'registration-id'. " +
"Check to ensure you have configured the JwkSet URI.");
}
@Test
public void createDecoderWhenJwsAlgorithmHmacAndClientSecretNullThenThrowOAuth2AuthenticationException() {
this.idTokenDecoderFactory.setJwsAlgorithmResolver(clientRegistration -> MacAlgorithm.HS256);
assertThatThrownBy(() -> this.idTokenDecoderFactory.createDecoder(this.registration.clientSecret(null).build()))
.isInstanceOf(OAuth2AuthenticationException.class)
.hasMessage("[missing_signature_verifier] Failed to find a Signature Verifier " +
"for Client Registration: 'registration-id'. " +
"Check to ensure you have configured the client secret.");
}
@Test
public void createDecoderWhenJwsAlgorithmNullThenThrowOAuth2AuthenticationException() {
this.idTokenDecoderFactory.setJwsAlgorithmResolver(clientRegistration -> null);
assertThatThrownBy(() -> this.idTokenDecoderFactory.createDecoder(this.registration.build()))
.isInstanceOf(OAuth2AuthenticationException.class)
.hasMessage("[missing_signature_verifier] Failed to find a Signature Verifier " +
"for Client Registration: 'registration-id'. " +
"Check to ensure you have configured a valid JWS Algorithm: 'null'");
}
@Test
@@ -78,11 +117,28 @@ public class OidcIdTokenDecoderFactoryTests {
Function<ClientRegistration, OAuth2TokenValidator<Jwt>> customJwtValidatorFactory = mock(Function.class);
this.idTokenDecoderFactory.setJwtValidatorFactory(customJwtValidatorFactory);
when(customJwtValidatorFactory.apply(any(ClientRegistration.class)))
.thenReturn(this.defaultJwtValidatorFactory.apply(this.registration.build()));
ClientRegistration clientRegistration = this.registration.build();
this.idTokenDecoderFactory.createDecoder(this.registration.build());
when(customJwtValidatorFactory.apply(same(clientRegistration)))
.thenReturn(new OidcIdTokenValidator(clientRegistration));
verify(customJwtValidatorFactory).apply(any(ClientRegistration.class));
this.idTokenDecoderFactory.createDecoder(clientRegistration);
verify(customJwtValidatorFactory).apply(same(clientRegistration));
}
@Test
public void createDecoderWhenCustomJwsAlgorithmResolverSetThenApplied() {
Function<ClientRegistration, JwsAlgorithm> customJwsAlgorithmResolver = mock(Function.class);
this.idTokenDecoderFactory.setJwsAlgorithmResolver(customJwsAlgorithmResolver);
ClientRegistration clientRegistration = this.registration.build();
when(customJwsAlgorithmResolver.apply(same(clientRegistration)))
.thenReturn(MacAlgorithm.HS256);
this.idTokenDecoderFactory.createDecoder(clientRegistration);
verify(customJwsAlgorithmResolver).apply(same(clientRegistration));
}
}

View File

@@ -21,13 +21,15 @@ import org.springframework.security.oauth2.client.registration.ClientRegistratio
import org.springframework.security.oauth2.client.registration.TestClientRegistrations;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.core.OAuth2TokenValidator;
import org.springframework.security.oauth2.jose.jws.JwsAlgorithm;
import org.springframework.security.oauth2.jose.jws.MacAlgorithm;
import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;
import org.springframework.security.oauth2.jwt.Jwt;
import java.util.function.Function;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;
/**
@@ -42,8 +44,6 @@ public class ReactiveOidcIdTokenDecoderFactoryTests {
private ReactiveOidcIdTokenDecoderFactory idTokenDecoderFactory;
private Function<ClientRegistration, OAuth2TokenValidator<Jwt>> defaultJwtValidatorFactory = OidcIdTokenValidator::new;
@Before
public void setUp() {
this.idTokenDecoderFactory = new ReactiveOidcIdTokenDecoderFactory();
@@ -55,6 +55,12 @@ public class ReactiveOidcIdTokenDecoderFactoryTests {
.isInstanceOf(IllegalArgumentException.class);
}
@Test
public void setJwsAlgorithmResolverWhenNullThenThrowIllegalArgumentException() {
assertThatThrownBy(() -> this.idTokenDecoderFactory.setJwsAlgorithmResolver(null))
.isInstanceOf(IllegalArgumentException.class);
}
@Test
public void createDecoderWhenClientRegistrationNullThenThrowIllegalArgumentException() {
assertThatThrownBy(() -> this.idTokenDecoderFactory.createDecoder(null))
@@ -62,9 +68,42 @@ public class ReactiveOidcIdTokenDecoderFactoryTests {
}
@Test
public void createDecoderWhenJwkSetUriEmptyThenThrowOAuth2AuthenticationException() {
public void createDecoderWhenJwsAlgorithmDefaultAndJwkSetUriEmptyThenThrowOAuth2AuthenticationException() {
assertThatThrownBy(() -> this.idTokenDecoderFactory.createDecoder(this.registration.jwkSetUri(null).build()))
.isInstanceOf(OAuth2AuthenticationException.class);
.isInstanceOf(OAuth2AuthenticationException.class)
.hasMessage("[missing_signature_verifier] Failed to find a Signature Verifier " +
"for Client Registration: 'registration-id'. " +
"Check to ensure you have configured the JwkSet URI.");
}
@Test
public void createDecoderWhenJwsAlgorithmEcAndJwkSetUriEmptyThenThrowOAuth2AuthenticationException() {
this.idTokenDecoderFactory.setJwsAlgorithmResolver(clientRegistration -> SignatureAlgorithm.ES256);
assertThatThrownBy(() -> this.idTokenDecoderFactory.createDecoder(this.registration.jwkSetUri(null).build()))
.isInstanceOf(OAuth2AuthenticationException.class)
.hasMessage("[missing_signature_verifier] Failed to find a Signature Verifier " +
"for Client Registration: 'registration-id'. " +
"Check to ensure you have configured the JwkSet URI.");
}
@Test
public void createDecoderWhenJwsAlgorithmHmacAndClientSecretNullThenThrowOAuth2AuthenticationException() {
this.idTokenDecoderFactory.setJwsAlgorithmResolver(clientRegistration -> MacAlgorithm.HS256);
assertThatThrownBy(() -> this.idTokenDecoderFactory.createDecoder(this.registration.clientSecret(null).build()))
.isInstanceOf(OAuth2AuthenticationException.class)
.hasMessage("[missing_signature_verifier] Failed to find a Signature Verifier " +
"for Client Registration: 'registration-id'. " +
"Check to ensure you have configured the client secret.");
}
@Test
public void createDecoderWhenJwsAlgorithmNullThenThrowOAuth2AuthenticationException() {
this.idTokenDecoderFactory.setJwsAlgorithmResolver(clientRegistration -> null);
assertThatThrownBy(() -> this.idTokenDecoderFactory.createDecoder(this.registration.build()))
.isInstanceOf(OAuth2AuthenticationException.class)
.hasMessage("[missing_signature_verifier] Failed to find a Signature Verifier " +
"for Client Registration: 'registration-id'. " +
"Check to ensure you have configured a valid JWS Algorithm: 'null'");
}
@Test
@@ -78,11 +117,28 @@ public class ReactiveOidcIdTokenDecoderFactoryTests {
Function<ClientRegistration, OAuth2TokenValidator<Jwt>> customJwtValidatorFactory = mock(Function.class);
this.idTokenDecoderFactory.setJwtValidatorFactory(customJwtValidatorFactory);
when(customJwtValidatorFactory.apply(any(ClientRegistration.class)))
.thenReturn(this.defaultJwtValidatorFactory.apply(this.registration.build()));
ClientRegistration clientRegistration = this.registration.build();
this.idTokenDecoderFactory.createDecoder(this.registration.build());
when(customJwtValidatorFactory.apply(same(clientRegistration)))
.thenReturn(new OidcIdTokenValidator(clientRegistration));
verify(customJwtValidatorFactory).apply(any(ClientRegistration.class));
this.idTokenDecoderFactory.createDecoder(clientRegistration);
verify(customJwtValidatorFactory).apply(same(clientRegistration));
}
@Test
public void createDecoderWhenCustomJwsAlgorithmResolverSetThenApplied() {
Function<ClientRegistration, JwsAlgorithm> customJwsAlgorithmResolver = mock(Function.class);
this.idTokenDecoderFactory.setJwsAlgorithmResolver(customJwsAlgorithmResolver);
ClientRegistration clientRegistration = this.registration.build();
when(customJwsAlgorithmResolver.apply(same(clientRegistration)))
.thenReturn(MacAlgorithm.HS256);
this.idTokenDecoderFactory.createDecoder(clientRegistration);
verify(customJwsAlgorithmResolver).apply(same(clientRegistration));
}
}