Extract the ID Token JwtDecoderFactory to enable user customization

This commit ensures that the JwtDecoderFactory is not a private field inside
the Oidc authentication provider by extracting this class and giving the
possibility to customize the way different providers are validated.

Fixes: gh-6379
This commit is contained in:
Rafael Dominguez 2019-01-10 09:02:47 -06:00 committed by Joe Grandja
parent dd45a49f02
commit fe5f10e9a2
6 changed files with 362 additions and 73 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -27,11 +27,9 @@ import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserRequest;
import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserService;
import org.springframework.security.oauth2.client.registration.ClientRegistration;
import org.springframework.security.oauth2.client.userinfo.OAuth2UserService;
import org.springframework.security.oauth2.core.DelegatingOAuth2TokenValidator;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.core.OAuth2AuthorizationException;
import org.springframework.security.oauth2.core.OAuth2Error;
import org.springframework.security.oauth2.core.OAuth2TokenValidator;
import org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationResponse;
@ -43,16 +41,10 @@ import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.oauth2.jwt.JwtDecoder;
import org.springframework.security.oauth2.jwt.JwtDecoderFactory;
import org.springframework.security.oauth2.jwt.JwtException;
import org.springframework.security.oauth2.jwt.JwtTimestampValidator;
import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import java.util.Collection;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import static org.springframework.security.oauth2.jwt.JwtProcessors.withJwkSetUri;
/**
* An implementation of an {@link AuthenticationProvider}
@ -82,10 +74,9 @@ public class OidcAuthorizationCodeAuthenticationProvider implements Authenticati
private static final String INVALID_STATE_PARAMETER_ERROR_CODE = "invalid_state_parameter";
private static final String INVALID_REDIRECT_URI_PARAMETER_ERROR_CODE = "invalid_redirect_uri_parameter";
private static final String INVALID_ID_TOKEN_ERROR_CODE = "invalid_id_token";
private static final String MISSING_SIGNATURE_VERIFIER_ERROR_CODE = "missing_signature_verifier";
private final OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> accessTokenResponseClient;
private final OAuth2UserService<OidcUserRequest, OidcUser> userService;
private JwtDecoderFactory<ClientRegistration> jwtDecoderFactory = new DefaultJwtDecoderFactory();
private JwtDecoderFactory<ClientRegistration> jwtDecoderFactory = new OidcIdTokenDecoderFactory();
private GrantedAuthoritiesMapper authoritiesMapper = (authorities -> authorities);
/**
@ -219,30 +210,4 @@ public class OidcAuthorizationCodeAuthenticationProvider implements Authenticati
OidcIdToken idToken = new OidcIdToken(jwt.getTokenValue(), jwt.getIssuedAt(), jwt.getExpiresAt(), jwt.getClaims());
return idToken;
}
private static class DefaultJwtDecoderFactory implements JwtDecoderFactory<ClientRegistration> {
private final Map<String, JwtDecoder> jwtDecoders = new ConcurrentHashMap<>();
@Override
public JwtDecoder createDecoder(ClientRegistration clientRegistration) {
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 = new DelegatingOAuth2TokenValidator<>(
new JwtTimestampValidator(), new OidcIdTokenValidator(clientRegistration));
jwtDecoder.setJwtValidator(jwtValidator);
return jwtDecoder;
});
}
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -26,12 +26,10 @@ import org.springframework.security.oauth2.client.endpoint.ReactiveOAuth2AccessT
import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserRequest;
import org.springframework.security.oauth2.client.registration.ClientRegistration;
import org.springframework.security.oauth2.client.userinfo.ReactiveOAuth2UserService;
import org.springframework.security.oauth2.core.DelegatingOAuth2TokenValidator;
import org.springframework.security.oauth2.core.OAuth2AccessToken;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.core.OAuth2AuthorizationException;
import org.springframework.security.oauth2.core.OAuth2Error;
import org.springframework.security.oauth2.core.OAuth2TokenValidator;
import org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationResponse;
@ -39,19 +37,14 @@ import org.springframework.security.oauth2.core.oidc.OidcIdToken;
import org.springframework.security.oauth2.core.oidc.endpoint.OidcParameterNames;
import org.springframework.security.oauth2.core.oidc.user.OidcUser;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.oauth2.jwt.JwtException;
import org.springframework.security.oauth2.jwt.JwtTimestampValidator;
import org.springframework.security.oauth2.jwt.NimbusReactiveJwtDecoder;
import org.springframework.security.oauth2.jwt.ReactiveJwtDecoder;
import org.springframework.security.oauth2.jwt.ReactiveJwtDecoderFactory;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import reactor.core.publisher.Mono;
import java.util.Collection;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* An implementation of an {@link org.springframework.security.authentication.AuthenticationProvider} for OAuth 2.0 Login,
@ -83,7 +76,6 @@ public class OidcAuthorizationCodeReactiveAuthenticationManager implements
private static final String INVALID_STATE_PARAMETER_ERROR_CODE = "invalid_state_parameter";
private static final String INVALID_REDIRECT_URI_PARAMETER_ERROR_CODE = "invalid_redirect_uri_parameter";
private static final String INVALID_ID_TOKEN_ERROR_CODE = "invalid_id_token";
private static final String MISSING_SIGNATURE_VERIFIER_ERROR_CODE = "missing_signature_verifier";
private final ReactiveOAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> accessTokenResponseClient;
@ -91,7 +83,7 @@ public class OidcAuthorizationCodeReactiveAuthenticationManager implements
private GrantedAuthoritiesMapper authoritiesMapper = (authorities -> authorities);
private ReactiveJwtDecoderFactory<ClientRegistration> jwtDecoderFactory = new DefaultJwtDecoderFactory();
private ReactiveJwtDecoderFactory<ClientRegistration> jwtDecoderFactory = new ReactiveOidcIdTokenDecoderFactory();
public OidcAuthorizationCodeReactiveAuthenticationManager(
ReactiveOAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> accessTokenResponseClient,
@ -199,30 +191,4 @@ public class OidcAuthorizationCodeReactiveAuthenticationManager implements
return jwtDecoder.decode(rawIdToken)
.map(jwt -> new OidcIdToken(jwt.getTokenValue(), jwt.getIssuedAt(), jwt.getExpiresAt(), jwt.getClaims()));
}
private static class DefaultJwtDecoderFactory implements ReactiveJwtDecoderFactory<ClientRegistration> {
private final Map<String, ReactiveJwtDecoder> jwtDecoders = new ConcurrentHashMap<>();
@Override
public ReactiveJwtDecoder createDecoder(ClientRegistration clientRegistration) {
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());
}
NimbusReactiveJwtDecoder jwtDecoder = new NimbusReactiveJwtDecoder(
clientRegistration.getProviderDetails().getJwkSetUri());
OAuth2TokenValidator<Jwt> jwtValidator = new DelegatingOAuth2TokenValidator<>(
new JwtTimestampValidator(), new OidcIdTokenValidator(clientRegistration));
jwtDecoder.setJwtValidator(jwtValidator);
return jwtDecoder;
});
}
}
}

View File

@ -0,0 +1,81 @@
/*
* Copyright 2002-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.oauth2.client.oidc.authentication;
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.jwt.Jwt;
import org.springframework.security.oauth2.jwt.JwtDecoder;
import org.springframework.security.oauth2.jwt.JwtDecoderFactory;
import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import static org.springframework.security.oauth2.jwt.JwtProcessors.withJwkSetUri;
/**
* Provides a default or custom implementation for {@link OAuth2TokenValidator}
*
* @author Joe Grandja
* @author Rafael Dominguez
* @since 5.2
*
* @see OAuth2TokenValidator
* @see Jwt
*/
public final class OidcIdTokenDecoderFactory implements JwtDecoderFactory<ClientRegistration> {
private static final String MISSING_SIGNATURE_VERIFIER_ERROR_CODE = "missing_signature_verifier";
private final Map<String, JwtDecoder> jwtDecoders = new ConcurrentHashMap<>();
private Function<ClientRegistration, OAuth2TokenValidator<Jwt>> jwtValidatorFactory = OidcIdTokenValidator::new;
@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 = jwtValidatorFactory.apply(clientRegistration);
jwtDecoder.setJwtValidator(jwtValidator);
return jwtDecoder;
});
}
/**
* Allows user customization for the {@link OAuth2TokenValidator}
*
* @param jwtValidatorFactory
*/
public final void setJwtValidatorFactory(Function<ClientRegistration, OAuth2TokenValidator<Jwt>> jwtValidatorFactory) {
Assert.notNull(jwtValidatorFactory, "jwtValidatorFactory cannot be null.");
this.jwtValidatorFactory = jwtValidatorFactory;
}
}

