Support symmetric key for JwtDecoder
Fixes gh-5465
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user