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:
		
							parent
							
								
									dd45a49f02
								
							
						
					
					
						commit
						fe5f10e9a2
					
				| 
						 | 
				
			
			@ -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;
 | 
			
		||||
			});
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
			});
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
	};
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
		};
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
		Reference in New Issue