View File

@ -0,0 +1,79 @@
/*
* Copyright 2002-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.oauth2.client.oidc.authentication;
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.jwt.Jwt;
import org.springframework.security.oauth2.jwt.NimbusReactiveJwtDecoder;
import org.springframework.security.oauth2.jwt.ReactiveJwtDecoder;
import org.springframework.security.oauth2.jwt.ReactiveJwtDecoderFactory;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
/**
* Provides a default or custom reactive implementation for {@link OAuth2TokenValidator}
*
* @author Joe Grandja
* @author Rafael Dominguez
* @since 5.2
*
* @see OAuth2TokenValidator
* @see Jwt
*/
public final class ReactiveOidcIdTokenDecoderFactory implements ReactiveJwtDecoderFactory<ClientRegistration> {
private static final String MISSING_SIGNATURE_VERIFIER_ERROR_CODE = "missing_signature_verifier";
private final Map<String, ReactiveJwtDecoder> jwtDecoders = new ConcurrentHashMap<>();
private Function<ClientRegistration, OAuth2TokenValidator<Jwt>> jwtValidatorFactory = OidcIdTokenValidator::new;
@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())) {
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());
}
NimbusReactiveJwtDecoder jwtDecoder = new NimbusReactiveJwtDecoder(
clientRegistration.getProviderDetails().getJwkSetUri());
OAuth2TokenValidator<Jwt> jwtValidator = jwtValidatorFactory.apply(clientRegistration);
jwtDecoder.setJwtValidator(jwtValidator);
return jwtDecoder;
});
}
/**
* Allows user customization for the {@link OAuth2TokenValidator}
*
* @param jwtValidatorFactory
*/
public final void setJwtValidatorFactory(Function<ClientRegistration, OAuth2TokenValidator<Jwt>> jwtValidatorFactory) {
Assert.notNull(jwtValidatorFactory, "jwtValidatorFactory cannot be null.");
this.jwtValidatorFactory = jwtValidatorFactory;
}
}

