Apply Spring formatting to 1.2.x
Issue gh-1616
This commit is contained in:
@@ -75,8 +75,9 @@ public class JdbcOAuth2AuthorizationConsentService implements OAuth2Authorizatio
|
||||
|
||||
@Override
|
||||
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
|
||||
hints.resources().registerResource(new ClassPathResource(
|
||||
"org/springframework/security/oauth2/server/authorization/oauth2-authorization-consent-schema.sql"));
|
||||
hints.resources()
|
||||
.registerResource(new ClassPathResource(
|
||||
"org/springframework/security/oauth2/server/authorization/oauth2-authorization-consent-schema.sql"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -101,8 +101,9 @@ public class JdbcOAuth2AuthorizationService implements OAuth2AuthorizationServic
|
||||
|
||||
@Override
|
||||
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
|
||||
hints.resources().registerResource(new ClassPathResource(
|
||||
"org/springframework/security/oauth2/server/authorization/oauth2-authorization-schema.sql"));
|
||||
hints.resources()
|
||||
.registerResource(new ClassPathResource(
|
||||
"org/springframework/security/oauth2/server/authorization/oauth2-authorization-schema.sql"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -51,19 +51,22 @@ import org.springframework.security.web.savedrequest.DefaultSavedRequest;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* {@link BeanRegistrationAotProcessor} that detects specific registered beans and contributes the required {@link RuntimeHints}.
|
||||
* Statically registered via META-INF/spring/aot.factories.
|
||||
* {@link BeanRegistrationAotProcessor} that detects specific registered beans and
|
||||
* contributes the required {@link RuntimeHints}. Statically registered via
|
||||
* META-INF/spring/aot.factories.
|
||||
*
|
||||
* @author Joe Grandja
|
||||
* @author Josh Long
|
||||
* @since 1.2
|
||||
*/
|
||||
class OAuth2AuthorizationServerBeanRegistrationAotProcessor implements BeanRegistrationAotProcessor {
|
||||
|
||||
private boolean jackson2Contributed;
|
||||
|
||||
@Override
|
||||
public BeanRegistrationAotContribution processAheadOfTime(RegisteredBean registeredBean) {
|
||||
String beanClassName = registeredBean.getBeanClass().getName();
|
||||
// @formatter:off
|
||||
if ((beanClassName.equals("org.springframework.security.oauth2.server.authorization.JdbcOAuth2AuthorizationService") ||
|
||||
beanClassName.equals("org.springframework.security.oauth2.server.authorization.client.JdbcRegisteredClientRepository")) &&
|
||||
!jackson2Contributed) {
|
||||
@@ -72,10 +75,13 @@ class OAuth2AuthorizationServerBeanRegistrationAotProcessor implements BeanRegis
|
||||
jackson2Contributed = true;
|
||||
return jackson2Contribution;
|
||||
}
|
||||
// @formatter:on
|
||||
return null;
|
||||
}
|
||||
|
||||
private static class Jackson2ConfigurationBeanRegistrationAotContribution implements BeanRegistrationAotContribution {
|
||||
private static class Jackson2ConfigurationBeanRegistrationAotContribution
|
||||
implements BeanRegistrationAotContribution {
|
||||
|
||||
private final BindingReflectionHintsRegistrar reflectionHintsRegistrar = new BindingReflectionHintsRegistrar();
|
||||
|
||||
@Override
|
||||
@@ -84,50 +90,40 @@ class OAuth2AuthorizationServerBeanRegistrationAotProcessor implements BeanRegis
|
||||
}
|
||||
|
||||
private void registerHints(RuntimeHints hints) {
|
||||
// Collections -> UnmodifiableSet, UnmodifiableList, UnmodifiableMap, UnmodifiableRandomAccessList, etc.
|
||||
hints.reflection().registerType(Collections.class,
|
||||
MemberCategory.DECLARED_CLASSES);
|
||||
// Collections -> UnmodifiableSet, UnmodifiableList, UnmodifiableMap,
|
||||
// UnmodifiableRandomAccessList, etc.
|
||||
hints.reflection().registerType(Collections.class, MemberCategory.DECLARED_CLASSES);
|
||||
|
||||
// HashSet
|
||||
hints.reflection().registerType(HashSet.class,
|
||||
MemberCategory.DECLARED_FIELDS, MemberCategory.INVOKE_DECLARED_CONSTRUCTORS,
|
||||
MemberCategory.INVOKE_DECLARED_METHODS);
|
||||
hints.reflection()
|
||||
.registerType(HashSet.class, MemberCategory.DECLARED_FIELDS,
|
||||
MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, MemberCategory.INVOKE_DECLARED_METHODS);
|
||||
|
||||
// Spring Security and Spring Authorization Server
|
||||
hints.reflection().registerTypes(
|
||||
Arrays.asList(
|
||||
TypeReference.of(AbstractAuthenticationToken.class),
|
||||
TypeReference.of(DefaultSavedRequest.Builder.class),
|
||||
TypeReference.of(WebAuthenticationDetails.class),
|
||||
TypeReference.of(UsernamePasswordAuthenticationToken.class),
|
||||
TypeReference.of(User.class),
|
||||
TypeReference.of(DefaultOidcUser.class),
|
||||
TypeReference.of(DefaultOAuth2User.class),
|
||||
TypeReference.of(OidcUserAuthority.class),
|
||||
TypeReference.of(OAuth2UserAuthority.class),
|
||||
TypeReference.of(SimpleGrantedAuthority.class),
|
||||
TypeReference.of(OidcIdToken.class),
|
||||
TypeReference.of(AbstractOAuth2Token.class),
|
||||
TypeReference.of(OidcUserInfo.class),
|
||||
TypeReference.of(OAuth2AuthorizationRequest.class),
|
||||
TypeReference.of(AuthorizationGrantType.class),
|
||||
TypeReference.of(OAuth2AuthorizationResponseType.class),
|
||||
TypeReference.of(OAuth2TokenFormat.class)
|
||||
), builder ->
|
||||
builder.withMembers(MemberCategory.DECLARED_FIELDS,
|
||||
MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, MemberCategory.INVOKE_DECLARED_METHODS)
|
||||
);
|
||||
hints.reflection()
|
||||
.registerTypes(Arrays.asList(TypeReference.of(AbstractAuthenticationToken.class),
|
||||
TypeReference.of(DefaultSavedRequest.Builder.class),
|
||||
TypeReference.of(WebAuthenticationDetails.class),
|
||||
TypeReference.of(UsernamePasswordAuthenticationToken.class), TypeReference.of(User.class),
|
||||
TypeReference.of(DefaultOidcUser.class), TypeReference.of(DefaultOAuth2User.class),
|
||||
TypeReference.of(OidcUserAuthority.class), TypeReference.of(OAuth2UserAuthority.class),
|
||||
TypeReference.of(SimpleGrantedAuthority.class), TypeReference.of(OidcIdToken.class),
|
||||
TypeReference.of(AbstractOAuth2Token.class), TypeReference.of(OidcUserInfo.class),
|
||||
TypeReference.of(OAuth2AuthorizationRequest.class),
|
||||
TypeReference.of(AuthorizationGrantType.class),
|
||||
TypeReference.of(OAuth2AuthorizationResponseType.class),
|
||||
TypeReference.of(OAuth2TokenFormat.class)),
|
||||
builder -> builder.withMembers(MemberCategory.DECLARED_FIELDS,
|
||||
MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, MemberCategory.INVOKE_DECLARED_METHODS));
|
||||
|
||||
// Jackson Modules - Spring Security and Spring Authorization Server
|
||||
hints.reflection().registerTypes(
|
||||
Arrays.asList(
|
||||
TypeReference.of(CoreJackson2Module.class),
|
||||
TypeReference.of(WebServletJackson2Module.class),
|
||||
TypeReference.of(OAuth2AuthorizationServerJackson2Module.class)
|
||||
), builder ->
|
||||
builder.withMembers(MemberCategory.DECLARED_FIELDS,
|
||||
MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, MemberCategory.INVOKE_DECLARED_METHODS)
|
||||
);
|
||||
hints.reflection()
|
||||
.registerTypes(
|
||||
Arrays.asList(TypeReference.of(CoreJackson2Module.class),
|
||||
TypeReference.of(WebServletJackson2Module.class),
|
||||
TypeReference.of(OAuth2AuthorizationServerJackson2Module.class)),
|
||||
builder -> builder.withMembers(MemberCategory.DECLARED_FIELDS,
|
||||
MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, MemberCategory.INVOKE_DECLARED_METHODS));
|
||||
|
||||
// Jackson Mixins - Spring Security and Spring Authorization Server
|
||||
this.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(),
|
||||
@@ -136,8 +132,8 @@ class OAuth2AuthorizationServerBeanRegistrationAotProcessor implements BeanRegis
|
||||
loadClass("org.springframework.security.jackson2.UnmodifiableListMixin"));
|
||||
this.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(),
|
||||
loadClass("org.springframework.security.jackson2.UnmodifiableMapMixin"));
|
||||
this.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(),
|
||||
loadClass("org.springframework.security.oauth2.server.authorization.jackson2.UnmodifiableMapMixin"));
|
||||
this.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(), loadClass(
|
||||
"org.springframework.security.oauth2.server.authorization.jackson2.UnmodifiableMapMixin"));
|
||||
this.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(),
|
||||
loadClass("org.springframework.security.oauth2.server.authorization.jackson2.HashSetMixin"));
|
||||
this.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(),
|
||||
@@ -150,28 +146,29 @@ class OAuth2AuthorizationServerBeanRegistrationAotProcessor implements BeanRegis
|
||||
loadClass("org.springframework.security.jackson2.UserMixin"));
|
||||
this.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(),
|
||||
loadClass("org.springframework.security.jackson2.SimpleGrantedAuthorityMixin"));
|
||||
this.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(),
|
||||
loadClass("org.springframework.security.oauth2.server.authorization.jackson2.OAuth2AuthorizationRequestMixin"));
|
||||
this.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(),
|
||||
loadClass("org.springframework.security.oauth2.server.authorization.jackson2.OAuth2TokenFormatMixin"));
|
||||
this.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(), loadClass(
|
||||
"org.springframework.security.oauth2.server.authorization.jackson2.OAuth2AuthorizationRequestMixin"));
|
||||
this.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(), loadClass(
|
||||
"org.springframework.security.oauth2.server.authorization.jackson2.OAuth2TokenFormatMixin"));
|
||||
|
||||
// Check if Spring Security OAuth2 Client is on classpath
|
||||
if (ClassUtils.isPresent("org.springframework.security.oauth2.client.registration.ClientRegistration",
|
||||
ClassUtils.getDefaultClassLoader())) {
|
||||
|
||||
// Jackson Module (and required types) - Spring Security OAuth2 Client
|
||||
hints.reflection().registerTypes(
|
||||
Arrays.asList(
|
||||
TypeReference.of("org.springframework.security.oauth2.client.jackson2.OAuth2ClientJackson2Module"),
|
||||
TypeReference.of("org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken")
|
||||
), builder ->
|
||||
builder.withMembers(MemberCategory.DECLARED_FIELDS,
|
||||
MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, MemberCategory.INVOKE_DECLARED_METHODS)
|
||||
);
|
||||
hints.reflection()
|
||||
.registerTypes(Arrays.asList(
|
||||
TypeReference
|
||||
.of("org.springframework.security.oauth2.client.jackson2.OAuth2ClientJackson2Module"),
|
||||
TypeReference
|
||||
.of("org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken")),
|
||||
builder -> builder.withMembers(MemberCategory.DECLARED_FIELDS,
|
||||
MemberCategory.INVOKE_DECLARED_CONSTRUCTORS,
|
||||
MemberCategory.INVOKE_DECLARED_METHODS));
|
||||
|
||||
// Jackson Mixins - Spring Security OAuth2 Client
|
||||
this.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(),
|
||||
loadClass("org.springframework.security.oauth2.client.jackson2.OAuth2AuthenticationTokenMixin"));
|
||||
this.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(), loadClass(
|
||||
"org.springframework.security.oauth2.client.jackson2.OAuth2AuthenticationTokenMixin"));
|
||||
this.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(),
|
||||
loadClass("org.springframework.security.oauth2.client.jackson2.DefaultOidcUserMixin"));
|
||||
this.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(),
|
||||
@@ -190,7 +187,8 @@ class OAuth2AuthorizationServerBeanRegistrationAotProcessor implements BeanRegis
|
||||
private static Class<?> loadClass(String className) {
|
||||
try {
|
||||
return Class.forName(className);
|
||||
} catch (ClassNotFoundException ex) {
|
||||
}
|
||||
catch (ClassNotFoundException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,8 +123,9 @@ public final class ClientSecretAuthenticationProvider implements AuthenticationP
|
||||
String clientSecret = clientAuthentication.getCredentials().toString();
|
||||
if (!this.passwordEncoder.matches(clientSecret, registeredClient.getClientSecret())) {
|
||||
if (this.logger.isDebugEnabled()) {
|
||||
this.logger.debug(LogMessage.format("Invalid request: client_secret does not match" +
|
||||
" for registered client '%s'", registeredClient.getId()));
|
||||
this.logger.debug(LogMessage.format(
|
||||
"Invalid request: client_secret does not match" + " for registered client '%s'",
|
||||
registeredClient.getId()));
|
||||
}
|
||||
throwInvalidClient(OAuth2ParameterNames.CLIENT_SECRET);
|
||||
}
|
||||
|
||||
@@ -98,11 +98,11 @@ final class CodeVerifierAuthenticator {
|
||||
.get(PkceParameterNames.CODE_CHALLENGE);
|
||||
String codeVerifier = (String) parameters.get(PkceParameterNames.CODE_VERIFIER);
|
||||
if (!StringUtils.hasText(codeChallenge)) {
|
||||
if (registeredClient.getClientSettings().isRequireProofKey() ||
|
||||
StringUtils.hasText(codeVerifier)) {
|
||||
if (registeredClient.getClientSettings().isRequireProofKey() || StringUtils.hasText(codeVerifier)) {
|
||||
if (this.logger.isDebugEnabled()) {
|
||||
this.logger.debug(LogMessage.format("Invalid request: code_challenge is required" +
|
||||
" for registered client '%s'", registeredClient.getId()));
|
||||
this.logger.debug(LogMessage.format(
|
||||
"Invalid request: code_challenge is required" + " for registered client '%s'",
|
||||
registeredClient.getId()));
|
||||
}
|
||||
throwInvalidGrant(PkceParameterNames.CODE_CHALLENGE);
|
||||
}
|
||||
@@ -122,8 +122,9 @@ final class CodeVerifierAuthenticator {
|
||||
.get(PkceParameterNames.CODE_CHALLENGE_METHOD);
|
||||
if (!codeVerifierValid(codeVerifier, codeChallenge, codeChallengeMethod)) {
|
||||
if (this.logger.isDebugEnabled()) {
|
||||
this.logger.debug(LogMessage.format("Invalid request: code_verifier is missing or invalid" +
|
||||
" for registered client '%s'", registeredClient.getId()));
|
||||
this.logger.debug(LogMessage.format(
|
||||
"Invalid request: code_verifier is missing or invalid" + " for registered client '%s'",
|
||||
registeredClient.getId()));
|
||||
}
|
||||
throwInvalidGrant(PkceParameterNames.CODE_VERIFIER);
|
||||
}
|
||||
|
||||
@@ -56,6 +56,7 @@ public final class OAuth2AuthorizationCodeRequestAuthenticationValidator
|
||||
implements Consumer<OAuth2AuthorizationCodeRequestAuthenticationContext> {
|
||||
|
||||
private static final String ERROR_URI = "https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.2.1";
|
||||
|
||||
private static final Log LOGGER = LogFactory.getLog(OAuth2AuthorizationCodeRequestAuthenticationValidator.class);
|
||||
|
||||
/**
|
||||
@@ -87,8 +88,9 @@ public final class OAuth2AuthorizationCodeRequestAuthenticationValidator
|
||||
Set<String> allowedScopes = registeredClient.getScopes();
|
||||
if (!requestedScopes.isEmpty() && !allowedScopes.containsAll(requestedScopes)) {
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug(LogMessage.format("Invalid request: requested scope is not allowed" +
|
||||
" for registered client '%s'", registeredClient.getId()));
|
||||
LOGGER.debug(LogMessage.format(
|
||||
"Invalid request: requested scope is not allowed" + " for registered client '%s'",
|
||||
registeredClient.getId()));
|
||||
}
|
||||
throwError(OAuth2ErrorCodes.INVALID_SCOPE, OAuth2ParameterNames.SCOPE,
|
||||
authorizationCodeRequestAuthentication, registeredClient);
|
||||
@@ -113,8 +115,8 @@ public final class OAuth2AuthorizationCodeRequestAuthenticationValidator
|
||||
}
|
||||
if (requestedRedirect == null || requestedRedirect.getFragment() != null) {
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug(LogMessage.format("Invalid request: redirect_uri is missing or contains a fragment" +
|
||||
" for registered client '%s'", registeredClient.getId()));
|
||||
LOGGER.debug(LogMessage.format("Invalid request: redirect_uri is missing or contains a fragment"
|
||||
+ " for registered client '%s'", registeredClient.getId()));
|
||||
}
|
||||
throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.REDIRECT_URI,
|
||||
authorizationCodeRequestAuthentication, registeredClient);
|
||||
@@ -148,8 +150,9 @@ public final class OAuth2AuthorizationCodeRequestAuthenticationValidator
|
||||
}
|
||||
if (!validRedirectUri) {
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug(LogMessage.format("Invalid request: redirect_uri does not match" +
|
||||
" for registered client '%s'", registeredClient.getId()));
|
||||
LOGGER.debug(LogMessage.format(
|
||||
"Invalid request: redirect_uri does not match" + " for registered client '%s'",
|
||||
registeredClient.getId()));
|
||||
}
|
||||
throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.REDIRECT_URI,
|
||||
authorizationCodeRequestAuthentication, registeredClient);
|
||||
|
||||
@@ -86,8 +86,9 @@ public class JdbcRegisteredClientRepository implements RegisteredClientRepositor
|
||||
|
||||
@Override
|
||||
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
|
||||
hints.resources().registerResource(new ClassPathResource(
|
||||
"org/springframework/security/oauth2/server/authorization/client/oauth2-registered-client-schema.sql"));
|
||||
hints.resources()
|
||||
.registerResource(new ClassPathResource(
|
||||
"org/springframework/security/oauth2/server/authorization/client/oauth2-registered-client-schema.sql"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -86,12 +86,14 @@ public final class OidcClientConfigurationAuthenticationProvider implements Auth
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link Converter} used for converting a {@link RegisteredClient} to an {@link OidcClientRegistration}.
|
||||
*
|
||||
* @param clientRegistrationConverter the {@link Converter} used for converting a {@link RegisteredClient} to an {@link OidcClientRegistration}
|
||||
* Sets the {@link Converter} used for converting a {@link RegisteredClient} to an
|
||||
* {@link OidcClientRegistration}.
|
||||
* @param clientRegistrationConverter the {@link Converter} used for converting a
|
||||
* {@link RegisteredClient} to an {@link OidcClientRegistration}
|
||||
* @since 1.2.0
|
||||
*/
|
||||
public void setClientRegistrationConverter(Converter<RegisteredClient, OidcClientRegistration> clientRegistrationConverter) {
|
||||
public void setClientRegistrationConverter(
|
||||
Converter<RegisteredClient, OidcClientRegistration> clientRegistrationConverter) {
|
||||
Assert.notNull(clientRegistrationConverter, "clientRegistrationConverter cannot be null");
|
||||
this.clientRegistrationConverter = clientRegistrationConverter;
|
||||
}
|
||||
|
||||
@@ -185,12 +185,14 @@ public final class OidcClientRegistrationAuthenticationProvider implements Authe
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link Converter} used for converting a {@link RegisteredClient} to an {@link OidcClientRegistration}.
|
||||
*
|
||||
* @param clientRegistrationConverter the {@link Converter} used for converting a {@link RegisteredClient} to an {@link OidcClientRegistration}
|
||||
* Sets the {@link Converter} used for converting a {@link RegisteredClient} to an
|
||||
* {@link OidcClientRegistration}.
|
||||
* @param clientRegistrationConverter the {@link Converter} used for converting a
|
||||
* {@link RegisteredClient} to an {@link OidcClientRegistration}
|
||||
* @since 1.2.0
|
||||
*/
|
||||
public void setClientRegistrationConverter(Converter<RegisteredClient, OidcClientRegistration> clientRegistrationConverter) {
|
||||
public void setClientRegistrationConverter(
|
||||
Converter<RegisteredClient, OidcClientRegistration> clientRegistrationConverter) {
|
||||
Assert.notNull(clientRegistrationConverter, "clientRegistrationConverter cannot be null");
|
||||
this.clientRegistrationConverter = clientRegistrationConverter;
|
||||
}
|
||||
|
||||
@@ -34,15 +34,19 @@ import org.springframework.security.oauth2.server.authorization.settings.TokenSe
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
/**
|
||||
* A {@link Converter} that converts the provided {@link OidcClientRegistration} to a {@link RegisteredClient}.
|
||||
* A {@link Converter} that converts the provided {@link OidcClientRegistration} to a
|
||||
* {@link RegisteredClient}.
|
||||
*
|
||||
* @author Joe Grandja
|
||||
* @author Dmitriy Dubson
|
||||
* @since 1.2.0
|
||||
*/
|
||||
public final class OidcClientRegistrationRegisteredClientConverter implements Converter<OidcClientRegistration, RegisteredClient> {
|
||||
public final class OidcClientRegistrationRegisteredClientConverter
|
||||
implements Converter<OidcClientRegistration, RegisteredClient> {
|
||||
|
||||
private static final StringKeyGenerator CLIENT_ID_GENERATOR = new Base64StringKeyGenerator(
|
||||
Base64.getUrlEncoder().withoutPadding(), 32);
|
||||
|
||||
private static final StringKeyGenerator CLIENT_SECRET_GENERATOR = new Base64StringKeyGenerator(
|
||||
Base64.getUrlEncoder().withoutPadding(), 48);
|
||||
|
||||
@@ -125,4 +129,3 @@ public final class OidcClientRegistrationRegisteredClientConverter implements Co
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -28,12 +28,14 @@ import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
|
||||
/**
|
||||
* A {@link Converter} that converts the provided {@link RegisteredClient} to an {@link OidcClientRegistration}.
|
||||
* A {@link Converter} that converts the provided {@link RegisteredClient} to an
|
||||
* {@link OidcClientRegistration}.
|
||||
*
|
||||
* @author Joe Grandja
|
||||
* @since 1.2.0
|
||||
*/
|
||||
public final class RegisteredClientOidcClientRegistrationConverter implements Converter<RegisteredClient, OidcClientRegistration> {
|
||||
public final class RegisteredClientOidcClientRegistrationConverter
|
||||
implements Converter<RegisteredClient, OidcClientRegistration> {
|
||||
|
||||
@Override
|
||||
public OidcClientRegistration convert(RegisteredClient registeredClient) {
|
||||
|
||||
@@ -99,27 +99,27 @@ public final class OidcProviderConfigurationEndpointFilter extends OncePerReques
|
||||
.getAuthorizationServerSettings();
|
||||
|
||||
OidcProviderConfiguration.Builder providerConfiguration = OidcProviderConfiguration.builder()
|
||||
.issuer(issuer)
|
||||
.authorizationEndpoint(asUrl(issuer, authorizationServerSettings.getAuthorizationEndpoint()))
|
||||
.deviceAuthorizationEndpoint(asUrl(issuer, authorizationServerSettings.getDeviceAuthorizationEndpoint()))
|
||||
.tokenEndpoint(asUrl(issuer, authorizationServerSettings.getTokenEndpoint()))
|
||||
.tokenEndpointAuthenticationMethods(clientAuthenticationMethods())
|
||||
.jwkSetUrl(asUrl(issuer, authorizationServerSettings.getJwkSetEndpoint()))
|
||||
.userInfoEndpoint(asUrl(issuer, authorizationServerSettings.getOidcUserInfoEndpoint()))
|
||||
.endSessionEndpoint(asUrl(issuer, authorizationServerSettings.getOidcLogoutEndpoint()))
|
||||
.responseType(OAuth2AuthorizationResponseType.CODE.getValue())
|
||||
.grantType(AuthorizationGrantType.AUTHORIZATION_CODE.getValue())
|
||||
.grantType(AuthorizationGrantType.CLIENT_CREDENTIALS.getValue())
|
||||
.grantType(AuthorizationGrantType.REFRESH_TOKEN.getValue())
|
||||
.grantType(AuthorizationGrantType.DEVICE_CODE.getValue())
|
||||
.tokenRevocationEndpoint(asUrl(issuer, authorizationServerSettings.getTokenRevocationEndpoint()))
|
||||
.tokenRevocationEndpointAuthenticationMethods(clientAuthenticationMethods())
|
||||
.tokenIntrospectionEndpoint(asUrl(issuer, authorizationServerSettings.getTokenIntrospectionEndpoint()))
|
||||
.tokenIntrospectionEndpointAuthenticationMethods(clientAuthenticationMethods())
|
||||
.codeChallengeMethod("S256")
|
||||
.subjectType("public")
|
||||
.idTokenSigningAlgorithm(SignatureAlgorithm.RS256.getName())
|
||||
.scope(OidcScopes.OPENID);
|
||||
.issuer(issuer)
|
||||
.authorizationEndpoint(asUrl(issuer, authorizationServerSettings.getAuthorizationEndpoint()))
|
||||
.deviceAuthorizationEndpoint(asUrl(issuer, authorizationServerSettings.getDeviceAuthorizationEndpoint()))
|
||||
.tokenEndpoint(asUrl(issuer, authorizationServerSettings.getTokenEndpoint()))
|
||||
.tokenEndpointAuthenticationMethods(clientAuthenticationMethods())
|
||||
.jwkSetUrl(asUrl(issuer, authorizationServerSettings.getJwkSetEndpoint()))
|
||||
.userInfoEndpoint(asUrl(issuer, authorizationServerSettings.getOidcUserInfoEndpoint()))
|
||||
.endSessionEndpoint(asUrl(issuer, authorizationServerSettings.getOidcLogoutEndpoint()))
|
||||
.responseType(OAuth2AuthorizationResponseType.CODE.getValue())
|
||||
.grantType(AuthorizationGrantType.AUTHORIZATION_CODE.getValue())
|
||||
.grantType(AuthorizationGrantType.CLIENT_CREDENTIALS.getValue())
|
||||
.grantType(AuthorizationGrantType.REFRESH_TOKEN.getValue())
|
||||
.grantType(AuthorizationGrantType.DEVICE_CODE.getValue())
|
||||
.tokenRevocationEndpoint(asUrl(issuer, authorizationServerSettings.getTokenRevocationEndpoint()))
|
||||
.tokenRevocationEndpointAuthenticationMethods(clientAuthenticationMethods())
|
||||
.tokenIntrospectionEndpoint(asUrl(issuer, authorizationServerSettings.getTokenIntrospectionEndpoint()))
|
||||
.tokenIntrospectionEndpointAuthenticationMethods(clientAuthenticationMethods())
|
||||
.codeChallengeMethod("S256")
|
||||
.subjectType("public")
|
||||
.idTokenSigningAlgorithm(SignatureAlgorithm.RS256.getName())
|
||||
.scope(OidcScopes.OPENID);
|
||||
|
||||
this.providerConfigurationCustomizer.accept(providerConfiguration);
|
||||
|
||||
|
||||
@@ -57,10 +57,12 @@ public final class OAuth2RefreshTokenGenerator implements OAuth2TokenGenerator<O
|
||||
}
|
||||
|
||||
private static boolean isPublicClientForAuthorizationCodeGrant(OAuth2TokenContext context) {
|
||||
// @formatter:off
|
||||
if (AuthorizationGrantType.AUTHORIZATION_CODE.equals(context.getAuthorizationGrantType()) &&
|
||||
(context.getAuthorizationGrant().getPrincipal() instanceof OAuth2ClientAuthenticationToken clientPrincipal)) {
|
||||
return clientPrincipal.getClientAuthenticationMethod().equals(ClientAuthenticationMethod.NONE);
|
||||
}
|
||||
// @formatter:on
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -35,8 +35,9 @@ import org.springframework.security.web.authentication.AuthenticationFailureHand
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* An implementation of an {@link AuthenticationFailureHandler} used for handling an {@link OAuth2AuthenticationException}
|
||||
* and returning the {@link OAuth2Error OAuth 2.0 Error Response}.
|
||||
* An implementation of an {@link AuthenticationFailureHandler} used for handling an
|
||||
* {@link OAuth2AuthenticationException} and returning the {@link OAuth2Error OAuth 2.0
|
||||
* Error Response}.
|
||||
*
|
||||
* @author Dmitriy Dubson
|
||||
* @see AuthenticationFailureHandler
|
||||
@@ -44,7 +45,9 @@ import org.springframework.util.Assert;
|
||||
* @since 1.2
|
||||
*/
|
||||
public final class OAuth2ErrorAuthenticationFailureHandler implements AuthenticationFailureHandler {
|
||||
|
||||
private final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private HttpMessageConverter<OAuth2Error> errorResponseConverter = new OAuth2ErrorHttpMessageConverter();
|
||||
|
||||
@Override
|
||||
@@ -56,19 +59,21 @@ public final class OAuth2ErrorAuthenticationFailureHandler implements Authentica
|
||||
if (authenticationException instanceof OAuth2AuthenticationException) {
|
||||
OAuth2Error error = ((OAuth2AuthenticationException) authenticationException).getError();
|
||||
this.errorResponseConverter.write(error, null, httpResponse);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
if (this.logger.isWarnEnabled()) {
|
||||
this.logger.warn(AuthenticationException.class.getSimpleName() + " must be of type " +
|
||||
OAuth2AuthenticationException.class.getName() +
|
||||
" but was " + authenticationException.getClass().getName());
|
||||
this.logger.warn(AuthenticationException.class.getSimpleName() + " must be of type "
|
||||
+ OAuth2AuthenticationException.class.getName() + " but was "
|
||||
+ authenticationException.getClass().getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link HttpMessageConverter} used for converting an {@link OAuth2Error} to an HTTP response.
|
||||
*
|
||||
* @param errorResponseConverter the {@link HttpMessageConverter} used for converting an {@link OAuth2Error} to an HTTP response
|
||||
* Sets the {@link HttpMessageConverter} used for converting an {@link OAuth2Error} to
|
||||
* an HTTP response.
|
||||
* @param errorResponseConverter the {@link HttpMessageConverter} used for converting
|
||||
* an {@link OAuth2Error} to an HTTP response
|
||||
*/
|
||||
public void setErrorResponseConverter(HttpMessageConverter<OAuth2Error> errorResponseConverter) {
|
||||
Assert.notNull(errorResponseConverter, "errorResponseConverter cannot be null");
|
||||
|
||||
@@ -406,8 +406,8 @@ public class OAuth2AuthorizationCodeAuthenticationProviderTests {
|
||||
doAnswer(answer -> {
|
||||
OAuth2TokenContext context = answer.getArgument(0);
|
||||
if (OAuth2TokenType.REFRESH_TOKEN.equals(context.getTokenType())) {
|
||||
return new OAuth2AccessToken(
|
||||
OAuth2AccessToken.TokenType.BEARER, "access-token", Instant.now(), Instant.now().plusSeconds(300));
|
||||
return new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, "access-token", Instant.now(),
|
||||
Instant.now().plusSeconds(300));
|
||||
}
|
||||
else {
|
||||
return answer.callRealMethod();
|
||||
@@ -415,12 +415,13 @@ public class OAuth2AuthorizationCodeAuthenticationProviderTests {
|
||||
}).when(this.tokenGenerator).generate(any());
|
||||
|
||||
assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication))
|
||||
.isInstanceOf(OAuth2AuthenticationException.class)
|
||||
.extracting(ex -> ((OAuth2AuthenticationException) ex).getError())
|
||||
.satisfies(error -> {
|
||||
assertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.SERVER_ERROR);
|
||||
assertThat(error.getDescription()).contains("The token generator failed to generate a valid refresh token.");
|
||||
});
|
||||
.isInstanceOf(OAuth2AuthenticationException.class)
|
||||
.extracting(ex -> ((OAuth2AuthenticationException) ex).getError())
|
||||
.satisfies(error -> {
|
||||
assertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.SERVER_ERROR);
|
||||
assertThat(error.getDescription())
|
||||
.contains("The token generator failed to generate a valid refresh token.");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -467,46 +467,52 @@ public class OAuth2AuthorizationCodeGrantTests {
|
||||
this.spring.register(AuthorizationServerConfigurationWithCustomRefreshTokenGenerator.class).autowire();
|
||||
|
||||
RegisteredClient registeredClient = TestRegisteredClients.registeredPublicClient()
|
||||
.authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)
|
||||
.build();
|
||||
.authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)
|
||||
.build();
|
||||
this.registeredClientRepository.save(registeredClient);
|
||||
|
||||
MvcResult mvcResult = this.mvc.perform(get(DEFAULT_AUTHORIZATION_ENDPOINT_URI)
|
||||
MvcResult mvcResult = this.mvc
|
||||
.perform(get(DEFAULT_AUTHORIZATION_ENDPOINT_URI)
|
||||
.queryParams(getAuthorizationRequestParameters(registeredClient))
|
||||
.queryParam(PkceParameterNames.CODE_CHALLENGE, S256_CODE_CHALLENGE)
|
||||
.queryParam(PkceParameterNames.CODE_CHALLENGE_METHOD, "S256")
|
||||
.with(user("user")))
|
||||
.andExpect(status().is3xxRedirection())
|
||||
.andReturn();
|
||||
.andExpect(status().is3xxRedirection())
|
||||
.andReturn();
|
||||
String redirectedUrl = mvcResult.getResponse().getRedirectedUrl();
|
||||
assertThat(redirectedUrl).matches("https://example.com\\?code=.{15,}&state=" + STATE_URL_ENCODED);
|
||||
|
||||
String authorizationCode = extractParameterFromRedirectUri(redirectedUrl, "code");
|
||||
OAuth2Authorization authorizationCodeAuthorization = this.authorizationService.findByToken(authorizationCode, AUTHORIZATION_CODE_TOKEN_TYPE);
|
||||
OAuth2Authorization authorizationCodeAuthorization = this.authorizationService.findByToken(authorizationCode,
|
||||
AUTHORIZATION_CODE_TOKEN_TYPE);
|
||||
assertThat(authorizationCodeAuthorization).isNotNull();
|
||||
assertThat(authorizationCodeAuthorization.getAuthorizationGrantType()).isEqualTo(AuthorizationGrantType.AUTHORIZATION_CODE);
|
||||
assertThat(authorizationCodeAuthorization.getAuthorizationGrantType())
|
||||
.isEqualTo(AuthorizationGrantType.AUTHORIZATION_CODE);
|
||||
|
||||
this.mvc.perform(post(DEFAULT_TOKEN_ENDPOINT_URI)
|
||||
this.mvc
|
||||
.perform(post(DEFAULT_TOKEN_ENDPOINT_URI)
|
||||
.params(getTokenRequestParameters(registeredClient, authorizationCodeAuthorization))
|
||||
.param(OAuth2ParameterNames.CLIENT_ID, registeredClient.getClientId())
|
||||
.param(PkceParameterNames.CODE_VERIFIER, S256_CODE_VERIFIER))
|
||||
.andExpect(header().string(HttpHeaders.CACHE_CONTROL, containsString("no-store")))
|
||||
.andExpect(header().string(HttpHeaders.PRAGMA, containsString("no-cache")))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.access_token").isNotEmpty())
|
||||
.andExpect(jsonPath("$.token_type").isNotEmpty())
|
||||
.andExpect(jsonPath("$.expires_in").isNotEmpty())
|
||||
.andExpect(jsonPath("$.refresh_token").isNotEmpty())
|
||||
.andExpect(jsonPath("$.scope").isNotEmpty());
|
||||
.andExpect(header().string(HttpHeaders.CACHE_CONTROL, containsString("no-store")))
|
||||
.andExpect(header().string(HttpHeaders.PRAGMA, containsString("no-cache")))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.access_token").isNotEmpty())
|
||||
.andExpect(jsonPath("$.token_type").isNotEmpty())
|
||||
.andExpect(jsonPath("$.expires_in").isNotEmpty())
|
||||
.andExpect(jsonPath("$.refresh_token").isNotEmpty())
|
||||
.andExpect(jsonPath("$.scope").isNotEmpty());
|
||||
|
||||
OAuth2Authorization authorization = this.authorizationService.findById(authorizationCodeAuthorization.getId());
|
||||
assertThat(authorization).isNotNull();
|
||||
assertThat(authorization.getAccessToken()).isNotNull();
|
||||
assertThat(authorization.getRefreshToken()).isNotNull();
|
||||
|
||||
OAuth2Authorization.Token<OAuth2AuthorizationCode> authorizationCodeToken = authorization.getToken(OAuth2AuthorizationCode.class);
|
||||
OAuth2Authorization.Token<OAuth2AuthorizationCode> authorizationCodeToken = authorization
|
||||
.getToken(OAuth2AuthorizationCode.class);
|
||||
assertThat(authorizationCodeToken).isNotNull();
|
||||
assertThat(authorizationCodeToken.getMetadata().get(OAuth2Authorization.Token.INVALIDATED_METADATA_NAME)).isEqualTo(true);
|
||||
assertThat(authorizationCodeToken.getMetadata().get(OAuth2Authorization.Token.INVALIDATED_METADATA_NAME))
|
||||
.isEqualTo(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -1032,7 +1038,8 @@ public class OAuth2AuthorizationCodeGrantTests {
|
||||
|
||||
@EnableWebSecurity
|
||||
@Import(OAuth2AuthorizationServerConfiguration.class)
|
||||
static class AuthorizationServerConfigurationWithCustomRefreshTokenGenerator extends AuthorizationServerConfiguration {
|
||||
static class AuthorizationServerConfigurationWithCustomRefreshTokenGenerator
|
||||
extends AuthorizationServerConfiguration {
|
||||
|
||||
@Bean
|
||||
JwtEncoder jwtEncoder() {
|
||||
@@ -1048,8 +1055,9 @@ public class OAuth2AuthorizationCodeGrantTests {
|
||||
}
|
||||
|
||||
private static final class CustomRefreshTokenGenerator implements OAuth2TokenGenerator<OAuth2RefreshToken> {
|
||||
private final StringKeyGenerator refreshTokenGenerator =
|
||||
new Base64StringKeyGenerator(Base64.getUrlEncoder().withoutPadding(), 96);
|
||||
|
||||
private final StringKeyGenerator refreshTokenGenerator = new Base64StringKeyGenerator(
|
||||
Base64.getUrlEncoder().withoutPadding(), 96);
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
@@ -1058,7 +1066,8 @@ public class OAuth2AuthorizationCodeGrantTests {
|
||||
return null;
|
||||
}
|
||||
Instant issuedAt = Instant.now();
|
||||
Instant expiresAt = issuedAt.plus(context.getRegisteredClient().getTokenSettings().getRefreshTokenTimeToLive());
|
||||
Instant expiresAt = issuedAt
|
||||
.plus(context.getRegisteredClient().getTokenSettings().getRefreshTokenTimeToLive());
|
||||
return new OAuth2RefreshToken(this.refreshTokenGenerator.generateKey(), issuedAt, expiresAt);
|
||||
}
|
||||
|
||||
|
||||
@@ -248,24 +248,24 @@ public class OAuth2RefreshTokenGrantTests {
|
||||
this.spring.register(AuthorizationServerConfigurationWithPublicClientAuthentication.class).autowire();
|
||||
|
||||
RegisteredClient registeredClient = TestRegisteredClients.registeredPublicClient()
|
||||
.authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)
|
||||
.build();
|
||||
.authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)
|
||||
.build();
|
||||
this.registeredClientRepository.save(registeredClient);
|
||||
|
||||
OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient).build();
|
||||
this.authorizationService.save(authorization);
|
||||
|
||||
this.mvc.perform(post(DEFAULT_TOKEN_ENDPOINT_URI)
|
||||
.params(getRefreshTokenRequestParameters(authorization))
|
||||
this.mvc
|
||||
.perform(post(DEFAULT_TOKEN_ENDPOINT_URI).params(getRefreshTokenRequestParameters(authorization))
|
||||
.param(OAuth2ParameterNames.CLIENT_ID, registeredClient.getClientId()))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(header().string(HttpHeaders.CACHE_CONTROL, containsString("no-store")))
|
||||
.andExpect(header().string(HttpHeaders.PRAGMA, containsString("no-cache")))
|
||||
.andExpect(jsonPath("$.access_token").isNotEmpty())
|
||||
.andExpect(jsonPath("$.token_type").isNotEmpty())
|
||||
.andExpect(jsonPath("$.expires_in").isNotEmpty())
|
||||
.andExpect(jsonPath("$.refresh_token").isNotEmpty())
|
||||
.andExpect(jsonPath("$.scope").isNotEmpty());
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(header().string(HttpHeaders.CACHE_CONTROL, containsString("no-store")))
|
||||
.andExpect(header().string(HttpHeaders.PRAGMA, containsString("no-cache")))
|
||||
.andExpect(jsonPath("$.access_token").isNotEmpty())
|
||||
.andExpect(jsonPath("$.token_type").isNotEmpty())
|
||||
.andExpect(jsonPath("$.expires_in").isNotEmpty())
|
||||
.andExpect(jsonPath("$.refresh_token").isNotEmpty())
|
||||
.andExpect(jsonPath("$.scope").isNotEmpty());
|
||||
}
|
||||
|
||||
private static MultiValueMap<String, String> getRefreshTokenRequestParameters(OAuth2Authorization authorization) {
|
||||
@@ -365,7 +365,9 @@ public class OAuth2RefreshTokenGrantTests {
|
||||
|
||||
@EnableWebSecurity
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class AuthorizationServerConfigurationWithPublicClientAuthentication extends AuthorizationServerConfiguration {
|
||||
static class AuthorizationServerConfigurationWithPublicClientAuthentication
|
||||
extends AuthorizationServerConfiguration {
|
||||
|
||||
// @formatter:off
|
||||
@Bean
|
||||
SecurityFilterChain authorizationServerSecurityFilterChain(
|
||||
@@ -393,6 +395,7 @@ public class OAuth2RefreshTokenGrantTests {
|
||||
return http.build();
|
||||
}
|
||||
// @formatter:on
|
||||
|
||||
}
|
||||
|
||||
@Transient
|
||||
@@ -431,6 +434,7 @@ public class OAuth2RefreshTokenGrantTests {
|
||||
}
|
||||
|
||||
private static final class PublicClientRefreshTokenAuthenticationProvider implements AuthenticationProvider {
|
||||
|
||||
private final RegisteredClientRepository registeredClientRepository;
|
||||
|
||||
private PublicClientRefreshTokenAuthenticationProvider(RegisteredClientRepository registeredClientRepository) {
|
||||
@@ -440,8 +444,7 @@ public class OAuth2RefreshTokenGrantTests {
|
||||
|
||||
@Override
|
||||
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
|
||||
PublicClientRefreshTokenAuthenticationToken publicClientAuthentication =
|
||||
(PublicClientRefreshTokenAuthenticationToken) authentication;
|
||||
PublicClientRefreshTokenAuthenticationToken publicClientAuthentication = (PublicClientRefreshTokenAuthenticationToken) authentication;
|
||||
|
||||
if (!ClientAuthenticationMethod.NONE.equals(publicClientAuthentication.getClientAuthenticationMethod())) {
|
||||
return null;
|
||||
@@ -453,8 +456,8 @@ public class OAuth2RefreshTokenGrantTests {
|
||||
throwInvalidClient(OAuth2ParameterNames.CLIENT_ID);
|
||||
}
|
||||
|
||||
if (!registeredClient.getClientAuthenticationMethods().contains(
|
||||
publicClientAuthentication.getClientAuthenticationMethod())) {
|
||||
if (!registeredClient.getClientAuthenticationMethods()
|
||||
.contains(publicClientAuthentication.getClientAuthenticationMethod())) {
|
||||
throwInvalidClient("authentication_method");
|
||||
}
|
||||
|
||||
@@ -467,11 +470,8 @@ public class OAuth2RefreshTokenGrantTests {
|
||||
}
|
||||
|
||||
private static void throwInvalidClient(String parameterName) {
|
||||
OAuth2Error error = new OAuth2Error(
|
||||
OAuth2ErrorCodes.INVALID_CLIENT,
|
||||
"Public client authentication failed: " + parameterName,
|
||||
null
|
||||
);
|
||||
OAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.INVALID_CLIENT,
|
||||
"Public client authentication failed: " + parameterName, null);
|
||||
throw new OAuth2AuthenticationException(error);
|
||||
}
|
||||
|
||||
|
||||
@@ -683,7 +683,7 @@ public class OidcClientRegistrationTests {
|
||||
// @formatter:on
|
||||
|
||||
private Consumer<List<AuthenticationProvider>> configureClientRegistrationConverters() {
|
||||
// @formatter:off
|
||||
// @formatter:off
|
||||
return (authenticationProviders) ->
|
||||
authenticationProviders.forEach(authenticationProvider -> {
|
||||
List<String> supportedCustomClientMetadata = List.of("custom-metadata-name-1", "custom-metadata-name-2");
|
||||
@@ -769,9 +769,11 @@ public class OidcClientRegistrationTests {
|
||||
|
||||
}
|
||||
|
||||
private static class CustomRegisteredClientConverter implements Converter<OidcClientRegistration, RegisteredClient> {
|
||||
private final OidcClientRegistrationRegisteredClientConverter delegate =
|
||||
new OidcClientRegistrationRegisteredClientConverter();
|
||||
private static class CustomRegisteredClientConverter
|
||||
implements Converter<OidcClientRegistration, RegisteredClient> {
|
||||
|
||||
private final OidcClientRegistrationRegisteredClientConverter delegate = new OidcClientRegistrationRegisteredClientConverter();
|
||||
|
||||
private final List<String> supportedCustomClientMetadata;
|
||||
|
||||
private CustomRegisteredClientConverter(List<String> supportedCustomClientMetadata) {
|
||||
@@ -783,7 +785,7 @@ public class OidcClientRegistrationTests {
|
||||
RegisteredClient registeredClient = this.delegate.convert(clientRegistration);
|
||||
|
||||
ClientSettings.Builder clientSettingsBuilder = ClientSettings
|
||||
.withSettings(registeredClient.getClientSettings().getSettings());
|
||||
.withSettings(registeredClient.getClientSettings().getSettings());
|
||||
if (!CollectionUtils.isEmpty(this.supportedCustomClientMetadata)) {
|
||||
clientRegistration.getClaims().forEach((claim, value) -> {
|
||||
if (this.supportedCustomClientMetadata.contains(claim)) {
|
||||
@@ -797,9 +799,11 @@ public class OidcClientRegistrationTests {
|
||||
|
||||
}
|
||||
|
||||
private static class CustomClientRegistrationConverter implements Converter<RegisteredClient, OidcClientRegistration> {
|
||||
private final RegisteredClientOidcClientRegistrationConverter delegate =
|
||||
new RegisteredClientOidcClientRegistrationConverter();
|
||||
private static class CustomClientRegistrationConverter
|
||||
implements Converter<RegisteredClient, OidcClientRegistration> {
|
||||
|
||||
private final RegisteredClientOidcClientRegistrationConverter delegate = new RegisteredClientOidcClientRegistrationConverter();
|
||||
|
||||
private final List<String> supportedCustomClientMetadata;
|
||||
|
||||
private CustomClientRegistrationConverter(List<String> supportedCustomClientMetadata) {
|
||||
@@ -822,6 +826,7 @@ public class OidcClientRegistrationTests {
|
||||
|
||||
return OidcClientRegistration.withClaims(clientMetadata).build();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -472,81 +472,85 @@ public class OidcTests {
|
||||
|
||||
// gh-1422
|
||||
@Test
|
||||
public void requestWhenAuthenticationRequestWithOfflineAccessScopeThenTokenResponseIncludesRefreshToken() throws Exception {
|
||||
public void requestWhenAuthenticationRequestWithOfflineAccessScopeThenTokenResponseIncludesRefreshToken()
|
||||
throws Exception {
|
||||
this.spring.register(AuthorizationServerConfigurationWithCustomRefreshTokenGenerator.class).autowire();
|
||||
|
||||
RegisteredClient registeredClient = TestRegisteredClients.registeredClient()
|
||||
.scope(OidcScopes.OPENID)
|
||||
.scope("offline_access")
|
||||
.build();
|
||||
.scope(OidcScopes.OPENID)
|
||||
.scope("offline_access")
|
||||
.build();
|
||||
this.registeredClientRepository.save(registeredClient);
|
||||
|
||||
MultiValueMap<String, String> authorizationRequestParameters = getAuthorizationRequestParameters(registeredClient);
|
||||
MvcResult mvcResult = this.mvc.perform(get(DEFAULT_AUTHORIZATION_ENDPOINT_URI)
|
||||
.queryParams(authorizationRequestParameters)
|
||||
.with(user("user")))
|
||||
.andExpect(status().is3xxRedirection())
|
||||
.andReturn();
|
||||
MultiValueMap<String, String> authorizationRequestParameters = getAuthorizationRequestParameters(
|
||||
registeredClient);
|
||||
MvcResult mvcResult = this.mvc
|
||||
.perform(get(DEFAULT_AUTHORIZATION_ENDPOINT_URI).queryParams(authorizationRequestParameters)
|
||||
.with(user("user")))
|
||||
.andExpect(status().is3xxRedirection())
|
||||
.andReturn();
|
||||
String redirectedUrl = mvcResult.getResponse().getRedirectedUrl();
|
||||
String expectedRedirectUri = authorizationRequestParameters.getFirst(OAuth2ParameterNames.REDIRECT_URI);
|
||||
assertThat(redirectedUrl).matches(expectedRedirectUri + "\\?code=.{15,}&state=state");
|
||||
|
||||
String authorizationCode = extractParameterFromRedirectUri(redirectedUrl, "code");
|
||||
OAuth2Authorization authorization = this.authorizationService.findByToken(authorizationCode, AUTHORIZATION_CODE_TOKEN_TYPE);
|
||||
OAuth2Authorization authorization = this.authorizationService.findByToken(authorizationCode,
|
||||
AUTHORIZATION_CODE_TOKEN_TYPE);
|
||||
|
||||
this.mvc.perform(post(DEFAULT_TOKEN_ENDPOINT_URI)
|
||||
.params(getTokenRequestParameters(registeredClient, authorization))
|
||||
.header(HttpHeaders.AUTHORIZATION, "Basic " + encodeBasicAuth(
|
||||
registeredClient.getClientId(), registeredClient.getClientSecret())))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(header().string(HttpHeaders.CACHE_CONTROL, containsString("no-store")))
|
||||
.andExpect(header().string(HttpHeaders.PRAGMA, containsString("no-cache")))
|
||||
.andExpect(jsonPath("$.access_token").isNotEmpty())
|
||||
.andExpect(jsonPath("$.token_type").isNotEmpty())
|
||||
.andExpect(jsonPath("$.expires_in").isNotEmpty())
|
||||
.andExpect(jsonPath("$.refresh_token").isNotEmpty())
|
||||
.andExpect(jsonPath("$.scope").isNotEmpty())
|
||||
.andExpect(jsonPath("$.id_token").isNotEmpty())
|
||||
.andReturn();
|
||||
this.mvc
|
||||
.perform(post(DEFAULT_TOKEN_ENDPOINT_URI).params(getTokenRequestParameters(registeredClient, authorization))
|
||||
.header(HttpHeaders.AUTHORIZATION,
|
||||
"Basic " + encodeBasicAuth(registeredClient.getClientId(), registeredClient.getClientSecret())))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(header().string(HttpHeaders.CACHE_CONTROL, containsString("no-store")))
|
||||
.andExpect(header().string(HttpHeaders.PRAGMA, containsString("no-cache")))
|
||||
.andExpect(jsonPath("$.access_token").isNotEmpty())
|
||||
.andExpect(jsonPath("$.token_type").isNotEmpty())
|
||||
.andExpect(jsonPath("$.expires_in").isNotEmpty())
|
||||
.andExpect(jsonPath("$.refresh_token").isNotEmpty())
|
||||
.andExpect(jsonPath("$.scope").isNotEmpty())
|
||||
.andExpect(jsonPath("$.id_token").isNotEmpty())
|
||||
.andReturn();
|
||||
}
|
||||
|
||||
// gh-1422
|
||||
@Test
|
||||
public void requestWhenAuthenticationRequestWithoutOfflineAccessScopeThenTokenResponseDoesNotIncludeRefreshToken() throws Exception {
|
||||
public void requestWhenAuthenticationRequestWithoutOfflineAccessScopeThenTokenResponseDoesNotIncludeRefreshToken()
|
||||
throws Exception {
|
||||
this.spring.register(AuthorizationServerConfigurationWithCustomRefreshTokenGenerator.class).autowire();
|
||||
|
||||
RegisteredClient registeredClient = TestRegisteredClients.registeredClient()
|
||||
.scope(OidcScopes.OPENID)
|
||||
.build();
|
||||
RegisteredClient registeredClient = TestRegisteredClients.registeredClient().scope(OidcScopes.OPENID).build();
|
||||
this.registeredClientRepository.save(registeredClient);
|
||||
|
||||
MultiValueMap<String, String> authorizationRequestParameters = getAuthorizationRequestParameters(registeredClient);
|
||||
MvcResult mvcResult = this.mvc.perform(get(DEFAULT_AUTHORIZATION_ENDPOINT_URI)
|
||||
.queryParams(authorizationRequestParameters)
|
||||
.with(user("user")))
|
||||
.andExpect(status().is3xxRedirection())
|
||||
.andReturn();
|
||||
MultiValueMap<String, String> authorizationRequestParameters = getAuthorizationRequestParameters(
|
||||
registeredClient);
|
||||
MvcResult mvcResult = this.mvc
|
||||
.perform(get(DEFAULT_AUTHORIZATION_ENDPOINT_URI).queryParams(authorizationRequestParameters)
|
||||
.with(user("user")))
|
||||
.andExpect(status().is3xxRedirection())
|
||||
.andReturn();
|
||||
String redirectedUrl = mvcResult.getResponse().getRedirectedUrl();
|
||||
String expectedRedirectUri = authorizationRequestParameters.getFirst(OAuth2ParameterNames.REDIRECT_URI);
|
||||
assertThat(redirectedUrl).matches(expectedRedirectUri + "\\?code=.{15,}&state=state");
|
||||
|
||||
String authorizationCode = extractParameterFromRedirectUri(redirectedUrl, "code");
|
||||
OAuth2Authorization authorization = this.authorizationService.findByToken(authorizationCode, AUTHORIZATION_CODE_TOKEN_TYPE);
|
||||
OAuth2Authorization authorization = this.authorizationService.findByToken(authorizationCode,
|
||||
AUTHORIZATION_CODE_TOKEN_TYPE);
|
||||
|
||||
this.mvc.perform(post(DEFAULT_TOKEN_ENDPOINT_URI)
|
||||
.params(getTokenRequestParameters(registeredClient, authorization))
|
||||
.header(HttpHeaders.AUTHORIZATION, "Basic " + encodeBasicAuth(
|
||||
registeredClient.getClientId(), registeredClient.getClientSecret())))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(header().string(HttpHeaders.CACHE_CONTROL, containsString("no-store")))
|
||||
.andExpect(header().string(HttpHeaders.PRAGMA, containsString("no-cache")))
|
||||
.andExpect(jsonPath("$.access_token").isNotEmpty())
|
||||
.andExpect(jsonPath("$.token_type").isNotEmpty())
|
||||
.andExpect(jsonPath("$.expires_in").isNotEmpty())
|
||||
.andExpect(jsonPath("$.refresh_token").doesNotExist())
|
||||
.andExpect(jsonPath("$.scope").isNotEmpty())
|
||||
.andExpect(jsonPath("$.id_token").isNotEmpty())
|
||||
.andReturn();
|
||||
this.mvc
|
||||
.perform(post(DEFAULT_TOKEN_ENDPOINT_URI).params(getTokenRequestParameters(registeredClient, authorization))
|
||||
.header(HttpHeaders.AUTHORIZATION,
|
||||
"Basic " + encodeBasicAuth(registeredClient.getClientId(), registeredClient.getClientSecret())))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(header().string(HttpHeaders.CACHE_CONTROL, containsString("no-store")))
|
||||
.andExpect(header().string(HttpHeaders.PRAGMA, containsString("no-cache")))
|
||||
.andExpect(jsonPath("$.access_token").isNotEmpty())
|
||||
.andExpect(jsonPath("$.token_type").isNotEmpty())
|
||||
.andExpect(jsonPath("$.expires_in").isNotEmpty())
|
||||
.andExpect(jsonPath("$.refresh_token").doesNotExist())
|
||||
.andExpect(jsonPath("$.scope").isNotEmpty())
|
||||
.andExpect(jsonPath("$.id_token").isNotEmpty())
|
||||
.andReturn();
|
||||
}
|
||||
|
||||
private static MultiValueMap<String, String> getAuthorizationRequestParameters(RegisteredClient registeredClient) {
|
||||
@@ -728,7 +732,8 @@ public class OidcTests {
|
||||
|
||||
@EnableWebSecurity
|
||||
@Configuration
|
||||
static class AuthorizationServerConfigurationWithCustomRefreshTokenGenerator extends AuthorizationServerConfiguration {
|
||||
static class AuthorizationServerConfigurationWithCustomRefreshTokenGenerator
|
||||
extends AuthorizationServerConfiguration {
|
||||
|
||||
// @formatter:off
|
||||
@Bean
|
||||
@@ -763,13 +768,14 @@ public class OidcTests {
|
||||
}
|
||||
|
||||
private static final class CustomRefreshTokenGenerator implements OAuth2TokenGenerator<OAuth2RefreshToken> {
|
||||
|
||||
private final OAuth2RefreshTokenGenerator delegate = new OAuth2RefreshTokenGenerator();
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public OAuth2RefreshToken generate(OAuth2TokenContext context) {
|
||||
if (context.getAuthorizedScopes().contains(OidcScopes.OPENID) &&
|
||||
!context.getAuthorizedScopes().contains("offline_access")) {
|
||||
if (context.getAuthorizedScopes().contains(OidcScopes.OPENID)
|
||||
&& !context.getAuthorizedScopes().contains("offline_access")) {
|
||||
return null;
|
||||
}
|
||||
return this.delegate.generate(context);
|
||||
|
||||
@@ -119,8 +119,8 @@ public class OidcClientConfigurationAuthenticationProviderTests {
|
||||
@Test
|
||||
public void setClientRegistrationConverterWhenNullThenThrowIllegalArgumentException() {
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> this.authenticationProvider.setClientRegistrationConverter(null))
|
||||
.withMessage("clientRegistrationConverter cannot be null");
|
||||
.isThrownBy(() -> this.authenticationProvider.setClientRegistrationConverter(null))
|
||||
.withMessage("clientRegistrationConverter cannot be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -172,8 +172,8 @@ public class OidcClientRegistrationAuthenticationProviderTests {
|
||||
@Test
|
||||
public void setClientRegistrationConverterWhenNullThenThrowIllegalArgumentException() {
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> this.authenticationProvider.setClientRegistrationConverter(null))
|
||||
.withMessage("clientRegistrationConverter cannot be null");
|
||||
.isThrownBy(() -> this.authenticationProvider.setClientRegistrationConverter(null))
|
||||
.withMessage("clientRegistrationConverter cannot be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -131,11 +131,16 @@ public class OidcProviderConfigurationEndpointFilterTests {
|
||||
assertThat(providerConfigurationResponse).contains("\"jwks_uri\":\"https://example.com/oauth2/v1/jwks\"");
|
||||
assertThat(providerConfigurationResponse).contains("\"scopes_supported\":[\"openid\"]");
|
||||
assertThat(providerConfigurationResponse).contains("\"response_types_supported\":[\"code\"]");
|
||||
assertThat(providerConfigurationResponse).contains("\"grant_types_supported\":[\"authorization_code\",\"client_credentials\",\"refresh_token\",\"urn:ietf:params:oauth:grant-type:device_code\"]");
|
||||
assertThat(providerConfigurationResponse).contains("\"revocation_endpoint\":\"https://example.com/oauth2/v1/revoke\"");
|
||||
assertThat(providerConfigurationResponse).contains("\"revocation_endpoint_auth_methods_supported\":[\"client_secret_basic\",\"client_secret_post\",\"client_secret_jwt\",\"private_key_jwt\"]");
|
||||
assertThat(providerConfigurationResponse).contains("\"introspection_endpoint\":\"https://example.com/oauth2/v1/introspect\"");
|
||||
assertThat(providerConfigurationResponse).contains("\"introspection_endpoint_auth_methods_supported\":[\"client_secret_basic\",\"client_secret_post\",\"client_secret_jwt\",\"private_key_jwt\"]");
|
||||
assertThat(providerConfigurationResponse).contains(
|
||||
"\"grant_types_supported\":[\"authorization_code\",\"client_credentials\",\"refresh_token\",\"urn:ietf:params:oauth:grant-type:device_code\"]");
|
||||
assertThat(providerConfigurationResponse)
|
||||
.contains("\"revocation_endpoint\":\"https://example.com/oauth2/v1/revoke\"");
|
||||
assertThat(providerConfigurationResponse).contains(
|
||||
"\"revocation_endpoint_auth_methods_supported\":[\"client_secret_basic\",\"client_secret_post\",\"client_secret_jwt\",\"private_key_jwt\"]");
|
||||
assertThat(providerConfigurationResponse)
|
||||
.contains("\"introspection_endpoint\":\"https://example.com/oauth2/v1/introspect\"");
|
||||
assertThat(providerConfigurationResponse).contains(
|
||||
"\"introspection_endpoint_auth_methods_supported\":[\"client_secret_basic\",\"client_secret_post\",\"client_secret_jwt\",\"private_key_jwt\"]");
|
||||
assertThat(providerConfigurationResponse).contains("\"code_challenge_methods_supported\":[\"S256\"]");
|
||||
assertThat(providerConfigurationResponse).contains("\"subject_types_supported\":[\"public\"]");
|
||||
assertThat(providerConfigurationResponse).contains("\"id_token_signing_alg_values_supported\":[\"RS256\"]");
|
||||
|
||||
@@ -39,6 +39,7 @@ import static org.mockito.Mockito.verifyNoInteractions;
|
||||
* @author Dmitriy Dubson
|
||||
*/
|
||||
public class OAuth2ErrorAuthenticationFailureHandlerTests {
|
||||
|
||||
private final OAuth2ErrorAuthenticationFailureHandler authenticationFailureHandler = new OAuth2ErrorAuthenticationFailureHandler();
|
||||
|
||||
@Test
|
||||
|
||||
Reference in New Issue
Block a user