View File

@ -0,0 +1,99 @@
/*
* Copyright 2002-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.oauth2.client.oidc.authentication;
import org.junit.Before;
import org.junit.Test;
import org.springframework.security.oauth2.client.registration.ClientRegistration;
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.jwt.Jwt;
import java.time.Duration;
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.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
/**
* @author Joe Grandja
* @author Rafael Dominguez
* @since 5.2
*/
public class OidcIdTokenDecoderFactoryTests {
private ClientRegistration.Builder registration = TestClientRegistrations.clientRegistration()
.scope("openid");
private OidcIdTokenDecoderFactory idTokenDecoderFactory;
@Before
public void setUp() {
idTokenDecoderFactory = new OidcIdTokenDecoderFactory();
}
@Test
public void setJwtValidatorFactoryWhenNullThenThrowIllegalArgumentException(){
assertThatThrownBy(()-> idTokenDecoderFactory.setJwtValidatorFactory(null))
.isInstanceOf(IllegalArgumentException.class);
}
@Test
public void createDecoderWhenClientRegistrationNullThenThrowIllegalArgumentException(){
assertThatThrownBy(() -> idTokenDecoderFactory.createDecoder(null))
.isInstanceOf(IllegalArgumentException.class);
}
@Test
public void createDecoderWhenJwkSetUriEmptyThenThrowOAuth2AuthenticationException(){
assertThatThrownBy(()-> idTokenDecoderFactory.createDecoder(registration.jwkSetUri(null).build()))
.isInstanceOf(OAuth2AuthenticationException.class);
}
@Test
public void createDecoderWhenClientRegistrationValidThenReturnDecoder(){
assertThat(idTokenDecoderFactory.createDecoder(registration.build()))
.isNotNull();
}
@Test
public void createDecoderWhenCustomJwtValidatorFactorySetThenApplied(){
Function<ClientRegistration, OAuth2TokenValidator<Jwt>> customValidator = mock(Function.class);
idTokenDecoderFactory.setJwtValidatorFactory(customValidator);
when(customValidator.apply(any(ClientRegistration.class)))
.thenReturn(customJwtValidatorFactory.apply(registration.build()));
idTokenDecoderFactory.createDecoder(registration.build());
verify(customValidator).apply(any(ClientRegistration.class));
}
private Function<ClientRegistration, OAuth2TokenValidator<Jwt>> customJwtValidatorFactory = (c) -> {
OidcIdTokenValidator idTokenValidator = new OidcIdTokenValidator(c);
if (c.getRegistrationId().equals("registration-id1")) {
idTokenValidator.setClockSkew(Duration.ofSeconds(30));
} else if (c.getRegistrationId().equals("registration-id2")) {
idTokenValidator.setClockSkew(Duration.ofSeconds(70));
}
return idTokenValidator;
};
}

View File

@ -0,0 +1,99 @@
/*
* Copyright 2002-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.oauth2.client.oidc.authentication;
import org.junit.Before;
import org.junit.Test;
import org.springframework.security.oauth2.client.registration.ClientRegistration;
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.jwt.Jwt;
import java.time.Duration;
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.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
/**
* @author Joe Grandja
* @author Rafael Dominguez
* @since 5.2
*/
public class ReactiveOidcIdTokenDecoderFactoryTests {
private ClientRegistration.Builder registration = TestClientRegistrations.clientRegistration()
.scope("openid");
private ReactiveOidcIdTokenDecoderFactory idTokenDecoderFactory;
@Before
public void setUp() {
idTokenDecoderFactory = new ReactiveOidcIdTokenDecoderFactory();
}
@Test
public void setJwtValidatorFactoryWhenNullThenThrowIllegalArgumentException(){
assertThatThrownBy(()-> idTokenDecoderFactory.setJwtValidatorFactory(null))
.isInstanceOf(IllegalArgumentException.class);
}
@Test
public void createDecoderWhenClientRegistrationNullThenThrowIllegalArgumentException(){
assertThatThrownBy(() -> idTokenDecoderFactory.createDecoder(null))
.isInstanceOf(IllegalArgumentException.class);
}
@Test
public void createDecoderWhenJwkSetUriEmptyThenThrowOAuth2AuthenticationException(){
assertThatThrownBy(()-> idTokenDecoderFactory.createDecoder(registration.jwkSetUri(null).build()))
.isInstanceOf(OAuth2AuthenticationException.class);
}
@Test
public void createDecoderWhenClientRegistrationValidThenReturnDecoder(){
assertThat(idTokenDecoderFactory.createDecoder(registration.build()))
.isNotNull();
}
@Test
public void createDecoderWhenCustomJwtValidatorFactorySetThenApplied(){
Function<ClientRegistration, OAuth2TokenValidator<Jwt>> customValidator = mock(Function.class);
idTokenDecoderFactory.setJwtValidatorFactory(customValidator);
when(customValidator.apply(any(ClientRegistration.class)))
.thenReturn(customJwtValidatorFactory.apply(registration.build()));
idTokenDecoderFactory.createDecoder(registration.build());
verify(customValidator).apply(any(ClientRegistration.class));
}
private Function<ClientRegistration, OAuth2TokenValidator<Jwt>> customJwtValidatorFactory = (c) -> {
OidcIdTokenValidator idTokenValidator = new OidcIdTokenValidator(c);
if (c.getRegistrationId().equals("registration-id1")) {
idTokenValidator.setClockSkew(Duration.ofSeconds(30));
} else if (c.getRegistrationId().equals("registration-id2")) {
idTokenValidator.setClockSkew(Duration.ofSeconds(70));
}
return idTokenValidator;
};
}