From ff8002eb2ededb5680811b8c739f210596eff947 Mon Sep 17 00:00:00 2001 From: Joe Grandja Date: Tue, 11 Feb 2020 04:20:59 -0500 Subject: [PATCH] Polish gh-4557 --- .../http/AuthenticationConfigBuilder.java | 75 ++- .../http/OAuth2LoginBeanDefinitionParser.java | 174 +++--- .../security/config/http/SecurityFilters.java | 2 +- ...ientRegistrationsBeanDefinitionParser.java | 160 +++--- .../security/config/spring-security-5.3.rnc | 32 +- .../security/config/spring-security-5.3.xsd | 32 +- .../OAuth2LoginBeanDefinitionParserTests.java | 505 +++++++----------- ...egistrationsBeanDefinitionParserTests.java | 183 +++---- ...tRegistration-WithCustomConfiguration.xml} | 18 +- ...stration-WithCustomGrantedAuthorities.xml} | 18 +- ...stration-WithCustomLoginProcessingUrl.xml} | 12 +- ...WithCustomAuthenticationFailureHandler.xml | 2 +- ...WithCustomAuthorizationRequestResolver.xml | 4 +- ...ientRegistration-WithJwtDecoderFactory.xml | 14 +- ...DecoderFactoryAndDefaultSuccessHandler.xml | 19 +- ...ientRegistration-WithTestConfiguration.xml | 57 -- ...ingleClientRegistration-WithinSameFile.xml | 52 -- ...onParserTests-SingleClientRegistration.xml | 25 +- ...thCustomAuthorizationRequestRepository.xml | 49 -- ...s-WithCustomAuthorizedClientRepository.xml | 10 +- ...ests-WithCustomAuthorizedClientService.xml | 10 +- ...WithCustomClientRegistrationRepository.xml | 9 +- ...eanDefinitionParserTests-FromIssuerUri.xml | 39 -- .../client/google-github-registration.xml | 2 +- .../_includes/servlet/appendix/namespace.adoc | 28 +- .../TestOAuth2AccessTokenResponses.java | 13 +- .../TestOAuth2AuthorizationRequests.java | 6 +- 27 files changed, 618 insertions(+), 932 deletions(-) rename config/src/test/resources/org/springframework/security/config/http/{OAuth2LoginBeanDefinitionParserTests-SingleClientRegistration-WithCustomAuthenticationHandler.xml => OAuth2LoginBeanDefinitionParserTests-MultiClientRegistration-WithCustomConfiguration.xml} (76%) rename config/src/test/resources/org/springframework/security/config/http/{OAuth2LoginBeanDefinitionParserTests-SingleClientRegistration-WithCustomGrantedAuthorities.xml => OAuth2LoginBeanDefinitionParserTests-MultiClientRegistration-WithCustomGrantedAuthorities.xml} (78%) rename config/src/test/resources/org/springframework/security/config/http/{OAuth2LoginBeanDefinitionParserTests-SingleClientRegistration-WithCustomLoginProcessingUrl.xml => OAuth2LoginBeanDefinitionParserTests-MultiClientRegistration-WithCustomLoginProcessingUrl.xml} (83%) delete mode 100644 config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-SingleClientRegistration-WithTestConfiguration.xml delete mode 100644 config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-SingleClientRegistration-WithinSameFile.xml delete mode 100644 config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-WithCustomAuthorizationRequestRepository.xml delete mode 100644 config/src/test/resources/org/springframework/security/config/oauth2/client/ClientRegistrationsBeanDefinitionParserTests-FromIssuerUri.xml diff --git a/config/src/main/java/org/springframework/security/config/http/AuthenticationConfigBuilder.java b/config/src/main/java/org/springframework/security/config/http/AuthenticationConfigBuilder.java index 9a89bff873..8152497c97 100644 --- a/config/src/main/java/org/springframework/security/config/http/AuthenticationConfigBuilder.java +++ b/config/src/main/java/org/springframework/security/config/http/AuthenticationConfigBuilder.java @@ -139,12 +139,10 @@ final class AuthenticationConfigBuilder { private String openIDLoginPage; private String oauth2LoginFilterId; - private String oauth2AuthorizationRequestRedirectFilterId; private BeanDefinition oauth2AuthorizationRequestRedirectFilter; private BeanDefinition oauth2LoginEntryPoint; private BeanReference oauth2LoginAuthenticationProviderRef; private BeanReference oauth2LoginOidcAuthenticationProviderRef; - private BeanDefinition oauth2LoginLinks; AuthenticationConfigBuilder(Element element, boolean forceAutoConfig, @@ -247,43 +245,42 @@ final class AuthenticationConfigBuilder { void createOAuth2LoginFilter(BeanReference sessionStrategy, BeanReference authManager) { Element oauth2LoginElt = DomUtils.getChildElementByTagName(this.httpElt, Elements.OAUTH2_LOGIN); - if (oauth2LoginElt != null) { - OAuth2LoginBeanDefinitionParser parser = new OAuth2LoginBeanDefinitionParser(requestCache, portMapper, - portResolver, sessionStrategy, allowSessionCreation); - BeanDefinition oauth2LoginFilterBean = parser.parse(oauth2LoginElt, this.pc); - oauth2LoginFilterBean.getPropertyValues().addPropertyValue("authenticationManager", authManager); - - // retrieve the other bean result - BeanDefinition oauth2LoginAuthProvider = parser.getOAuth2LoginAuthenticationProvider(); - oauth2AuthorizationRequestRedirectFilter = parser.getOAuth2AuthorizationRequestRedirectFilter(); - oauth2LoginEntryPoint = parser.getOAuth2LoginAuthenticationEntryPoint(); - - // generate bean name to be registered - String oauth2LoginAuthenticationProviderId = pc.getReaderContext() - .generateBeanName(oauth2LoginAuthProvider); - oauth2LoginFilterId = pc.getReaderContext().generateBeanName(oauth2LoginFilterBean); - oauth2AuthorizationRequestRedirectFilterId = pc.getReaderContext() - .generateBeanName(oauth2AuthorizationRequestRedirectFilter); - oauth2LoginLinks = parser.getOAuth2LoginLinks(); - - // register the component - pc.registerBeanComponent(new BeanComponentDefinition(oauth2AuthorizationRequestRedirectFilter, - oauth2AuthorizationRequestRedirectFilterId)); - pc.registerBeanComponent(new BeanComponentDefinition(oauth2LoginFilterBean, oauth2LoginFilterId)); - pc.registerBeanComponent( - new BeanComponentDefinition(oauth2LoginAuthProvider, oauth2LoginAuthenticationProviderId)); - - oauth2LoginAuthenticationProviderRef = new RuntimeBeanReference(oauth2LoginAuthenticationProviderId); - - // oidc provider - BeanDefinition oauth2LoginOidcAuthProvider = parser.getOAuth2LoginOidcAuthenticationProvider(); - String oauth2LoginOidcAuthenticationProviderId = pc.getReaderContext() - .generateBeanName(oauth2LoginOidcAuthProvider); - pc.registerBeanComponent( - new BeanComponentDefinition(oauth2LoginOidcAuthProvider, oauth2LoginOidcAuthenticationProviderId)); - oauth2LoginOidcAuthenticationProviderRef = new RuntimeBeanReference( - oauth2LoginOidcAuthenticationProviderId); + if (oauth2LoginElt == null) { + return; } + + OAuth2LoginBeanDefinitionParser parser = new OAuth2LoginBeanDefinitionParser(requestCache, portMapper, + portResolver, sessionStrategy, allowSessionCreation); + BeanDefinition oauth2LoginFilterBean = parser.parse(oauth2LoginElt, this.pc); + oauth2LoginFilterBean.getPropertyValues().addPropertyValue("authenticationManager", authManager); + + // retrieve the other bean result + BeanDefinition oauth2LoginAuthProvider = parser.getOAuth2LoginAuthenticationProvider(); + oauth2AuthorizationRequestRedirectFilter = parser.getOAuth2AuthorizationRequestRedirectFilter(); + oauth2LoginEntryPoint = parser.getOAuth2LoginAuthenticationEntryPoint(); + + // generate bean name to be registered + String oauth2LoginAuthProviderId = pc.getReaderContext() + .generateBeanName(oauth2LoginAuthProvider); + oauth2LoginFilterId = pc.getReaderContext().generateBeanName(oauth2LoginFilterBean); + String oauth2AuthorizationRequestRedirectFilterId = pc.getReaderContext() + .generateBeanName(oauth2AuthorizationRequestRedirectFilter); + oauth2LoginLinks = parser.getOAuth2LoginLinks(); + + // register the component + pc.registerBeanComponent(new BeanComponentDefinition(oauth2LoginFilterBean, oauth2LoginFilterId)); + pc.registerBeanComponent(new BeanComponentDefinition( + oauth2AuthorizationRequestRedirectFilter, oauth2AuthorizationRequestRedirectFilterId)); + pc.registerBeanComponent(new BeanComponentDefinition(oauth2LoginAuthProvider, oauth2LoginAuthProviderId)); + + oauth2LoginAuthenticationProviderRef = new RuntimeBeanReference(oauth2LoginAuthProviderId); + + // oidc provider + BeanDefinition oauth2LoginOidcAuthProvider = parser.getOAuth2LoginOidcAuthenticationProvider(); + String oauth2LoginOidcAuthProviderId = pc.getReaderContext().generateBeanName(oauth2LoginOidcAuthProvider); + pc.registerBeanComponent(new BeanComponentDefinition( + oauth2LoginOidcAuthProvider, oauth2LoginOidcAuthProviderId)); + oauth2LoginOidcAuthenticationProviderRef = new RuntimeBeanReference(oauth2LoginOidcAuthProviderId); } void createOpenIDLoginFilter(BeanReference sessionStrategy, BeanReference authManager) { @@ -870,7 +867,7 @@ final class AuthenticationConfigBuilder { if (oauth2LoginFilterId != null) { filters.add(new OrderDecorator(new RuntimeBeanReference(oauth2LoginFilterId), OAUTH2_LOGIN_FILTER)); - filters.add(new OrderDecorator(oauth2AuthorizationRequestRedirectFilter, OAUTH2_REDIRECT_FILTER)); + filters.add(new OrderDecorator(oauth2AuthorizationRequestRedirectFilter, OAUTH2_AUTHORIZATION_REQUEST_FILTER)); } if (openIDFilterId != null) { diff --git a/config/src/main/java/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParser.java b/config/src/main/java/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParser.java index 255b0083b3..0d2c7c4410 100644 --- a/config/src/main/java/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParser.java +++ b/config/src/main/java/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParser.java @@ -92,7 +92,7 @@ final class OAuth2LoginBeanDefinitionParser implements BeanDefinitionParser { private static final String ATT_AUTHENTICATION_FAILURE_HANDLER_REF = "authentication-failure-handler-ref"; private static final String ATT_JWT_DECODER_FACTORY_REF = "jwt-decoder-factory-ref"; - private BeanReference requestCache; + private final BeanReference requestCache; private final BeanReference portMapper; private final BeanReference portResolver; private final BeanReference sessionStrategy; @@ -123,8 +123,8 @@ final class OAuth2LoginBeanDefinitionParser implements BeanDefinitionParser { BeanDefinition oauth2LoginBeanConfig = BeanDefinitionBuilder.rootBeanDefinition(OAuth2LoginBeanConfig.class) .getBeanDefinition(); String oauth2LoginBeanConfigId = parserContext.getReaderContext().generateBeanName(oauth2LoginBeanConfig); - parserContext - .registerBeanComponent(new BeanComponentDefinition(oauth2LoginBeanConfig, oauth2LoginBeanConfigId)); + parserContext.registerBeanComponent( + new BeanComponentDefinition(oauth2LoginBeanConfig, oauth2LoginBeanConfigId)); // configure filter BeanMetadataElement clientRegistrationRepository = getClientRegistrationRepository(element); @@ -132,12 +132,13 @@ final class OAuth2LoginBeanDefinitionParser implements BeanDefinitionParser { clientRegistrationRepository); BeanMetadataElement accessTokenResponseClient = getAccessTokenResponseClient(element); BeanMetadataElement oauth2UserService = getOAuth2UserService(element); - BeanMetadataElement oauth2AuthRequestRepository = getOAuth2AuthorizationRequestRepository(element); + BeanMetadataElement authorizationRequestRepository = getAuthorizationRequestRepository(element); BeanDefinitionBuilder oauth2LoginAuthenticationFilterBuilder = BeanDefinitionBuilder .rootBeanDefinition(OAuth2LoginAuthenticationFilter.class) - .addConstructorArgValue(clientRegistrationRepository).addConstructorArgValue(authorizedClientRepository) - .addPropertyValue("authorizationRequestRepository", oauth2AuthRequestRepository); + .addConstructorArgValue(clientRegistrationRepository) + .addConstructorArgValue(authorizedClientRepository) + .addPropertyValue("authorizationRequestRepository", authorizationRequestRepository); if (sessionStrategy != null) { oauth2LoginAuthenticationFilterBuilder.addPropertyValue("sessionAuthenticationStrategy", sessionStrategy); @@ -145,8 +146,8 @@ final class OAuth2LoginBeanDefinitionParser implements BeanDefinitionParser { Object source = parserContext.extractSource(element); String loginProcessingUrl = element.getAttribute(ATT_LOGIN_PROCESSING_URL); - WebConfigUtils.validateHttpRedirect(loginProcessingUrl, parserContext, source); if (!StringUtils.isEmpty(loginProcessingUrl)) { + WebConfigUtils.validateHttpRedirect(loginProcessingUrl, parserContext, source); oauth2LoginAuthenticationFilterBuilder.addConstructorArgValue(loginProcessingUrl); } else { oauth2LoginAuthenticationFilterBuilder @@ -155,31 +156,32 @@ final class OAuth2LoginBeanDefinitionParser implements BeanDefinitionParser { BeanDefinitionBuilder oauth2LoginAuthenticationProviderBuilder = BeanDefinitionBuilder .rootBeanDefinition(OAuth2LoginAuthenticationProvider.class) - .addConstructorArgValue(accessTokenResponseClient).addConstructorArgValue(oauth2UserService); + .addConstructorArgValue(accessTokenResponseClient) + .addConstructorArgValue(oauth2UserService); - String oauth2UserAuthMapperRef = element.getAttribute(ATT_USER_AUTHORITIES_MAPPER_REF); - if (!StringUtils.isEmpty(oauth2UserAuthMapperRef)) { - oauth2LoginAuthenticationProviderBuilder.addPropertyReference("authoritiesMapper", oauth2UserAuthMapperRef); + String userAuthoritiesMapperRef = element.getAttribute(ATT_USER_AUTHORITIES_MAPPER_REF); + if (!StringUtils.isEmpty(userAuthoritiesMapperRef)) { + oauth2LoginAuthenticationProviderBuilder.addPropertyReference("authoritiesMapper", userAuthoritiesMapperRef); } oauth2LoginAuthenticationProvider = oauth2LoginAuthenticationProviderBuilder.getBeanDefinition(); - oauth2LoginOidcAuthenticationProvider = getOAuth2OidcAuthProvider(element, accessTokenResponseClient, - oauth2UserAuthMapperRef, parserContext); + oauth2LoginOidcAuthenticationProvider = getOidcAuthProvider( + element, accessTokenResponseClient, userAuthoritiesMapperRef); BeanDefinitionBuilder oauth2AuthorizationRequestRedirectFilterBuilder = BeanDefinitionBuilder .rootBeanDefinition(OAuth2AuthorizationRequestRedirectFilter.class); - String oauth2AuthorizationRequestResolverRef = element.getAttribute(ATT_AUTHORIZATION_REQUEST_RESOLVER_REF); - if (!StringUtils.isEmpty(oauth2AuthorizationRequestResolverRef)) { + String authorizationRequestResolverRef = element.getAttribute(ATT_AUTHORIZATION_REQUEST_RESOLVER_REF); + if (!StringUtils.isEmpty(authorizationRequestResolverRef)) { oauth2AuthorizationRequestRedirectFilterBuilder - .addConstructorArgReference(oauth2AuthorizationRequestResolverRef); + .addConstructorArgReference(authorizationRequestResolverRef); } else { oauth2AuthorizationRequestRedirectFilterBuilder.addConstructorArgValue(clientRegistrationRepository); } oauth2AuthorizationRequestRedirectFilterBuilder - .addPropertyValue("authorizationRequestRepository", oauth2AuthRequestRepository) + .addPropertyValue("authorizationRequestRepository", authorizationRequestRepository) .addPropertyValue("requestCache", requestCache); oauth2AuthorizationRequestRedirectFilter = oauth2AuthorizationRequestRedirectFilterBuilder.getBeanDefinition(); @@ -187,21 +189,29 @@ final class OAuth2LoginBeanDefinitionParser implements BeanDefinitionParser { if (!StringUtils.isEmpty(authenticationSuccessHandlerRef)) { oauth2LoginAuthenticationFilterBuilder.addPropertyReference("authenticationSuccessHandler", authenticationSuccessHandlerRef); + } else { + BeanDefinitionBuilder successHandlerBuilder = BeanDefinitionBuilder.rootBeanDefinition( + "org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler") + .addPropertyValue("requestCache", requestCache); + oauth2LoginAuthenticationFilterBuilder.addPropertyValue("authenticationSuccessHandler", + successHandlerBuilder.getBeanDefinition()); } String loginPage = element.getAttribute(ATT_LOGIN_PAGE); - WebConfigUtils.validateHttpRedirect(loginPage, parserContext, source); if (!StringUtils.isEmpty(loginPage)) { + WebConfigUtils.validateHttpRedirect(loginPage, parserContext, source); oauth2LoginAuthenticationEntryPoint = BeanDefinitionBuilder - .rootBeanDefinition(LoginUrlAuthenticationEntryPoint.class).addConstructorArgValue(loginPage) - .addPropertyValue("portMapper", portMapper).addPropertyValue("portResolver", portResolver) + .rootBeanDefinition(LoginUrlAuthenticationEntryPoint.class) + .addConstructorArgValue(loginPage) + .addPropertyValue("portMapper", portMapper) + .addPropertyValue("portResolver", portResolver) .getBeanDefinition(); } else { - Map entryPoint = getLoginEntryPoint(element, parserContext); - + Map entryPoint = getLoginEntryPoint(element); if (entryPoint != null) { oauth2LoginAuthenticationEntryPoint = BeanDefinitionBuilder - .rootBeanDefinition(DelegatingAuthenticationEntryPoint.class).addConstructorArgValue(entryPoint) + .rootBeanDefinition(DelegatingAuthenticationEntryPoint.class) + .addConstructorArgValue(entryPoint) .addPropertyValue("defaultEntryPoint", new LoginUrlAuthenticationEntryPoint(DEFAULT_LOGIN_URI)) .getBeanDefinition(); } @@ -217,7 +227,6 @@ final class OAuth2LoginBeanDefinitionParser implements BeanDefinitionParser { failureHandlerBuilder.addConstructorArgValue( DEFAULT_LOGIN_URI + "?" + DefaultLoginPageGeneratingFilter.ERROR_PARAMETER_NAME); failureHandlerBuilder.addPropertyValue("allowSessionCreation", allowSessionCreation); - oauth2LoginAuthenticationFilterBuilder.addPropertyValue("authenticationFailureHandler", failureHandlerBuilder.getBeanDefinition()); } @@ -229,49 +238,45 @@ final class OAuth2LoginBeanDefinitionParser implements BeanDefinitionParser { return oauth2LoginAuthenticationFilterBuilder.getBeanDefinition(); } - private BeanMetadataElement getOAuth2AuthorizationRequestRepository(Element element) { - BeanMetadataElement oauth2AuthRequestRepository = null; - String oauth2AuthRequestRepositoryRef = element.getAttribute(ATT_AUTHORIZATION_REQUEST_REPOSITORY_REF); - if (!StringUtils.isEmpty(oauth2AuthRequestRepositoryRef)) { - oauth2AuthRequestRepository = new RuntimeBeanReference(oauth2AuthRequestRepositoryRef); + private BeanMetadataElement getAuthorizationRequestRepository(Element element) { + BeanMetadataElement authorizationRequestRepository; + String authorizationRequestRepositoryRef = element.getAttribute(ATT_AUTHORIZATION_REQUEST_REPOSITORY_REF); + if (!StringUtils.isEmpty(authorizationRequestRepositoryRef)) { + authorizationRequestRepository = new RuntimeBeanReference(authorizationRequestRepositoryRef); } else { - oauth2AuthRequestRepository = BeanDefinitionBuilder.rootBeanDefinition( + authorizationRequestRepository = BeanDefinitionBuilder.rootBeanDefinition( "org.springframework.security.oauth2.client.web.HttpSessionOAuth2AuthorizationRequestRepository") .getBeanDefinition(); } - return oauth2AuthRequestRepository; + return authorizationRequestRepository; } private BeanMetadataElement getAuthorizedClientRepository(Element element, BeanMetadataElement clientRegistrationRepository) { - BeanMetadataElement authorizedClientRepository = null; - + BeanMetadataElement authorizedClientRepository; String authorizedClientRepositoryRef = element.getAttribute(ATT_AUTHORIZED_CLIENT_REPOSITORY_REF); if (!StringUtils.isEmpty(authorizedClientRepositoryRef)) { authorizedClientRepository = new RuntimeBeanReference(authorizedClientRepositoryRef); } else { - BeanMetadataElement oauth2AuthorizedClientService = null; + BeanMetadataElement authorizedClientService; String authorizedClientServiceRef = element.getAttribute(ATT_AUTHORIZED_CLIENT_SERVICE_REF); if (!StringUtils.isEmpty(authorizedClientServiceRef)) { - oauth2AuthorizedClientService = new RuntimeBeanReference(authorizedClientServiceRef); + authorizedClientService = new RuntimeBeanReference(authorizedClientServiceRef); } else { - oauth2AuthorizedClientService = BeanDefinitionBuilder + authorizedClientService = BeanDefinitionBuilder .rootBeanDefinition( "org.springframework.security.oauth2.client.InMemoryOAuth2AuthorizedClientService") .addConstructorArgValue(clientRegistrationRepository).getBeanDefinition(); } - authorizedClientRepository = BeanDefinitionBuilder.rootBeanDefinition( "org.springframework.security.oauth2.client.web.AuthenticatedPrincipalOAuth2AuthorizedClientRepository") - .addConstructorArgValue(oauth2AuthorizedClientService).getBeanDefinition(); + .addConstructorArgValue(authorizedClientService).getBeanDefinition(); } - return authorizedClientRepository; } private BeanMetadataElement getClientRegistrationRepository(Element element) { - BeanMetadataElement clientRegistrationRepository = null; - + BeanMetadataElement clientRegistrationRepository; String clientRegistrationRepositoryRef = element.getAttribute(ATT_CLIENT_REGISTRATION_REPOSITORY_REF); if (!StringUtils.isEmpty(clientRegistrationRepositoryRef)) { clientRegistrationRepository = new RuntimeBeanReference(clientRegistrationRepositoryRef); @@ -281,52 +286,49 @@ final class OAuth2LoginBeanDefinitionParser implements BeanDefinitionParser { return clientRegistrationRepository; } - private BeanDefinition getOAuth2OidcAuthProvider(Element element, BeanMetadataElement accessTokenResponseClient, - String oauth2UserAuthMapperRef, ParserContext parserContext) { - BeanDefinition oauth2OidcAuthProvider = null; - boolean oidcAuthenticationProviderEnabled = ClassUtils - .isPresent("org.springframework.security.oauth2.jwt.JwtDecoder", this.getClass().getClassLoader()); + private BeanDefinition getOidcAuthProvider(Element element, + BeanMetadataElement accessTokenResponseClient, String userAuthoritiesMapperRef) { - if (oidcAuthenticationProviderEnabled) { - BeanMetadataElement oidcUserService = getOAuth2OidcUserService(element); - - BeanDefinitionBuilder oauth2OidcAuthProviderBuilder = BeanDefinitionBuilder.rootBeanDefinition( - "org.springframework.security.oauth2.client.oidc.authentication.OidcAuthorizationCodeAuthenticationProvider") - .addConstructorArgValue(accessTokenResponseClient).addConstructorArgValue(oidcUserService); - - if (!StringUtils.isEmpty(oauth2UserAuthMapperRef)) { - oauth2OidcAuthProviderBuilder.addPropertyReference("authoritiesMapper", oauth2UserAuthMapperRef); - } - - String jwtDecoderFactoryRef = element.getAttribute(ATT_JWT_DECODER_FACTORY_REF); - if (!StringUtils.isEmpty(jwtDecoderFactoryRef)) { - oauth2OidcAuthProviderBuilder.addPropertyReference("jwtDecoderFactory", jwtDecoderFactoryRef); - } - - oauth2OidcAuthProvider = oauth2OidcAuthProviderBuilder.getBeanDefinition(); - } else { - oauth2OidcAuthProvider = BeanDefinitionBuilder.rootBeanDefinition(OidcAuthenticationRequestChecker.class) - .getBeanDefinition(); + boolean oidcAuthenticationProviderEnabled = ClassUtils.isPresent( + "org.springframework.security.oauth2.jwt.JwtDecoder", this.getClass().getClassLoader()); + if (!oidcAuthenticationProviderEnabled) { + return BeanDefinitionBuilder.rootBeanDefinition(OidcAuthenticationRequestChecker.class).getBeanDefinition(); } - return oauth2OidcAuthProvider; + BeanMetadataElement oidcUserService = getOidcUserService(element); + + BeanDefinitionBuilder oidcAuthProviderBuilder = BeanDefinitionBuilder.rootBeanDefinition( + "org.springframework.security.oauth2.client.oidc.authentication.OidcAuthorizationCodeAuthenticationProvider") + .addConstructorArgValue(accessTokenResponseClient) + .addConstructorArgValue(oidcUserService); + + if (!StringUtils.isEmpty(userAuthoritiesMapperRef)) { + oidcAuthProviderBuilder.addPropertyReference("authoritiesMapper", userAuthoritiesMapperRef); + } + + String jwtDecoderFactoryRef = element.getAttribute(ATT_JWT_DECODER_FACTORY_REF); + if (!StringUtils.isEmpty(jwtDecoderFactoryRef)) { + oidcAuthProviderBuilder.addPropertyReference("jwtDecoderFactory", jwtDecoderFactoryRef); + } + + return oidcAuthProviderBuilder.getBeanDefinition(); } - private BeanMetadataElement getOAuth2OidcUserService(Element element) { - BeanMetadataElement oauth2OidcUserService = null; - String oauth2UserServiceRef = element.getAttribute(ATT_OIDC_USER_SERVICE_REF); - if (!StringUtils.isEmpty(oauth2UserServiceRef)) { - oauth2OidcUserService = new RuntimeBeanReference(oauth2UserServiceRef); + private BeanMetadataElement getOidcUserService(Element element) { + BeanMetadataElement oidcUserService; + String oidcUserServiceRef = element.getAttribute(ATT_OIDC_USER_SERVICE_REF); + if (!StringUtils.isEmpty(oidcUserServiceRef)) { + oidcUserService = new RuntimeBeanReference(oidcUserServiceRef); } else { - oauth2OidcUserService = BeanDefinitionBuilder + oidcUserService = BeanDefinitionBuilder .rootBeanDefinition("org.springframework.security.oauth2.client.oidc.userinfo.OidcUserService") .getBeanDefinition(); } - return oauth2OidcUserService; + return oidcUserService; } private BeanMetadataElement getOAuth2UserService(Element element) { - BeanMetadataElement oauth2UserService = null; + BeanMetadataElement oauth2UserService; String oauth2UserServiceRef = element.getAttribute(ATT_USER_SERVICE_REF); if (!StringUtils.isEmpty(oauth2UserServiceRef)) { oauth2UserService = new RuntimeBeanReference(oauth2UserServiceRef); @@ -339,8 +341,7 @@ final class OAuth2LoginBeanDefinitionParser implements BeanDefinitionParser { } private BeanMetadataElement getAccessTokenResponseClient(Element element) { - BeanMetadataElement accessTokenResponseClient = null; - + BeanMetadataElement accessTokenResponseClient; String accessTokenResponseClientRef = element.getAttribute(ATT_ACCESS_TOKEN_RESPONSE_CLIENT_REF); if (!StringUtils.isEmpty(accessTokenResponseClientRef)) { accessTokenResponseClient = new RuntimeBeanReference(accessTokenResponseClientRef); @@ -372,15 +373,12 @@ final class OAuth2LoginBeanDefinitionParser implements BeanDefinitionParser { return oauth2LoginLinks; } - private Map getLoginEntryPoint(Element element, - ParserContext parserContext) { + private Map getLoginEntryPoint(Element element) { Map entryPoints = null; Element clientRegsElt = DomUtils.getChildElementByTagName(element.getOwnerDocument().getDocumentElement(), Elements.CLIENT_REGISTRATIONS); - if (clientRegsElt != null) { List clientRegList = DomUtils.getChildElementsByTagName(clientRegsElt, ELT_CLIENT_REGISTRATION); - if (clientRegList.size() == 1) { RequestMatcher loginPageMatcher = new AntPathRequestMatcher(DEFAULT_LOGIN_URI); RequestMatcher faviconMatcher = new AntPathRequestMatcher("/favicon.ico"); @@ -397,24 +395,19 @@ final class OAuth2LoginBeanDefinitionParser implements BeanDefinitionParser { new AndRequestMatcher(notXRequestedWith, new NegatedRequestMatcher(defaultLoginPageMatcher)), new LoginUrlAuthenticationEntryPoint(DEFAULT_AUTHORIZATION_REQUEST_BASE_URI + "/" + clientRegElt.getAttribute(ATT_REGISTRATION_ID))); - } } - return entryPoints; } private RequestMatcher getAuthenticationEntryPointMatcher() { ContentNegotiationStrategy contentNegotiationStrategy = new HeaderContentNegotiationStrategy(); - MediaTypeRequestMatcher mediaMatcher = new MediaTypeRequestMatcher(contentNegotiationStrategy, MediaType.APPLICATION_XHTML_XML, new MediaType("image", "*"), MediaType.TEXT_HTML, MediaType.TEXT_PLAIN); mediaMatcher.setIgnoredMediaTypes(Collections.singleton(MediaType.ALL)); - RequestMatcher notXRequestedWith = new NegatedRequestMatcher( new RequestHeaderRequestMatcher("X-Requested-With", "XMLHttpRequest")); - return new AndRequestMatcher(Arrays.asList(notXRequestedWith, mediaMatcher)); } @@ -452,17 +445,17 @@ final class OAuth2LoginBeanDefinitionParser implements BeanDefinitionParser { */ private static class OAuth2LoginBeanConfig implements ApplicationContextAware { - private ApplicationContext appContext; + private ApplicationContext context; @Override - public void setApplicationContext(ApplicationContext appContext) throws BeansException { - this.appContext = appContext; + public void setApplicationContext(ApplicationContext context) throws BeansException { + this.context = context; } @SuppressWarnings({ "unchecked", "unused" }) public Map getLoginLinks() { Iterable clientRegistrations = null; - ClientRegistrationRepository clientRegistrationRepository = appContext + ClientRegistrationRepository clientRegistrationRepository = context .getBean(ClientRegistrationRepository.class); ResolvableType type = ResolvableType.forInstance(clientRegistrationRepository).as(Iterable.class); if (type != ResolvableType.NONE && ClientRegistration.class.isAssignableFrom(type.resolveGenerics()[0])) { @@ -480,6 +473,5 @@ final class OAuth2LoginBeanDefinitionParser implements BeanDefinitionParser { return loginUrlToClientName; } - } } diff --git a/config/src/main/java/org/springframework/security/config/http/SecurityFilters.java b/config/src/main/java/org/springframework/security/config/http/SecurityFilters.java index 6a759fc3dc..2ca1b76e0f 100644 --- a/config/src/main/java/org/springframework/security/config/http/SecurityFilters.java +++ b/config/src/main/java/org/springframework/security/config/http/SecurityFilters.java @@ -34,7 +34,7 @@ enum SecurityFilters { HEADERS_FILTER, CORS_FILTER, CSRF_FILTER, LOGOUT_FILTER, - OAUTH2_REDIRECT_FILTER, + OAUTH2_AUTHORIZATION_REQUEST_FILTER, X509_FILTER, PRE_AUTH_FILTER, CAS_FILTER, diff --git a/config/src/main/java/org/springframework/security/config/oauth2/client/ClientRegistrationsBeanDefinitionParser.java b/config/src/main/java/org/springframework/security/config/oauth2/client/ClientRegistrationsBeanDefinitionParser.java index 650a82886c..4dfbae5819 100644 --- a/config/src/main/java/org/springframework/security/config/oauth2/client/ClientRegistrationsBeanDefinitionParser.java +++ b/config/src/main/java/org/springframework/security/config/oauth2/client/ClientRegistrationsBeanDefinitionParser.java @@ -15,14 +15,6 @@ */ package org.springframework.security.config.oauth2.client; -import java.util.ArrayList; -import java.util.EnumSet; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Set; - import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.parsing.BeanComponentDefinition; import org.springframework.beans.factory.parsing.CompositeComponentDefinition; @@ -39,6 +31,13 @@ import org.springframework.util.StringUtils; import org.springframework.util.xml.DomUtils; import org.w3c.dom.Element; +import java.util.ArrayList; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; + /** * @author Ruby Hartono * @since 5.3 @@ -70,85 +69,94 @@ public final class ClientRegistrationsBeanDefinitionParser implements BeanDefini parserContext.extractSource(element)); parserContext.pushContainingComponent(compositeDef); - Map> providerDetailMap = getProviders(element); + Map> providers = getProviders(element); + List clientRegistrations = getClientRegistrations(element, parserContext, providers); - List clientRegs = getClientRegistrations(element, parserContext, providerDetailMap); - - BeanDefinition inMemClientRegRepoBeanDef = BeanDefinitionBuilder - .rootBeanDefinition(InMemoryClientRegistrationRepository.class).addConstructorArgValue(clientRegs) + BeanDefinition clientRegistrationRepositoryBean = BeanDefinitionBuilder + .rootBeanDefinition(InMemoryClientRegistrationRepository.class) + .addConstructorArgValue(clientRegistrations) .getBeanDefinition(); - String beanName = parserContext.getReaderContext().generateBeanName(inMemClientRegRepoBeanDef); - parserContext.registerBeanComponent(new BeanComponentDefinition(inMemClientRegRepoBeanDef, beanName)); + String clientRegistrationRepositoryId = parserContext.getReaderContext().generateBeanName( + clientRegistrationRepositoryBean); + parserContext.registerBeanComponent(new BeanComponentDefinition( + clientRegistrationRepositoryBean, clientRegistrationRepositoryId)); parserContext.popAndRegisterContainingComponent(); return null; } private List getClientRegistrations(Element element, ParserContext parserContext, - Map> providerDetailMap) { - List clientRegElts = DomUtils.getChildElementsByTagName(element, ELT_CLIENT_REGISTRATION); - List clientRegs = new ArrayList<>(); + Map> providers) { + List clientRegistrationElts = DomUtils.getChildElementsByTagName(element, ELT_CLIENT_REGISTRATION); + List clientRegistrations = new ArrayList<>(); - for (Element clientRegElt : clientRegElts) { - String regId = clientRegElt.getAttribute(ATT_REGISTRATION_ID); - String clientId = clientRegElt.getAttribute(ATT_CLIENT_ID); - String clientSecret = clientRegElt.getAttribute(ATT_CLIENT_SECRET); - String clientAuthMethod = clientRegElt.getAttribute(ATT_CLIENT_AUTHENTICATION_METHOD); - String authGrantType = clientRegElt.getAttribute(ATT_AUTHORIZATION_GRANT_TYPE); - String redirectUri = clientRegElt.getAttribute(ATT_REDIRECT_URI); - String scope = clientRegElt.getAttribute(ATT_SCOPE); - String clientName = clientRegElt.getAttribute(ATT_CLIENT_NAME); - String providerId = clientRegElt.getAttribute(ATT_PROVIDER_ID); - - Set scopes = StringUtils.commaDelimitedListToSet(scope); - ClientRegistration.Builder builder = getBuilderFromIssuerIfPossible(regId, providerId, providerDetailMap); + for (Element clientRegistrationElt : clientRegistrationElts) { + String registrationId = clientRegistrationElt.getAttribute(ATT_REGISTRATION_ID); + String providerId = clientRegistrationElt.getAttribute(ATT_PROVIDER_ID); + ClientRegistration.Builder builder = getBuilderFromIssuerIfPossible(registrationId, providerId, providers); if (builder == null) { - builder = getBuilder(regId, providerId, providerDetailMap); + builder = getBuilder(registrationId, providerId, providers); if (builder == null) { Object source = parserContext.extractSource(element); - parserContext.getReaderContext().error(getErrorMessage(providerId, regId), source); + parserContext.getReaderContext().error(getErrorMessage(providerId, registrationId), source); // error on the config skip to next element - break; + continue; } } - - ClientRegistration clientReg = builder.clientId(clientId) - .clientSecret(clientSecret) - .clientAuthenticationMethod(new ClientAuthenticationMethod(clientAuthMethod)) - .authorizationGrantType(new AuthorizationGrantType(authGrantType)) - .redirectUriTemplate(redirectUri) - .scope(scopes) - .clientName(clientName) - .build(); - clientRegs.add(clientReg); + getOptionalIfNotEmpty(clientRegistrationElt.getAttribute(ATT_CLIENT_ID)) + .ifPresent(builder::clientId); + getOptionalIfNotEmpty(clientRegistrationElt.getAttribute(ATT_CLIENT_SECRET)) + .ifPresent(builder::clientSecret); + getOptionalIfNotEmpty(clientRegistrationElt.getAttribute(ATT_CLIENT_AUTHENTICATION_METHOD)) + .map(ClientAuthenticationMethod::new) + .ifPresent(builder::clientAuthenticationMethod); + getOptionalIfNotEmpty(clientRegistrationElt.getAttribute(ATT_AUTHORIZATION_GRANT_TYPE)) + .map(AuthorizationGrantType::new) + .ifPresent(builder::authorizationGrantType); + getOptionalIfNotEmpty(clientRegistrationElt.getAttribute(ATT_REDIRECT_URI)) + .ifPresent(builder::redirectUriTemplate); + getOptionalIfNotEmpty(clientRegistrationElt.getAttribute(ATT_SCOPE)) + .map(StringUtils::commaDelimitedListToSet) + .ifPresent(builder::scope); + getOptionalIfNotEmpty(clientRegistrationElt.getAttribute(ATT_CLIENT_NAME)) + .ifPresent(builder::clientName); + clientRegistrations.add(builder.build()); } - return clientRegs; + + return clientRegistrations; } private Map> getProviders(Element element) { - List providerRegElts = DomUtils.getChildElementsByTagName(element, ELT_PROVIDER); - Map> providerDetailMap = new HashMap<>(); - for (Element providerRegElt : providerRegElts) { - Map detail = new HashMap(); - String providerId = providerRegElt.getAttribute(ATT_PROVIDER_ID); - detail.put(ATT_PROVIDER_ID, providerId); - detail.put(ATT_AUTHORIZATION_URI, providerRegElt.getAttribute(ATT_AUTHORIZATION_URI)); - detail.put(ATT_TOKEN_URI, providerRegElt.getAttribute(ATT_TOKEN_URI)); - detail.put(ATT_USERINFO_URI, providerRegElt.getAttribute(ATT_USERINFO_URI)); - detail.put(ATT_USERINFO_AUTHENTICATION_METHOD, - providerRegElt.getAttribute(ATT_USERINFO_AUTHENTICATION_METHOD)); - detail.put(ATT_USERNAME_ATTRIBUTE_NAME, providerRegElt.getAttribute(ATT_USERNAME_ATTRIBUTE_NAME)); - detail.put(ATT_JWKSET_URI, providerRegElt.getAttribute(ATT_JWKSET_URI)); - detail.put(ATT_ISSUER_URI, providerRegElt.getAttribute(ATT_ISSUER_URI)); + List providerElts = DomUtils.getChildElementsByTagName(element, ELT_PROVIDER); + Map> providers = new HashMap<>(); - providerDetailMap.put(providerId, detail); + for (Element providerElt : providerElts) { + Map provider = new HashMap<>(); + String providerId = providerElt.getAttribute(ATT_PROVIDER_ID); + provider.put(ATT_PROVIDER_ID, providerId); + getOptionalIfNotEmpty(providerElt.getAttribute(ATT_AUTHORIZATION_URI)) + .ifPresent(value -> provider.put(ATT_AUTHORIZATION_URI, value)); + getOptionalIfNotEmpty(providerElt.getAttribute(ATT_TOKEN_URI)) + .ifPresent(value -> provider.put(ATT_TOKEN_URI, value)); + getOptionalIfNotEmpty(providerElt.getAttribute(ATT_USERINFO_URI)) + .ifPresent(value -> provider.put(ATT_USERINFO_URI, value)); + getOptionalIfNotEmpty(providerElt.getAttribute(ATT_USERINFO_AUTHENTICATION_METHOD)) + .ifPresent(value -> provider.put(ATT_USERINFO_AUTHENTICATION_METHOD, value)); + getOptionalIfNotEmpty(providerElt.getAttribute(ATT_USERNAME_ATTRIBUTE_NAME)) + .ifPresent(value -> provider.put(ATT_USERNAME_ATTRIBUTE_NAME, value)); + getOptionalIfNotEmpty(providerElt.getAttribute(ATT_JWKSET_URI)) + .ifPresent(value -> provider.put(ATT_JWKSET_URI, value)); + getOptionalIfNotEmpty(providerElt.getAttribute(ATT_ISSUER_URI)) + .ifPresent(value -> provider.put(ATT_ISSUER_URI, value)); + providers.put(providerId, provider); } - return providerDetailMap; + + return providers; } private static ClientRegistration.Builder getBuilderFromIssuerIfPossible(String registrationId, String configuredProviderId, Map> providers) { - String providerId = (configuredProviderId != null) ? configuredProviderId : registrationId; + String providerId = configuredProviderId != null ? configuredProviderId : registrationId; if (providers.containsKey(providerId)) { Map provider = providers.get(providerId); String issuer = provider.get(ATT_ISSUER_URI); @@ -168,7 +176,7 @@ public final class ClientRegistrationsBeanDefinitionParser implements BeanDefini if (provider == null && !providers.containsKey(providerId)) { return null; } - ClientRegistration.Builder builder = (provider != null) ? provider.getBuilder(registrationId) + ClientRegistration.Builder builder = provider != null ? provider.getBuilder(registrationId) : ClientRegistration.withRegistrationId(registrationId); if (providers.containsKey(providerId)) { return getBuilder(builder, providers.get(providerId)); @@ -178,13 +186,19 @@ public final class ClientRegistrationsBeanDefinitionParser implements BeanDefini private static ClientRegistration.Builder getBuilder(ClientRegistration.Builder builder, Map provider) { - getOptionalIfNotEmpty(provider.get(ATT_AUTHORIZATION_URI)).ifPresent(builder::authorizationUri); - getOptionalIfNotEmpty(provider.get(ATT_TOKEN_URI)).ifPresent(builder::tokenUri); - getOptionalIfNotEmpty(provider.get(ATT_USERINFO_URI)).ifPresent(builder::userInfoUri); - getOptionalIfNotEmpty(provider.get(ATT_USERINFO_AUTHENTICATION_METHOD)).map(AuthenticationMethod::new) + getOptionalIfNotEmpty(provider.get(ATT_AUTHORIZATION_URI)) + .ifPresent(builder::authorizationUri); + getOptionalIfNotEmpty(provider.get(ATT_TOKEN_URI)) + .ifPresent(builder::tokenUri); + getOptionalIfNotEmpty(provider.get(ATT_USERINFO_URI)) + .ifPresent(builder::userInfoUri); + getOptionalIfNotEmpty(provider.get(ATT_USERINFO_AUTHENTICATION_METHOD)) + .map(AuthenticationMethod::new) .ifPresent(builder::userInfoAuthenticationMethod); - getOptionalIfNotEmpty(provider.get(ATT_JWKSET_URI)).ifPresent(builder::jwkSetUri); - getOptionalIfNotEmpty(provider.get(ATT_USERNAME_ATTRIBUTE_NAME)).ifPresent(builder::userNameAttributeName); + getOptionalIfNotEmpty(provider.get(ATT_JWKSET_URI)) + .ifPresent(builder::jwkSetUri); + getOptionalIfNotEmpty(provider.get(ATT_USERNAME_ATTRIBUTE_NAME)) + .ifPresent(builder::userNameAttributeName); return builder; } @@ -194,7 +208,7 @@ public final class ClientRegistrationsBeanDefinitionParser implements BeanDefini private static CommonOAuth2Provider getCommonProvider(String providerId) { try { - String value = providerId.toString().trim(); + String value = providerId.trim(); if (value.isEmpty()) { return null; } @@ -223,12 +237,12 @@ public final class ClientRegistrationsBeanDefinitionParser implements BeanDefini private static String getCanonicalName(String name) { StringBuilder canonicalName = new StringBuilder(name.length()); name.chars().filter(Character::isLetterOrDigit).map(Character::toLowerCase) - .forEach((c) -> canonicalName.append((char) c)); + .forEach(c -> canonicalName.append((char) c)); return canonicalName.toString(); } private static String getErrorMessage(String configuredProviderId, String registrationId) { - return ((configuredProviderId != null) ? "Unknown provider ID '" + configuredProviderId + "'" - : "Provider ID must be specified for client registration '" + registrationId + "'"); + return configuredProviderId != null ? "Unknown provider ID '" + configuredProviderId + "'" + : "Provider ID must be specified for client registration '" + registrationId + "'"; } } diff --git a/config/src/main/resources/org/springframework/security/config/spring-security-5.3.rnc b/config/src/main/resources/org/springframework/security/config/spring-security-5.3.rnc index 92afc6a5c5..a3739b3eab 100644 --- a/config/src/main/resources/org/springframework/security/config/spring-security-5.3.rnc +++ b/config/src/main/resources/org/springframework/security/config/spring-security-5.3.rnc @@ -441,46 +441,46 @@ oauth2-login = ## Configures authentication support using an OAuth 2.0 and/or OpenID Connect 1.0 Provider. element oauth2-login {oauth2-login.attlist} oauth2-login.attlist &= - ## Reference to ClientRegistrationRepository + ## Reference to the ClientRegistrationRepository attribute client-registration-repository-ref {xsd:token}? oauth2-login.attlist &= - ## Reference to OAuth2AuthorizedClientRepository + ## Reference to the OAuth2AuthorizedClientRepository attribute authorized-client-repository-ref {xsd:token}? oauth2-login.attlist &= - ## Reference to OAuth2AuthorizedClientService + ## Reference to the OAuth2AuthorizedClientService attribute authorized-client-service-ref {xsd:token}? oauth2-login.attlist &= - ## Reference to AuthorizationRequestRepository + ## Reference to the AuthorizationRequestRepository attribute authorization-request-repository-ref {xsd:token}? oauth2-login.attlist &= - ## Reference to OAuth2AuthorizationRequestResolver + ## Reference to the OAuth2AuthorizationRequestResolver attribute authorization-request-resolver-ref {xsd:token}? oauth2-login.attlist &= - ## Reference to OAuth2AccessTokenResponseClient + ## Reference to the OAuth2AccessTokenResponseClient attribute access-token-response-client-ref {xsd:token}? oauth2-login.attlist &= - ## Reference to GrantedAuthoritiesMapper + ## Reference to the GrantedAuthoritiesMapper attribute user-authorities-mapper-ref {xsd:token}? oauth2-login.attlist &= - ## Reference to OAuth2UserService + ## Reference to the OAuth2UserService attribute user-service-ref {xsd:token}? oauth2-login.attlist &= - ## Reference to OidcUserService + ## Reference to the OpenID Connect OAuth2UserService attribute oidc-user-service-ref {xsd:token}? oauth2-login.attlist &= - ## Specifies the URL to validate the credentials. + ## The URI where the filter processes authentication requests attribute login-processing-url {xsd:token}? oauth2-login.attlist &= - ## Specifies the URL to send users to if login is required + ## The URI to send users to login attribute login-page {xsd:token}? oauth2-login.attlist &= - ## Specifies authentication success handler + ## Reference to the AuthenticationSuccessHandler attribute authentication-success-handler-ref {xsd:token}? oauth2-login.attlist &= - ## Specifies authentication failure handler + ## Reference to the AuthenticationFailureHandler attribute authentication-failure-handler-ref {xsd:token}? oauth2-login.attlist &= - ## Specifies JWT decoder factory for OidcAuthorizationCodeAuthenticationProvider + ## Reference to the JwtDecoderFactory used by OidcAuthorizationCodeAuthenticationProvider attribute jwt-decoder-factory-ref {xsd:token}? client-registrations = @@ -504,7 +504,7 @@ client-registration.attlist &= attribute client-authentication-method {"basic" | "post" | "none"}? client-registration.attlist &= ## The OAuth 2.0 Authorization Framework defines four Authorization Grant types. The supported values are authorization_code, client_credentials, password and implicit. - attribute authorization-grant-type {"authorization_code" | "client_credentials" | "password" | "implicit"} + attribute authorization-grant-type {"authorization_code" | "client_credentials" | "password" | "implicit"}? client-registration.attlist &= ## The client’s registered redirect URI that the Authorization Server redirects the end-user’s user-agent to after the end-user has authenticated and authorized access to the client. attribute redirect-uri {xsd:token}? @@ -1024,4 +1024,4 @@ position = ## The explicit position at which the custom-filter should be placed in the chain. Use if you are replacing a standard filter. attribute position {named-security-filter} -named-security-filter = "FIRST" | "CHANNEL_FILTER" | "SECURITY_CONTEXT_FILTER" | "CONCURRENT_SESSION_FILTER" | "WEB_ASYNC_MANAGER_FILTER" | "HEADERS_FILTER" | "CORS_FILTER" | "CSRF_FILTER" | "LOGOUT_FILTER" | "OAUTH2_REDIRECT_FILTER" | "X509_FILTER" | "PRE_AUTH_FILTER" | "CAS_FILTER" | "OAUTH2_LOGIN_FILTER" | "FORM_LOGIN_FILTER" | "OPENID_FILTER" | "LOGIN_PAGE_FILTER" |"LOGOUT_PAGE_FILTER" | "DIGEST_AUTH_FILTER" | "BEARER_TOKEN_AUTH_FILTER" | "BASIC_AUTH_FILTER" | "REQUEST_CACHE_FILTER" | "SERVLET_API_SUPPORT_FILTER" | "JAAS_API_SUPPORT_FILTER" | "REMEMBER_ME_FILTER" | "ANONYMOUS_FILTER" | "SESSION_MANAGEMENT_FILTER" | "EXCEPTION_TRANSLATION_FILTER" | "FILTER_SECURITY_INTERCEPTOR" | "SWITCH_USER_FILTER" | "LAST" +named-security-filter = "FIRST" | "CHANNEL_FILTER" | "SECURITY_CONTEXT_FILTER" | "CONCURRENT_SESSION_FILTER" | "WEB_ASYNC_MANAGER_FILTER" | "HEADERS_FILTER" | "CORS_FILTER" | "CSRF_FILTER" | "LOGOUT_FILTER" | "OAUTH2_AUTHORIZATION_REQUEST_FILTER" | "X509_FILTER" | "PRE_AUTH_FILTER" | "CAS_FILTER" | "OAUTH2_LOGIN_FILTER" | "FORM_LOGIN_FILTER" | "OPENID_FILTER" | "LOGIN_PAGE_FILTER" |"LOGOUT_PAGE_FILTER" | "DIGEST_AUTH_FILTER" | "BEARER_TOKEN_AUTH_FILTER" | "BASIC_AUTH_FILTER" | "REQUEST_CACHE_FILTER" | "SERVLET_API_SUPPORT_FILTER" | "JAAS_API_SUPPORT_FILTER" | "REMEMBER_ME_FILTER" | "ANONYMOUS_FILTER" | "SESSION_MANAGEMENT_FILTER" | "EXCEPTION_TRANSLATION_FILTER" | "FILTER_SECURITY_INTERCEPTOR" | "SWITCH_USER_FILTER" | "LAST" diff --git a/config/src/main/resources/org/springframework/security/config/spring-security-5.3.xsd b/config/src/main/resources/org/springframework/security/config/spring-security-5.3.xsd index 7a4d9aa4fa..4d91099c8f 100644 --- a/config/src/main/resources/org/springframework/security/config/spring-security-5.3.xsd +++ b/config/src/main/resources/org/springframework/security/config/spring-security-5.3.xsd @@ -1463,85 +1463,85 @@ - Reference to ClientRegistrationRepository + Reference to the ClientRegistrationRepository - Reference to OAuth2AuthorizedClientRepository + Reference to the OAuth2AuthorizedClientRepository - Reference to OAuth2AuthorizedClientService + Reference to the OAuth2AuthorizedClientService - Reference to AuthorizationRequestRepository + Reference to the AuthorizationRequestRepository - Reference to OAuth2AuthorizationRequestResolver + Reference to the OAuth2AuthorizationRequestResolver - Reference to OAuth2AccessTokenResponseClient + Reference to the OAuth2AccessTokenResponseClient - Reference to GrantedAuthoritiesMapper + Reference to the GrantedAuthoritiesMapper - Reference to OAuth2UserService + Reference to the OAuth2UserService - Reference to OidcUserService + Reference to the OpenID Connect OAuth2UserService - Specifies the URL to validate the credentials. + The URI where the filter processes authentication requests - Specifies the URL to send users to if login is required + The URI to send users to login - Specifies authentication success handler + Reference to the AuthenticationSuccessHandler - Specifies authentication failure handler + Reference to the AuthenticationFailureHandler - Specifies JWT decoder factory for OidcAuthorizationCodeAuthenticationProvider + Reference to the JwtDecoderFactory used by OidcAuthorizationCodeAuthenticationProvider @@ -1601,7 +1601,7 @@ - + The OAuth 2.0 Authorization Framework defines four Authorization Grant types. The supported values are authorization_code, client_credentials, password and implicit. @@ -2989,7 +2989,7 @@ - + diff --git a/config/src/test/java/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests.java b/config/src/test/java/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests.java index d8a1b5e1b6..9470cd03b4 100644 --- a/config/src/test/java/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests.java +++ b/config/src/test/java/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests.java @@ -18,16 +18,14 @@ package org.springframework.security.config.http; import org.junit.Rule; import org.junit.Test; import org.mockito.ArgumentCaptor; -import org.mockito.internal.util.MockUtil; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationListener; -import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.security.authentication.event.AuthenticationSuccessEvent; import org.springframework.security.config.test.SpringTestRule; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; -import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.AuthorityUtils; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper; import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService; @@ -38,17 +36,15 @@ import org.springframework.security.oauth2.client.registration.ClientRegistratio import org.springframework.security.oauth2.client.registration.TestClientRegistrations; import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest; import org.springframework.security.oauth2.client.userinfo.OAuth2UserService; -import org.springframework.security.oauth2.client.web.AuthenticatedPrincipalOAuth2AuthorizedClientRepository; import org.springframework.security.oauth2.client.web.AuthorizationRequestRepository; import org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestResolver; import org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository; -import org.springframework.security.oauth2.client.web.OAuth2LoginAuthenticationFilter; import org.springframework.security.oauth2.core.OAuth2AuthenticationException; import org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse; import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest; import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames; import org.springframework.security.oauth2.core.endpoint.TestOAuth2AuthorizationRequests; -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.core.user.TestOAuth2Users; import org.springframework.security.oauth2.jwt.Jwt; @@ -56,7 +52,7 @@ import org.springframework.security.oauth2.jwt.JwtDecoderFactory; import org.springframework.security.oauth2.jwt.TestJwts; import org.springframework.security.web.authentication.AuthenticationFailureHandler; import org.springframework.security.web.authentication.AuthenticationSuccessHandler; -import org.springframework.security.web.context.SecurityContextRepository; +import org.springframework.security.web.savedrequest.RequestCache; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MvcResult; import org.springframework.util.LinkedMultiValueMap; @@ -68,19 +64,15 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.springframework.security.oauth2.core.endpoint.TestOAuth2AccessTokenResponses.accessTokenResponse; +import static org.springframework.security.oauth2.core.endpoint.TestOAuth2AccessTokenResponses.oidcAccessTokenResponse; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; -import java.lang.reflect.Field; import java.util.Collection; -import java.util.Collections; import java.util.HashMap; import java.util.Map; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - /** * Tests for {@link OAuth2LoginBeanDefinitionParser}. * @@ -95,17 +87,11 @@ public class OAuth2LoginBeanDefinitionParserTests { @Autowired private ClientRegistrationRepository clientRegistrationRepository; - @Autowired - private OAuth2LoginAuthenticationFilter oauth2LoginAuthenticationFilter; + @Autowired(required = false) + private OAuth2AuthorizedClientRepository authorizedClientRepository; @Autowired(required = false) - private OAuth2AuthorizedClientRepository oauth2AuthorizedClientRepository; - - @Autowired(required = false) - private OAuth2AuthorizedClientService oauth2AuthorizedClientService; - - @Autowired - SecurityContextRepository securityContextRepository; + private OAuth2AuthorizedClientService authorizedClientService; @Autowired(required = false) private ApplicationListener authenticationSuccessListener; @@ -113,6 +99,9 @@ public class OAuth2LoginBeanDefinitionParserTests { @Autowired(required = false) private AuthorizationRequestRepository authorizationRequestRepository; + @Autowired(required = false) + private OAuth2AuthorizationRequestResolver authorizationRequestResolver; + @Autowired(required = false) private OAuth2AccessTokenResponseClient accessTokenResponseClient; @@ -123,10 +112,7 @@ public class OAuth2LoginBeanDefinitionParserTests { private JwtDecoderFactory jwtDecoderFactory; @Autowired(required = false) - private OAuth2AuthorizationRequestResolver oauth2AuthorizationRequestResolver; - - @Autowired(required = false) - private GrantedAuthoritiesMapper grantedAuthoritiesMapper; + private GrantedAuthoritiesMapper userAuthoritiesMapper; @Autowired(required = false) private AuthenticationFailureHandler authenticationFailureHandler; @@ -134,14 +120,19 @@ public class OAuth2LoginBeanDefinitionParserTests { @Autowired(required = false) private AuthenticationSuccessHandler authenticationSuccessHandler; + @Autowired(required = false) + private RequestCache requestCache; + @Autowired private MockMvc mvc; @Test - public void requestLoginWhenMultiClientRegistrationThenReturnLoginPageWithOauth2Login() throws Exception { + public void requestLoginWhenMultiClientRegistrationThenReturnLoginPageWithClients() throws Exception { this.spring.configLocations(this.xml("MultiClientRegistration")).autowire(); - MvcResult result = this.mvc.perform(get("/login")).andExpect(status().is2xxSuccessful()).andReturn(); + MvcResult result = this.mvc.perform(get("/login")) + .andExpect(status().is2xxSuccessful()) + .andReturn(); assertThat(result.getResponse().getContentAsString()) .contains("Google"); @@ -152,10 +143,13 @@ public class OAuth2LoginBeanDefinitionParserTests { // gh-5347 @Test public void requestWhenSingleClientRegistrationThenAutoRedirect() throws Exception { - this.spring.configLocations(this.xml("SingleClientRegistration-WithinSameFile")).autowire(); + this.spring.configLocations(this.xml("SingleClientRegistration")).autowire(); - this.mvc.perform(get("/")).andExpect(status().is3xxRedirection()) + this.mvc.perform(get("/")) + .andExpect(status().is3xxRedirection()) .andExpect(redirectedUrl("http://localhost/oauth2/authorization/google-login")); + + verify(requestCache).saveRequest(any(), any()); } // gh-5347 @@ -164,7 +158,8 @@ public class OAuth2LoginBeanDefinitionParserTests { throws Exception { this.spring.configLocations(this.xml("SingleClientRegistration")).autowire(); - this.mvc.perform(get("/favicon.ico").accept(new MediaType("image", "*"))).andExpect(status().is3xxRedirection()) + this.mvc.perform(get("/favicon.ico").accept(new MediaType("image", "*"))) + .andExpect(status().is3xxRedirection()) .andExpect(redirectedUrl("http://localhost/login")); } @@ -174,7 +169,8 @@ public class OAuth2LoginBeanDefinitionParserTests { throws Exception { this.spring.configLocations(this.xml("SingleClientRegistration")).autowire(); - this.mvc.perform(get("/").header("X-Requested-With", "XMLHttpRequest")).andExpect(status().is3xxRedirection()) + this.mvc.perform(get("/").header("X-Requested-With", "XMLHttpRequest")) + .andExpect(status().is3xxRedirection()) .andExpect(redirectedUrl("http://localhost/login")); } @@ -188,66 +184,65 @@ public class OAuth2LoginBeanDefinitionParserTests { params.add("state", "state123"); this.mvc.perform(get("/login/oauth2/code/google").params(params)); - // assertions ArgumentCaptor exceptionCaptor = ArgumentCaptor .forClass(AuthenticationException.class); verify(authenticationFailureHandler).onAuthenticationFailure(any(), any(), exceptionCaptor.capture()); - AuthenticationException excValue = exceptionCaptor.getValue(); - assertThat(excValue).isInstanceOf(OAuth2AuthenticationException.class); - assertThat(((OAuth2AuthenticationException) excValue).getError().getErrorCode()) + AuthenticationException exception = exceptionCaptor.getValue(); + assertThat(exception).isInstanceOf(OAuth2AuthenticationException.class); + assertThat(((OAuth2AuthenticationException) exception).getError().getErrorCode()) .isEqualTo("authorization_request_not_found"); } @Test public void requestWhenAuthorizationResponseValidThenAuthenticate() throws Exception { - this.spring.configLocations(this.xml("SingleClientRegistration-WithTestConfiguration")).autowire(); + this.spring.configLocations(this.xml("MultiClientRegistration-WithCustomConfiguration")).autowire(); + + Map attributes = new HashMap<>(); + attributes.put(OAuth2ParameterNames.REGISTRATION_ID, "github-login"); + OAuth2AuthorizationRequest authorizationRequest = TestOAuth2AuthorizationRequests.request() + .attributes(attributes).build(); + when(this.authorizationRequestRepository.removeAuthorizationRequest(any(), any())).thenReturn(authorizationRequest); + OAuth2AccessTokenResponse accessTokenResponse = accessTokenResponse().build(); when(this.accessTokenResponseClient.getTokenResponse(any())).thenReturn(accessTokenResponse); OAuth2User oauth2User = TestOAuth2Users.create(); when(this.oauth2UserService.loadUser(any())).thenReturn(oauth2User); - Map attributes = new HashMap<>(); - attributes.put(OAuth2ParameterNames.REGISTRATION_ID, "google-login"); - OAuth2AuthorizationRequest authRequest = TestOAuth2AuthorizationRequests.request().attributes(attributes) - .build(); - when(this.authorizationRequestRepository.removeAuthorizationRequest(any(), any())).thenReturn(authRequest); - MultiValueMap params = new LinkedMultiValueMap<>(); params.add("code", "code123"); - params.add("state", authRequest.getState()); - this.mvc.perform(get("/login/oauth2/code/google").params(params)).andExpect(status().is2xxSuccessful()); + params.add("state", authorizationRequest.getState()); + this.mvc.perform(get("/login/oauth2/code/github-login").params(params)) + .andExpect(status().is2xxSuccessful()); ArgumentCaptor authenticationCaptor = ArgumentCaptor.forClass(Authentication.class); verify(authenticationSuccessHandler).onAuthenticationSuccess(any(), any(), authenticationCaptor.capture()); - Authentication authenticationValue = authenticationCaptor.getValue(); - assertThat(authenticationValue.getAuthorities()).hasSize(1); - assertThat(authenticationValue.getAuthorities()).first().isInstanceOf(SimpleGrantedAuthority.class) - .hasToString("ROLE_USER"); + Authentication authentication = authenticationCaptor.getValue(); + assertThat(authentication.getPrincipal()).isInstanceOf(OAuth2User.class); } // gh-6009 @Test public void requestWhenAuthorizationResponseValidThenAuthenticationSuccessEventPublished() throws Exception { - this.spring.configLocations(this.xml("SingleClientRegistration-WithTestConfiguration")).autowire(); + this.spring.configLocations(this.xml("MultiClientRegistration-WithCustomConfiguration")).autowire(); + + Map attributes = new HashMap<>(); + attributes.put(OAuth2ParameterNames.REGISTRATION_ID, "github-login"); + OAuth2AuthorizationRequest authorizationRequest = TestOAuth2AuthorizationRequests.request() + .attributes(attributes).build(); + when(this.authorizationRequestRepository.removeAuthorizationRequest(any(), any())).thenReturn(authorizationRequest); + OAuth2AccessTokenResponse accessTokenResponse = accessTokenResponse().build(); when(this.accessTokenResponseClient.getTokenResponse(any())).thenReturn(accessTokenResponse); OAuth2User oauth2User = TestOAuth2Users.create(); when(this.oauth2UserService.loadUser(any())).thenReturn(oauth2User); - Map attributes = new HashMap<>(); - attributes.put(OAuth2ParameterNames.REGISTRATION_ID, "google-login"); - OAuth2AuthorizationRequest authRequest = TestOAuth2AuthorizationRequests.request().attributes(attributes) - .build(); - when(this.authorizationRequestRepository.removeAuthorizationRequest(any(), any())).thenReturn(authRequest); - MultiValueMap params = new LinkedMultiValueMap<>(); params.add("code", "code123"); - params.add("state", authRequest.getState()); - this.mvc.perform(get("/login/oauth2/code/google").params(params)); + params.add("state", authorizationRequest.getState()); + this.mvc.perform(get("/login/oauth2/code/github-login").params(params)); - // assertions verify(authenticationSuccessListener).onApplicationEvent(any(AuthenticationSuccessEvent.class)); } @@ -255,149 +250,120 @@ public class OAuth2LoginBeanDefinitionParserTests { public void requestWhenOidcAuthenticationResponseValidThenJwtDecoderFactoryCalled() throws Exception { this.spring.configLocations(this.xml("SingleClientRegistration-WithJwtDecoderFactoryAndDefaultSuccessHandler")) .autowire(); - Map additionalParameters = new HashMap<>(); - additionalParameters.put(OidcParameterNames.ID_TOKEN, "token123"); - OAuth2AccessTokenResponse accessTokenResponse = accessTokenResponse().additionalParameters(additionalParameters) - .build(); + + Map attributes = new HashMap<>(); + attributes.put(OAuth2ParameterNames.REGISTRATION_ID, "google-login"); + OAuth2AuthorizationRequest authorizationRequest = TestOAuth2AuthorizationRequests.oidcRequest() + .attributes(attributes).build(); + when(this.authorizationRequestRepository.removeAuthorizationRequest(any(), any())).thenReturn(authorizationRequest); + + OAuth2AccessTokenResponse accessTokenResponse = oidcAccessTokenResponse().build(); when(this.accessTokenResponseClient.getTokenResponse(any())).thenReturn(accessTokenResponse); Jwt jwt = TestJwts.user(); when(this.jwtDecoderFactory.createDecoder(any())).thenReturn(token -> jwt); - Map attributes = new HashMap<>(); - attributes.put(OAuth2ParameterNames.REGISTRATION_ID, "google-login"); - OAuth2AuthorizationRequest authRequest = TestOAuth2AuthorizationRequests.request().attributes(attributes) - .scope("openid").build(); - when(this.authorizationRequestRepository.removeAuthorizationRequest(any(), any())).thenReturn(authRequest); - MultiValueMap params = new LinkedMultiValueMap<>(); params.add("code", "code123"); - params.add("state", authRequest.getState()); - this.mvc.perform(get("/login/oauth2/code/google").params(params)).andExpect(status().is3xxRedirection()) + params.add("state", authorizationRequest.getState()); + this.mvc.perform(get("/login/oauth2/code/google-login").params(params)) + .andExpect(status().is3xxRedirection()) .andExpect(redirectedUrl("/")); + + verify(this.jwtDecoderFactory).createDecoder(any()); + verify(this.requestCache).getRequest(any(), any()); } @SuppressWarnings({ "unchecked", "rawtypes" }) @Test public void requestWhenCustomGrantedAuthoritiesMapperThenCalled() throws Exception { - this.spring.configLocations(this.xml("SingleClientRegistration-WithCustomGrantedAuthorities")).autowire(); - Map additionalParameters = new HashMap<>(); - additionalParameters.put(OidcParameterNames.ID_TOKEN, "token123"); - OAuth2AccessTokenResponse accessTokenResponse = accessTokenResponse().additionalParameters(additionalParameters) - .build(); - when(this.accessTokenResponseClient.getTokenResponse(any())).thenReturn(accessTokenResponse); - - OAuth2User oauth2User = TestOAuth2Users.create(); - when(this.oauth2UserService.loadUser(any())).thenReturn(oauth2User); - - GrantedAuthority grantedAuthority = new SimpleGrantedAuthority("ROLE_OAUTH2_USER"); - when(this.grantedAuthoritiesMapper.mapAuthorities(any())) - .thenReturn((Collection) Collections.singletonList(grantedAuthority)); + this.spring.configLocations(this.xml("MultiClientRegistration-WithCustomGrantedAuthorities")).autowire(); Map attributes = new HashMap<>(); - attributes.put(OAuth2ParameterNames.REGISTRATION_ID, "google-login"); - OAuth2AuthorizationRequest authRequest = TestOAuth2AuthorizationRequests.request().attributes(attributes) - .build(); - when(this.authorizationRequestRepository.removeAuthorizationRequest(any(), any())).thenReturn(authRequest); + attributes.put(OAuth2ParameterNames.REGISTRATION_ID, "github-login"); + OAuth2AuthorizationRequest authorizationRequest = TestOAuth2AuthorizationRequests.request() + .attributes(attributes).build(); + when(this.authorizationRequestRepository.removeAuthorizationRequest(any(), any())).thenReturn(authorizationRequest); - MultiValueMap params = new LinkedMultiValueMap<>(); - params.add("code", "code123"); - params.add("state", authRequest.getState()); - this.mvc.perform(get("/login/oauth2/code/google").params(params)).andExpect(status().is2xxSuccessful()); - - ArgumentCaptor authenticationCaptor = ArgumentCaptor.forClass(Authentication.class); - verify(authenticationSuccessHandler).onAuthenticationSuccess(any(), any(), authenticationCaptor.capture()); - Authentication authenticationValue = authenticationCaptor.getValue(); - assertThat(authenticationValue.getAuthorities()).hasSize(1); - assertThat(authenticationValue.getAuthorities()).first().isInstanceOf(SimpleGrantedAuthority.class) - .hasToString("ROLE_OAUTH2_USER"); - - // re-setup for OIDC test - Jwt jwt = TestJwts.user(); - when(this.jwtDecoderFactory.createDecoder(any())).thenReturn(token -> jwt); - - grantedAuthority = new SimpleGrantedAuthority("ROLE_OIDC_USER"); - when(this.grantedAuthoritiesMapper.mapAuthorities(any())) - .thenReturn((Collection) Collections.singletonList(grantedAuthority)); - - authRequest = TestOAuth2AuthorizationRequests.request().attributes(attributes).scope("openid").build(); - when(this.authorizationRequestRepository.removeAuthorizationRequest(any(), any())).thenReturn(authRequest); - - this.mvc.perform(get("/login/oauth2/code/google").params(params)).andExpect(status().is2xxSuccessful()); - - authenticationCaptor = ArgumentCaptor.forClass(Authentication.class); - verify(authenticationSuccessHandler, times(2)).onAuthenticationSuccess(any(), any(), - authenticationCaptor.capture()); - authenticationValue = authenticationCaptor.getValue(); - assertThat(authenticationValue.getAuthorities()).hasSize(1); - assertThat(authenticationValue.getAuthorities()).first().isInstanceOf(SimpleGrantedAuthority.class) - .hasToString("ROLE_OIDC_USER"); - } - - @SuppressWarnings({ "unchecked", "rawtypes" }) - @Test - public void successOidcLoginWhenSingleClientRegistrationAndCustomAuthoritiesThenReturnSuccessWithCorrectAuthorities() - throws Exception { - this.spring.configLocations(this.xml("SingleClientRegistration-WithJwtDecoderFactory")).autowire(); - Map additionalParameters = new HashMap<>(); - additionalParameters.put(OidcParameterNames.ID_TOKEN, "token123"); - OAuth2AccessTokenResponse accessTokenResponse = accessTokenResponse().additionalParameters(additionalParameters) - .build(); - when(this.accessTokenResponseClient.getTokenResponse(any())).thenReturn(accessTokenResponse); - - Jwt jwt = TestJwts.user(); - when(this.jwtDecoderFactory.createDecoder(any())).thenReturn(token -> jwt); - - GrantedAuthority grantedAuthority = new SimpleGrantedAuthority("ROLE_OIDC_USER"); - when(this.grantedAuthoritiesMapper.mapAuthorities(any())) - .thenReturn((Collection) Collections.singletonList(grantedAuthority)); - - Map attributes = new HashMap<>(); - attributes.put(OAuth2ParameterNames.REGISTRATION_ID, "google-login"); - OAuth2AuthorizationRequest authRequest = TestOAuth2AuthorizationRequests.request().attributes(attributes) - .scope("openid").build(); - when(this.authorizationRequestRepository.removeAuthorizationRequest(any(), any())).thenReturn(authRequest); - - MultiValueMap params = new LinkedMultiValueMap<>(); - params.add("code", "code123"); - params.add("state", authRequest.getState()); - this.mvc.perform(get("/login/oauth2/code/google").params(params)).andExpect(status().is2xxSuccessful()); - - ArgumentCaptor authenticationCaptor = ArgumentCaptor.forClass(Authentication.class); - verify(authenticationSuccessHandler).onAuthenticationSuccess(any(), any(), authenticationCaptor.capture()); - Authentication authenticationValue = authenticationCaptor.getValue(); - assertThat(authenticationValue.getAuthorities()).hasSize(1); - assertThat(authenticationValue.getAuthorities()).first().isInstanceOf(SimpleGrantedAuthority.class) - .hasToString("ROLE_OIDC_USER"); - } - - // gh-5488 - @Test - public void requestWhenCustomLoginProcessingUrlThenProcessAuthentication() throws Exception { - this.spring.configLocations(this.xml("SingleClientRegistration-WithCustomLoginProcessingUrl")).autowire(); OAuth2AccessTokenResponse accessTokenResponse = accessTokenResponse().build(); when(this.accessTokenResponseClient.getTokenResponse(any())).thenReturn(accessTokenResponse); OAuth2User oauth2User = TestOAuth2Users.create(); when(this.oauth2UserService.loadUser(any())).thenReturn(oauth2User); - Map attributes = new HashMap<>(); - attributes.put(OAuth2ParameterNames.REGISTRATION_ID, "google-login"); - OAuth2AuthorizationRequest authRequest = TestOAuth2AuthorizationRequests.request().attributes(attributes) - .build(); - when(this.authorizationRequestRepository.removeAuthorizationRequest(any(), any())).thenReturn(authRequest); + when(this.userAuthoritiesMapper.mapAuthorities(any())).thenReturn( + (Collection) AuthorityUtils.createAuthorityList("ROLE_OAUTH2_USER")); MultiValueMap params = new LinkedMultiValueMap<>(); params.add("code", "code123"); - params.add("state", authRequest.getState()); - this.mvc.perform(get("/login/oauth2/google").params(params)).andExpect(status().is2xxSuccessful()); + params.add("state", authorizationRequest.getState()); + this.mvc.perform(get("/login/oauth2/code/github-login").params(params)) + .andExpect(status().is2xxSuccessful()); ArgumentCaptor authenticationCaptor = ArgumentCaptor.forClass(Authentication.class); verify(authenticationSuccessHandler).onAuthenticationSuccess(any(), any(), authenticationCaptor.capture()); - Authentication authenticationValue = authenticationCaptor.getValue(); - assertThat(authenticationValue.getAuthorities()).hasSize(1); - assertThat(authenticationValue.getAuthorities()).first().isInstanceOf(SimpleGrantedAuthority.class) - .hasToString("ROLE_USER"); + Authentication authentication = authenticationCaptor.getValue(); + assertThat(authentication.getPrincipal()).isInstanceOf(OAuth2User.class); + assertThat(authentication.getAuthorities()).hasSize(1); + assertThat(authentication.getAuthorities()).first().isInstanceOf(SimpleGrantedAuthority.class) + .hasToString("ROLE_OAUTH2_USER"); + + // re-setup for OIDC test + attributes = new HashMap<>(); + attributes.put(OAuth2ParameterNames.REGISTRATION_ID, "google-login"); + authorizationRequest = TestOAuth2AuthorizationRequests.oidcRequest() + .attributes(attributes).build(); + when(this.authorizationRequestRepository.removeAuthorizationRequest(any(), any())).thenReturn(authorizationRequest); + + accessTokenResponse = oidcAccessTokenResponse().build(); + when(this.accessTokenResponseClient.getTokenResponse(any())).thenReturn(accessTokenResponse); + + Jwt jwt = TestJwts.user(); + when(this.jwtDecoderFactory.createDecoder(any())).thenReturn(token -> jwt); + + when(this.userAuthoritiesMapper.mapAuthorities(any())) + .thenReturn((Collection) AuthorityUtils.createAuthorityList("ROLE_OIDC_USER")); + + this.mvc.perform(get("/login/oauth2/code/google-login").params(params)) + .andExpect(status().is2xxSuccessful()); + + authenticationCaptor = ArgumentCaptor.forClass(Authentication.class); + verify(authenticationSuccessHandler, times(2)).onAuthenticationSuccess(any(), any(), + authenticationCaptor.capture()); + authentication = authenticationCaptor.getValue(); + assertThat(authentication.getPrincipal()).isInstanceOf(OidcUser.class); + assertThat(authentication.getAuthorities()).hasSize(1); + assertThat(authentication.getAuthorities()).first().isInstanceOf(SimpleGrantedAuthority.class) + .hasToString("ROLE_OIDC_USER"); + } + + // gh-5488 + @Test + public void requestWhenCustomLoginProcessingUrlThenProcessAuthentication() throws Exception { + this.spring.configLocations(this.xml("MultiClientRegistration-WithCustomLoginProcessingUrl")).autowire(); + + Map attributes = new HashMap<>(); + attributes.put(OAuth2ParameterNames.REGISTRATION_ID, "github-login"); + OAuth2AuthorizationRequest authorizationRequest = TestOAuth2AuthorizationRequests.request() + .attributes(attributes).build(); + when(this.authorizationRequestRepository.removeAuthorizationRequest(any(), any())).thenReturn(authorizationRequest); + + OAuth2AccessTokenResponse accessTokenResponse = accessTokenResponse().build(); + when(this.accessTokenResponseClient.getTokenResponse(any())).thenReturn(accessTokenResponse); + + OAuth2User oauth2User = TestOAuth2Users.create(); + when(this.oauth2UserService.loadUser(any())).thenReturn(oauth2User); + + MultiValueMap params = new LinkedMultiValueMap<>(); + params.add("code", "code123"); + params.add("state", authorizationRequest.getState()); + this.mvc.perform(get("/login/oauth2/github-login").params(params)) + .andExpect(status().is2xxSuccessful()); + + ArgumentCaptor authenticationCaptor = ArgumentCaptor.forClass(Authentication.class); + verify(authenticationSuccessHandler).onAuthenticationSuccess(any(), any(), authenticationCaptor.capture()); + Authentication authentication = authenticationCaptor.getValue(); + assertThat(authentication.getPrincipal()).isInstanceOf(OAuth2User.class); } // gh-5521 @@ -406,17 +372,19 @@ public class OAuth2LoginBeanDefinitionParserTests { this.spring.configLocations(this.xml("SingleClientRegistration-WithCustomAuthorizationRequestResolver")) .autowire(); - this.mvc.perform(get("/oauth2/authorization/google")).andExpect(status().is3xxRedirection()); + this.mvc.perform(get("/oauth2/authorization/google-login")) + .andExpect(status().is3xxRedirection()); - verify(oauth2AuthorizationRequestResolver).resolve(any()); + verify(authorizationRequestResolver).resolve(any()); } // gh-5347 @Test - public void requestWhenMultipleClientsConfiguredThenRedirectDefaultLoginPage() throws Exception { + public void requestWhenMultiClientRegistrationThenRedirectDefaultLoginPage() throws Exception { this.spring.configLocations(this.xml("MultiClientRegistration")).autowire(); - this.mvc.perform(get("/")).andExpect(status().is3xxRedirection()) + this.mvc.perform(get("/")) + .andExpect(status().is3xxRedirection()) .andExpect(redirectedUrl("http://localhost/login")); } @@ -424,7 +392,8 @@ public class OAuth2LoginBeanDefinitionParserTests { public void requestWhenCustomLoginPageThenRedirectCustomLoginPage() throws Exception { this.spring.configLocations(this.xml("SingleClientRegistration-WithCustomLoginPage")).autowire(); - this.mvc.perform(get("/")).andExpect(status().is3xxRedirection()) + this.mvc.perform(get("/")) + .andExpect(status().is3xxRedirection()) .andExpect(redirectedUrl("http://localhost/custom-login")); } @@ -434,7 +403,8 @@ public class OAuth2LoginBeanDefinitionParserTests { throws Exception { this.spring.configLocations(this.xml("SingleClientRegistration-WithFormLogin")).autowire(); - this.mvc.perform(get("/")).andExpect(status().is3xxRedirection()) + this.mvc.perform(get("/")) + .andExpect(status().is3xxRedirection()) .andExpect(redirectedUrl("http://localhost/login")); } @@ -442,39 +412,41 @@ public class OAuth2LoginBeanDefinitionParserTests { public void requestWhenCustomClientRegistrationRepositoryThenCalled() throws Exception { this.spring.configLocations(this.xml("WithCustomClientRegistrationRepository")).autowire(); + ClientRegistration clientRegistration = TestClientRegistrations.clientRegistration().build(); + when(this.clientRegistrationRepository.findByRegistrationId(any())).thenReturn(clientRegistration); + + Map attributes = new HashMap<>(); + attributes.put(OAuth2ParameterNames.REGISTRATION_ID, clientRegistration.getRegistrationId()); + OAuth2AuthorizationRequest authorizationRequest = TestOAuth2AuthorizationRequests.request() + .attributes(attributes).build(); + when(this.authorizationRequestRepository.removeAuthorizationRequest(any(), any())).thenReturn(authorizationRequest); + OAuth2AccessTokenResponse accessTokenResponse = accessTokenResponse().build(); when(this.accessTokenResponseClient.getTokenResponse(any())).thenReturn(accessTokenResponse); OAuth2User oauth2User = TestOAuth2Users.create(); when(this.oauth2UserService.loadUser(any())).thenReturn(oauth2User); - Map attributes = new HashMap<>(); - attributes.put(OAuth2ParameterNames.REGISTRATION_ID, "google-login"); - OAuth2AuthorizationRequest authRequest = TestOAuth2AuthorizationRequests.request().attributes(attributes) - .build(); - when(this.authorizationRequestRepository.removeAuthorizationRequest(any(), any())).thenReturn(authRequest); - MultiValueMap params = new LinkedMultiValueMap<>(); params.add("code", "code123"); - params.add("state", authRequest.getState()); - this.mvc.perform(get("/login/oauth2/code/google").params(params)); + params.add("state", authorizationRequest.getState()); + this.mvc.perform(get("/login/oauth2/code/" + clientRegistration.getRegistrationId()).params(params)); - assertThat(MockUtil.isMock(clientRegistrationRepository)).isTrue(); - verify(clientRegistrationRepository).findByRegistrationId("google-login"); - - Field field = oauth2LoginAuthenticationFilter.getClass().getDeclaredField("clientRegistrationRepository"); - field.setAccessible(true); - Object fieldVal = field.get(oauth2LoginAuthenticationFilter); - assertThat(MockUtil.isMock(fieldVal)).isTrue(); - assertThat(fieldVal).isSameAs(clientRegistrationRepository); + verify(clientRegistrationRepository).findByRegistrationId(clientRegistration.getRegistrationId()); } @Test public void requestWhenCustomAuthorizedClientRepositoryThenCalled() throws Exception { this.spring.configLocations(this.xml("WithCustomAuthorizedClientRepository")).autowire(); - ClientRegistration clientReg = TestClientRegistrations.clientRegistration().build(); - when(this.clientRegistrationRepository.findByRegistrationId(any())).thenReturn(clientReg); + ClientRegistration clientRegistration = TestClientRegistrations.clientRegistration().build(); + when(this.clientRegistrationRepository.findByRegistrationId(any())).thenReturn(clientRegistration); + + Map attributes = new HashMap<>(); + attributes.put(OAuth2ParameterNames.REGISTRATION_ID, clientRegistration.getRegistrationId()); + OAuth2AuthorizationRequest authorizationRequest = TestOAuth2AuthorizationRequests.request() + .attributes(attributes).build(); + when(this.authorizationRequestRepository.removeAuthorizationRequest(any(), any())).thenReturn(authorizationRequest); OAuth2AccessTokenResponse accessTokenResponse = accessTokenResponse().build(); when(this.accessTokenResponseClient.getTokenResponse(any())).thenReturn(accessTokenResponse); @@ -482,76 +454,26 @@ public class OAuth2LoginBeanDefinitionParserTests { OAuth2User oauth2User = TestOAuth2Users.create(); when(this.oauth2UserService.loadUser(any())).thenReturn(oauth2User); - Map attributes = new HashMap<>(); - attributes.put(OAuth2ParameterNames.REGISTRATION_ID, "google-login"); - OAuth2AuthorizationRequest authRequest = TestOAuth2AuthorizationRequests.request().attributes(attributes) - .build(); - when(this.authorizationRequestRepository.removeAuthorizationRequest(any(), any())).thenReturn(authRequest); - MultiValueMap params = new LinkedMultiValueMap<>(); params.add("code", "code123"); - params.add("state", authRequest.getState()); - this.mvc.perform(get("/login/oauth2/code/registration-id").params(params)); + params.add("state", authorizationRequest.getState()); + this.mvc.perform(get("/login/oauth2/code/" + clientRegistration.getRegistrationId()).params(params)); - assertThat(MockUtil.isMock(oauth2AuthorizedClientRepository)).isTrue(); - verify(oauth2AuthorizedClientRepository).saveAuthorizedClient(any(), any(), any(), any()); - - Field field = oauth2LoginAuthenticationFilter.getClass().getDeclaredField("authorizedClientRepository"); - field.setAccessible(true); - Object fieldVal = field.get(oauth2LoginAuthenticationFilter); - assertThat(MockUtil.isMock(fieldVal)).isTrue(); - assertThat(fieldVal).isSameAs(oauth2AuthorizedClientRepository); + verify(authorizedClientRepository).saveAuthorizedClient(any(), any(), any(), any()); } @Test public void requestWhenCustomAuthorizedClientServiceThenCalled() throws Exception { this.spring.configLocations(this.xml("WithCustomAuthorizedClientService")).autowire(); - ClientRegistration clientReg = TestClientRegistrations.clientRegistration().build(); - when(this.clientRegistrationRepository.findByRegistrationId(any())).thenReturn(clientReg); - - OAuth2AccessTokenResponse accessTokenResponse = accessTokenResponse().build(); - when(this.accessTokenResponseClient.getTokenResponse(any())).thenReturn(accessTokenResponse); - - OAuth2User oauth2User = TestOAuth2Users.create(); - when(this.oauth2UserService.loadUser(any())).thenReturn(oauth2User); + ClientRegistration clientRegistration = TestClientRegistrations.clientRegistration().build(); + when(this.clientRegistrationRepository.findByRegistrationId(any())).thenReturn(clientRegistration); Map attributes = new HashMap<>(); - attributes.put(OAuth2ParameterNames.REGISTRATION_ID, "google-login"); - OAuth2AuthorizationRequest authRequest = TestOAuth2AuthorizationRequests.request().attributes(attributes) - .build(); - when(this.authorizationRequestRepository.removeAuthorizationRequest(any(), any())).thenReturn(authRequest); - - MultiValueMap params = new LinkedMultiValueMap<>(); - params.add("code", "code123"); - params.add("state", authRequest.getState()); - this.mvc.perform(get("/login/oauth2/code/registration-id").params(params)); - - assertThat(MockUtil.isMock(oauth2AuthorizedClientService)).isTrue(); - verify(oauth2AuthorizedClientService).saveAuthorizedClient(any(), any()); - - Field authorizedClientRepositoryField = oauth2LoginAuthenticationFilter.getClass() - .getDeclaredField("authorizedClientRepository"); - authorizedClientRepositoryField.setAccessible(true); - Object authorizedClientRepositoryFieldVal = authorizedClientRepositoryField - .get(oauth2LoginAuthenticationFilter); - assertThat(authorizedClientRepositoryFieldVal) - .isInstanceOf(AuthenticatedPrincipalOAuth2AuthorizedClientRepository.class); - - Field authorizedClientServiceField = authorizedClientRepositoryFieldVal.getClass() - .getDeclaredField("authorizedClientService"); - authorizedClientServiceField.setAccessible(true); - Object authorizedClientServiceFieldVal = authorizedClientServiceField.get(authorizedClientRepositoryFieldVal); - assertThat(MockUtil.isMock(authorizedClientServiceFieldVal)).isTrue(); - assertThat(authorizedClientServiceFieldVal).isSameAs(oauth2AuthorizedClientService); - } - - @Test - public void requestWhenCustomAuthorizationRequestRepositoryThenCalled() throws Exception { - this.spring.configLocations(this.xml("WithCustomAuthorizationRequestRepository")).autowire(); - - ClientRegistration clientReg = TestClientRegistrations.clientRegistration().build(); - when(this.clientRegistrationRepository.findByRegistrationId(any())).thenReturn(clientReg); + attributes.put(OAuth2ParameterNames.REGISTRATION_ID, clientRegistration.getRegistrationId()); + OAuth2AuthorizationRequest authorizationRequest = TestOAuth2AuthorizationRequests.request() + .attributes(attributes).build(); + when(this.authorizationRequestRepository.removeAuthorizationRequest(any(), any())).thenReturn(authorizationRequest); OAuth2AccessTokenResponse accessTokenResponse = accessTokenResponse().build(); when(this.accessTokenResponseClient.getTokenResponse(any())).thenReturn(accessTokenResponse); @@ -561,78 +483,13 @@ public class OAuth2LoginBeanDefinitionParserTests { MultiValueMap params = new LinkedMultiValueMap<>(); params.add("code", "code123"); - params.add("state", "state"); - this.mvc.perform(get("/login/oauth2/code/registration-id").params(params)); + params.add("state", authorizationRequest.getState()); + this.mvc.perform(get("/login/oauth2/code/" + clientRegistration.getRegistrationId()).params(params)); - assertThat(MockUtil.isMock(authorizationRequestRepository)).isTrue(); - verify(authorizationRequestRepository).removeAuthorizationRequest(any(), any()); - - Field authorizationRequestRepositoryField = oauth2LoginAuthenticationFilter.getClass() - .getDeclaredField("authorizationRequestRepository"); - authorizationRequestRepositoryField.setAccessible(true); - Object authorizationRequestRepositoryFieldVal = authorizationRequestRepositoryField - .get(oauth2LoginAuthenticationFilter); - assertThat(MockUtil.isMock(authorizationRequestRepositoryFieldVal)).isTrue(); - assertThat(authorizationRequestRepositoryFieldVal).isSameAs(authorizationRequestRepository); - } - - @Test - public void requestWhenCustomAuthenticationSuccessHandlerThenCalled() throws Exception { - this.spring.configLocations(this.xml("SingleClientRegistration-WithCustomAuthenticationHandler")).autowire(); - OAuth2AccessTokenResponse accessTokenResponse = accessTokenResponse().build(); - when(this.accessTokenResponseClient.getTokenResponse(any())).thenReturn(accessTokenResponse); - - OAuth2User oauth2User = TestOAuth2Users.create(); - when(this.oauth2UserService.loadUser(any())).thenReturn(oauth2User); - - Map attributes = new HashMap<>(); - attributes.put(OAuth2ParameterNames.REGISTRATION_ID, "google-login"); - OAuth2AuthorizationRequest authRequest = TestOAuth2AuthorizationRequests.request().attributes(attributes) - .build(); - when(this.authorizationRequestRepository.removeAuthorizationRequest(any(), any())).thenReturn(authRequest); - - MultiValueMap params = new LinkedMultiValueMap<>(); - params.add("code", "code123"); - params.add("state", authRequest.getState()); - this.mvc.perform(get("/login/oauth2/code/google").params(params)).andExpect(status().is2xxSuccessful()); - - ArgumentCaptor authenticationCaptor = ArgumentCaptor.forClass(Authentication.class); - verify(authenticationSuccessHandler).onAuthenticationSuccess(any(), any(), authenticationCaptor.capture()); - Authentication authenticationValue = authenticationCaptor.getValue(); - assertThat(authenticationValue.getAuthorities()).hasSize(1); - assertThat(authenticationValue.getAuthorities()).first().isInstanceOf(SimpleGrantedAuthority.class) - .hasToString("ROLE_USER"); - - } - - @Test - public void requestWhenCustomAuthenticationFailureHandlerThenCalled() throws Exception { - this.spring.configLocations(this.xml("SingleClientRegistration-WithCustomAuthenticationHandler")).autowire(); - - MultiValueMap params = new LinkedMultiValueMap<>(); - params.add("code", "code123"); - params.add("state", "state123"); - this.mvc.perform(get("/login/oauth2/code/google").params(params)).andExpect(status().isIAmATeapot()); + verify(authorizedClientService).saveAuthorizedClient(any(), any()); } private String xml(String configName) { return CONFIG_LOCATION_PREFIX + "-" + configName + ".xml"; } - - public static class TeapotAuthenticationHandler - implements AuthenticationSuccessHandler, AuthenticationFailureHandler { - - @Override - public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, - AuthenticationException exception) { - response.setStatus(HttpStatus.I_AM_A_TEAPOT.value()); - } - - @Override - public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, - Authentication authentication) { - response.setStatus(HttpStatus.I_AM_A_TEAPOT.value()); - } - } - } diff --git a/config/src/test/java/org/springframework/security/config/oauth2/client/ClientRegistrationsBeanDefinitionParserTests.java b/config/src/test/java/org/springframework/security/config/oauth2/client/ClientRegistrationsBeanDefinitionParserTests.java index f6a40c9dbc..606af24ae4 100644 --- a/config/src/test/java/org/springframework/security/config/oauth2/client/ClientRegistrationsBeanDefinitionParserTests.java +++ b/config/src/test/java/org/springframework/security/config/oauth2/client/ClientRegistrationsBeanDefinitionParserTests.java @@ -15,9 +15,9 @@ */ package org.springframework.security.config.oauth2.client; -import static org.assertj.core.api.Assertions.assertThat; - -import java.util.Map; +import okhttp3.mockwebserver.MockResponse; +import okhttp3.mockwebserver.MockWebServer; +import org.junit.After; import org.junit.Rule; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -33,13 +33,7 @@ import org.springframework.security.oauth2.core.AuthorizationGrantType; import org.springframework.security.oauth2.core.ClientAuthenticationMethod; import org.springframework.util.StringUtils; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.ObjectMapper; - -import okhttp3.mockwebserver.Dispatcher; -import okhttp3.mockwebserver.MockResponse; -import okhttp3.mockwebserver.MockWebServer; -import okhttp3.mockwebserver.RecordedRequest; +import static org.assertj.core.api.Assertions.assertThat; /** * Tests for {@link ClientRegistrationsBeanDefinitionParser}. @@ -49,13 +43,24 @@ import okhttp3.mockwebserver.RecordedRequest; public class ClientRegistrationsBeanDefinitionParserTests { private static final String CONFIG_LOCATION_PREFIX = "classpath:org/springframework/security/config/oauth2/client/ClientRegistrationsBeanDefinitionParserTests"; - @Rule - public final SpringTestRule spring = new SpringTestRule(); + private static final String ISSUER_URI_XML_CONFIG = "\n" + + "\n" + + "\t\n" + + "\t\t\n" + + "\t\t\n" + + "\t\n" + + "\n" + + "\n"; - @Autowired - private ClientRegistrationRepository clientRegistrationRepository; - - private static final String DEFAULT_RESPONSE = + private static final String OIDC_DISCOVERY_RESPONSE = "{\n" + " \"authorization_endpoint\": \"https://example.com/o/oauth2/v2/auth\", \n" + " \"claims_supported\": [\n" @@ -79,7 +84,7 @@ public class ClientRegistrationsBeanDefinitionParserTests { + " \"id_token_signing_alg_values_supported\": [\n" + " \"RS256\"\n" + " ], \n" - + " \"issuer\": \"http://localhost:49259\", \n" + + " \"issuer\": \"${issuer-uri}\", \n" + " \"jwks_uri\": \"https://example.com/oauth2/v3/certs\", \n" + " \"response_types_supported\": [\n" + " \"code\", \n" @@ -110,71 +115,47 @@ public class ClientRegistrationsBeanDefinitionParserTests { + " \"userinfo_endpoint\": \"https://example.com/oauth2/v3/userinfo\"\n" + "}"; + @Autowired + private ClientRegistrationRepository clientRegistrationRepository; + + @Rule + public final SpringTestRule spring = new SpringTestRule(); + + private MockWebServer server; + + @After + public void cleanup() throws Exception { + if (this.server != null) { + this.server.shutdown(); + } + } + @Test public void parseWhenIssuerUriConfiguredThenRequestConfigFromIssuer() throws Exception { - MockWebServer server = new MockWebServer(); - ObjectMapper mapper = new ObjectMapper(); - server.start(49259); - Map response = mapper.readValue(DEFAULT_RESPONSE, new TypeReference>() { - }); - final String responseBody = mapper.writeValueAsString(response); + this.server = new MockWebServer(); + this.server.start(); + String serverUrl = this.server.url("/").toString(); - final Dispatcher oidcDispatcher = new Dispatcher() { - @Override - public MockResponse dispatch(RecordedRequest request) { - switch (request.getPath()) { - case "/issuer1/.well-known/openid-configuration": - case "/.well-known/openid-configuration": - return new MockResponse().setResponseCode(200).setBody(responseBody) - .setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE); - } - return new MockResponse().setResponseCode(404); - } - }; + String discoveryResponse = OIDC_DISCOVERY_RESPONSE.replace("${issuer-uri}", serverUrl); + this.server.enqueue(jsonResponse(discoveryResponse)); - final Dispatcher oauthDispatcher = new Dispatcher() { - @Override - public MockResponse dispatch(RecordedRequest request) { - switch (request.getPath()) { - case "/.well-known/oauth-authorization-server/issuer1": - case "/.well-known/oauth-authorization-server": - return new MockResponse().setResponseCode(200).setBody(responseBody) - .setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE); - } - return new MockResponse().setResponseCode(404); - } - }; - - server.setDispatcher(oidcDispatcher); - - this.spring.configLocations(this.xml("FromIssuerUri")).autowire(); + String contextConfig = ISSUER_URI_XML_CONFIG.replace("${issuer-uri}", serverUrl); + this.spring.context(contextConfig).autowire(); assertThat(clientRegistrationRepository).isInstanceOf(InMemoryClientRegistrationRepository.class); - testIssuerUriResponse(clientRegistrationRepository); + ClientRegistration googleRegistration = clientRegistrationRepository.findByRegistrationId("google-login"); + assertThat(googleRegistration).isNotNull(); + assertThat(googleRegistration.getRegistrationId()).isEqualTo("google-login"); + assertThat(googleRegistration.getClientId()).isEqualTo("google-client-id"); + assertThat(googleRegistration.getClientSecret()).isEqualTo("google-client-secret"); + assertThat(googleRegistration.getClientAuthenticationMethod()).isEqualTo(ClientAuthenticationMethod.BASIC); + assertThat(googleRegistration.getAuthorizationGrantType()).isEqualTo(AuthorizationGrantType.AUTHORIZATION_CODE); + assertThat(googleRegistration.getRedirectUriTemplate()).isEqualTo("{baseUrl}/{action}/oauth2/code/{registrationId}"); + assertThat(googleRegistration.getScopes()).isEqualTo(StringUtils.commaDelimitedListToSet("openid,profile,email")); + assertThat(googleRegistration.getClientName()).isEqualTo(serverUrl); - // test oauth - server.setDispatcher(oauthDispatcher); - this.spring.configLocations(this.xml("FromIssuerUri")).autowire(); - testIssuerUriResponse(clientRegistrationRepository); - - server.shutdown(); - server.close(); - } - - private void testIssuerUriResponse(ClientRegistrationRepository clientRegistrationRepository) { - ClientRegistration googleLogin = clientRegistrationRepository.findByRegistrationId("google-login"); - assertThat(googleLogin).isNotNull(); - assertThat(googleLogin.getRegistrationId()).isEqualTo("google-login"); - assertThat(googleLogin.getClientId()).isEqualTo("google-client-id"); - assertThat(googleLogin.getClientSecret()).isEqualTo("google-client-secret"); - assertThat(googleLogin.getClientAuthenticationMethod()).isEqualTo(ClientAuthenticationMethod.BASIC); - assertThat(googleLogin.getAuthorizationGrantType()).isEqualTo(AuthorizationGrantType.AUTHORIZATION_CODE); - assertThat(googleLogin.getRedirectUriTemplate()).isEqualTo("{baseUrl}/login/oauth2/code/{registrationId}"); - assertThat(googleLogin.getScopes()).isEqualTo(StringUtils.commaDelimitedListToSet("openid,profile,email")); - assertThat(googleLogin.getClientName()).isEqualTo("Google"); - - ProviderDetails googleProviderDetails = googleLogin.getProviderDetails(); + ProviderDetails googleProviderDetails = googleRegistration.getProviderDetails(); assertThat(googleProviderDetails).isNotNull(); assertThat(googleProviderDetails.getAuthorizationUri()).isEqualTo("https://example.com/o/oauth2/v2/auth"); assertThat(googleProviderDetails.getTokenUri()).isEqualTo("https://example.com/oauth2/v4/token"); @@ -187,23 +168,23 @@ public class ClientRegistrationsBeanDefinitionParserTests { } @Test - public void parseWhenMultipleClientsConfiguredThenAvailableInRepository() throws Exception { + public void parseWhenMultipleClientsConfiguredThenAvailableInRepository() { this.spring.configLocations(this.xml("MultiClientRegistration")).autowire(); assertThat(clientRegistrationRepository).isInstanceOf(InMemoryClientRegistrationRepository.class); - ClientRegistration googleLogin = clientRegistrationRepository.findByRegistrationId("google-login"); - assertThat(googleLogin).isNotNull(); - assertThat(googleLogin.getRegistrationId()).isEqualTo("google-login"); - assertThat(googleLogin.getClientId()).isEqualTo("google-client-id"); - assertThat(googleLogin.getClientSecret()).isEqualTo("google-client-secret"); - assertThat(googleLogin.getClientAuthenticationMethod()).isEqualTo(ClientAuthenticationMethod.BASIC); - assertThat(googleLogin.getAuthorizationGrantType()).isEqualTo(AuthorizationGrantType.AUTHORIZATION_CODE); - assertThat(googleLogin.getRedirectUriTemplate()).isEqualTo("{baseUrl}/login/oauth2/code/{registrationId}"); - assertThat(googleLogin.getScopes()).isEqualTo(StringUtils.commaDelimitedListToSet("openid,profile,email")); - assertThat(googleLogin.getClientName()).isEqualTo("Google"); + ClientRegistration googleRegistration = clientRegistrationRepository.findByRegistrationId("google-login"); + assertThat(googleRegistration).isNotNull(); + assertThat(googleRegistration.getRegistrationId()).isEqualTo("google-login"); + assertThat(googleRegistration.getClientId()).isEqualTo("google-client-id"); + assertThat(googleRegistration.getClientSecret()).isEqualTo("google-client-secret"); + assertThat(googleRegistration.getClientAuthenticationMethod()).isEqualTo(ClientAuthenticationMethod.BASIC); + assertThat(googleRegistration.getAuthorizationGrantType()).isEqualTo(AuthorizationGrantType.AUTHORIZATION_CODE); + assertThat(googleRegistration.getRedirectUriTemplate()).isEqualTo("{baseUrl}/login/oauth2/code/{registrationId}"); + assertThat(googleRegistration.getScopes()).isEqualTo(StringUtils.commaDelimitedListToSet("openid,profile,email")); + assertThat(googleRegistration.getClientName()).isEqualTo("Google"); - ProviderDetails googleProviderDetails = googleLogin.getProviderDetails(); + ProviderDetails googleProviderDetails = googleRegistration.getProviderDetails(); assertThat(googleProviderDetails).isNotNull(); assertThat(googleProviderDetails.getAuthorizationUri()) .isEqualTo("https://accounts.google.com/o/oauth2/v2/auth"); @@ -215,18 +196,18 @@ public class ClientRegistrationsBeanDefinitionParserTests { assertThat(googleProviderDetails.getUserInfoEndpoint().getUserNameAttributeName()).isEqualTo("sub"); assertThat(googleProviderDetails.getJwkSetUri()).isEqualTo("https://www.googleapis.com/oauth2/v3/certs"); - ClientRegistration githubLogin = clientRegistrationRepository.findByRegistrationId("github-login"); - assertThat(githubLogin).isNotNull(); - assertThat(githubLogin.getRegistrationId()).isEqualTo("github-login"); - assertThat(githubLogin.getClientId()).isEqualTo("github-client-id"); - assertThat(githubLogin.getClientSecret()).isEqualTo("github-client-secret"); - assertThat(githubLogin.getClientAuthenticationMethod()).isEqualTo(ClientAuthenticationMethod.BASIC); - assertThat(githubLogin.getAuthorizationGrantType()).isEqualTo(AuthorizationGrantType.AUTHORIZATION_CODE); - assertThat(githubLogin.getRedirectUriTemplate()).isEqualTo("{baseUrl}/login/oauth2/code/{registrationId}"); - assertThat(googleLogin.getScopes()).isEqualTo(StringUtils.commaDelimitedListToSet("openid,profile,email")); - assertThat(githubLogin.getClientName()).isEqualTo("Github"); + ClientRegistration githubRegistration = clientRegistrationRepository.findByRegistrationId("github-login"); + assertThat(githubRegistration).isNotNull(); + assertThat(githubRegistration.getRegistrationId()).isEqualTo("github-login"); + assertThat(githubRegistration.getClientId()).isEqualTo("github-client-id"); + assertThat(githubRegistration.getClientSecret()).isEqualTo("github-client-secret"); + assertThat(githubRegistration.getClientAuthenticationMethod()).isEqualTo(ClientAuthenticationMethod.BASIC); + assertThat(githubRegistration.getAuthorizationGrantType()).isEqualTo(AuthorizationGrantType.AUTHORIZATION_CODE); + assertThat(githubRegistration.getRedirectUriTemplate()).isEqualTo("{baseUrl}/login/oauth2/code/{registrationId}"); + assertThat(googleRegistration.getScopes()).isEqualTo(StringUtils.commaDelimitedListToSet("openid,profile,email")); + assertThat(githubRegistration.getClientName()).isEqualTo("Github"); - ProviderDetails githubProviderDetails = githubLogin.getProviderDetails(); + ProviderDetails githubProviderDetails = githubRegistration.getProviderDetails(); assertThat(githubProviderDetails).isNotNull(); assertThat(githubProviderDetails.getAuthorizationUri()).isEqualTo("https://github.com/login/oauth/authorize"); assertThat(githubProviderDetails.getTokenUri()).isEqualTo("https://github.com/login/oauth/access_token"); @@ -236,7 +217,13 @@ public class ClientRegistrationsBeanDefinitionParserTests { assertThat(githubProviderDetails.getUserInfoEndpoint().getUserNameAttributeName()).isEqualTo("id"); } - private String xml(String configName) { + private static MockResponse jsonResponse(String json) { + return new MockResponse() + .setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) + .setBody(json); + } + + private static String xml(String configName) { return CONFIG_LOCATION_PREFIX + "-" + configName + ".xml"; } } diff --git a/config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-SingleClientRegistration-WithCustomAuthenticationHandler.xml b/config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-MultiClientRegistration-WithCustomConfiguration.xml similarity index 76% rename from config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-SingleClientRegistration-WithCustomAuthenticationHandler.xml rename to config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-MultiClientRegistration-WithCustomConfiguration.xml index fd8cbe6c4d..0b4b8a4a20 100644 --- a/config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-SingleClientRegistration-WithCustomAuthenticationHandler.xml +++ b/config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-MultiClientRegistration-WithCustomConfiguration.xml @@ -29,25 +29,25 @@ + authentication-success-handler-ref="authenticationSuccessHandler"/> - + - + - + + + + - + - - - + diff --git a/config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-SingleClientRegistration-WithCustomGrantedAuthorities.xml b/config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-MultiClientRegistration-WithCustomGrantedAuthorities.xml similarity index 78% rename from config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-SingleClientRegistration-WithCustomGrantedAuthorities.xml rename to config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-MultiClientRegistration-WithCustomGrantedAuthorities.xml index 9997826ac1..f0f7cecc8d 100644 --- a/config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-SingleClientRegistration-WithCustomGrantedAuthorities.xml +++ b/config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-MultiClientRegistration-WithCustomGrantedAuthorities.xml @@ -28,31 +28,31 @@ - - + + - + - + - + - + - + - + diff --git a/config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-SingleClientRegistration-WithCustomLoginProcessingUrl.xml b/config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-MultiClientRegistration-WithCustomLoginProcessingUrl.xml similarity index 83% rename from config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-SingleClientRegistration-WithCustomLoginProcessingUrl.xml rename to config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-MultiClientRegistration-WithCustomLoginProcessingUrl.xml index 531f0aa3e3..8d075bc6fd 100644 --- a/config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-SingleClientRegistration-WithCustomLoginProcessingUrl.xml +++ b/config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-MultiClientRegistration-WithCustomLoginProcessingUrl.xml @@ -32,20 +32,20 @@ authorization-request-repository-ref="authorizationRequestRepository" authentication-success-handler-ref="authenticationSuccessHandler" /> - - + + - + - + - + - + diff --git a/config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-SingleClientRegistration-WithCustomAuthenticationFailureHandler.xml b/config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-SingleClientRegistration-WithCustomAuthenticationFailureHandler.xml index d49e4257aa..4b5e241d76 100644 --- a/config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-SingleClientRegistration-WithCustomAuthenticationFailureHandler.xml +++ b/config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-SingleClientRegistration-WithCustomAuthenticationFailureHandler.xml @@ -29,7 +29,7 @@ - + diff --git a/config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-SingleClientRegistration-WithCustomAuthorizationRequestResolver.xml b/config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-SingleClientRegistration-WithCustomAuthorizationRequestResolver.xml index 26c02c03c6..f40da7c40e 100644 --- a/config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-SingleClientRegistration-WithCustomAuthorizationRequestResolver.xml +++ b/config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-SingleClientRegistration-WithCustomAuthorizationRequestResolver.xml @@ -26,10 +26,10 @@ - + - + diff --git a/config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-SingleClientRegistration-WithJwtDecoderFactory.xml b/config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-SingleClientRegistration-WithJwtDecoderFactory.xml index 20f3bd9114..e695599875 100644 --- a/config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-SingleClientRegistration-WithJwtDecoderFactory.xml +++ b/config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-SingleClientRegistration-WithJwtDecoderFactory.xml @@ -28,24 +28,24 @@ - - + + - + - + - + - + diff --git a/config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-SingleClientRegistration-WithJwtDecoderFactoryAndDefaultSuccessHandler.xml b/config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-SingleClientRegistration-WithJwtDecoderFactoryAndDefaultSuccessHandler.xml index 80633b2289..de5b7547ef 100644 --- a/config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-SingleClientRegistration-WithJwtDecoderFactoryAndDefaultSuccessHandler.xml +++ b/config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-SingleClientRegistration-WithJwtDecoderFactoryAndDefaultSuccessHandler.xml @@ -28,23 +28,22 @@ + authorization-request-repository-ref="authorizationRequestRepository"/> + - - + + - + - - - - + + + + diff --git a/config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-SingleClientRegistration-WithTestConfiguration.xml b/config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-SingleClientRegistration-WithTestConfiguration.xml deleted file mode 100644 index 6e69c374ec..0000000000 --- a/config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-SingleClientRegistration-WithTestConfiguration.xml +++ /dev/null @@ -1,57 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-SingleClientRegistration-WithinSameFile.xml b/config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-SingleClientRegistration-WithinSameFile.xml deleted file mode 100644 index d176bfdd31..0000000000 --- a/config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-SingleClientRegistration-WithinSameFile.xml +++ /dev/null @@ -1,52 +0,0 @@ - - - - - - - - - - - - - - - - - diff --git a/config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-SingleClientRegistration.xml b/config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-SingleClientRegistration.xml index 2bef962eed..66660c5a4a 100644 --- a/config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-SingleClientRegistration.xml +++ b/config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-SingleClientRegistration.xml @@ -27,8 +27,31 @@ + - + + + + + + + + + diff --git a/config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-WithCustomAuthorizationRequestRepository.xml b/config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-WithCustomAuthorizationRequestRepository.xml deleted file mode 100644 index 3c4071ee06..0000000000 --- a/config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-WithCustomAuthorizationRequestRepository.xml +++ /dev/null @@ -1,49 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-WithCustomAuthorizedClientRepository.xml b/config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-WithCustomAuthorizedClientRepository.xml index e3ffdc7a63..cef5a89a80 100644 --- a/config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-WithCustomAuthorizedClientRepository.xml +++ b/config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-WithCustomAuthorizedClientRepository.xml @@ -33,19 +33,19 @@ authorization-request-repository-ref="authorizationRequestRepository"/> - + - + - + - + - + diff --git a/config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-WithCustomAuthorizedClientService.xml b/config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-WithCustomAuthorizedClientService.xml index 593f329422..e0c5787219 100644 --- a/config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-WithCustomAuthorizedClientService.xml +++ b/config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-WithCustomAuthorizedClientService.xml @@ -33,19 +33,19 @@ authorized-client-service-ref="authorizedClientService"/> - + - + - + - + - + diff --git a/config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-WithCustomClientRegistrationRepository.xml b/config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-WithCustomClientRegistrationRepository.xml index c700b5fb4c..d2f5947c4d 100644 --- a/config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-WithCustomClientRegistrationRepository.xml +++ b/config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-WithCustomClientRegistrationRepository.xml @@ -32,19 +32,18 @@ authorization-request-repository-ref="authorizationRequestRepository"/> - + - + - + - + - diff --git a/config/src/test/resources/org/springframework/security/config/oauth2/client/ClientRegistrationsBeanDefinitionParserTests-FromIssuerUri.xml b/config/src/test/resources/org/springframework/security/config/oauth2/client/ClientRegistrationsBeanDefinitionParserTests-FromIssuerUri.xml deleted file mode 100644 index cbf4680a76..0000000000 --- a/config/src/test/resources/org/springframework/security/config/oauth2/client/ClientRegistrationsBeanDefinitionParserTests-FromIssuerUri.xml +++ /dev/null @@ -1,39 +0,0 @@ - - - - - - - - - diff --git a/config/src/test/resources/org/springframework/security/config/oauth2/client/google-github-registration.xml b/config/src/test/resources/org/springframework/security/config/oauth2/client/google-github-registration.xml index fc27e86b03..004657dfe6 100644 --- a/config/src/test/resources/org/springframework/security/config/oauth2/client/google-github-registration.xml +++ b/config/src/test/resources/org/springframework/security/config/oauth2/client/google-github-registration.xml @@ -39,7 +39,7 @@ client-authentication-method="basic" authorization-grant-type="authorization_code" redirect-uri="{baseUrl}/login/oauth2/code/{registrationId}" - scope="openid,profile,email" + scope="read:user" client-name="Github" provider-id="github"/> > feature configures authentication support us [[nsa-oauth2-login-client-registration-repository-ref]] * **client-registration-repository-ref** -Reference to `ClientRegistrationRepository`. +Reference to the `ClientRegistrationRepository`. [[nsa-oauth2-login-authorized-client-repository-ref]] * **authorized-client-repository-ref** -Reference to `OAuth2AuthorizedClientRepository`. +Reference to the `OAuth2AuthorizedClientRepository`. [[nsa-oauth2-login-authorized-client-service-ref]] * **authorized-client-service-ref** -Reference to `OAuth2AuthorizedClientService`. +Reference to the `OAuth2AuthorizedClientService`. [[nsa-oauth2-login-authorization-request-repository-ref]] * **authorization-request-repository-ref** -Reference to `AuthorizationRequestRepository`. +Reference to the `AuthorizationRequestRepository`. [[nsa-oauth2-login-authorization-request-resolver-ref]] * **authorization-request-resolver-ref** -Reference to `OAuth2AuthorizationRequestResolver`. +Reference to the `OAuth2AuthorizationRequestResolver`. [[nsa-oauth2-login-access-token-response-client-ref]] * **access-token-response-client-ref** -Reference to `OAuth2AccessTokenResponseClient`. +Reference to the `OAuth2AccessTokenResponseClient`. [[nsa-oauth2-login-user-authorities-mapper-ref]] * **user-authorities-mapper-ref** -Reference to `GrantedAuthoritiesMapper`. +Reference to the `GrantedAuthoritiesMapper`. [[nsa-oauth2-login-user-service-ref]] * **user-service-ref** -Reference to `OAuth2UserService`. +Reference to the `OAuth2UserService`. [[nsa-oauth2-login-oidc-user-service-ref]] * **oidc-user-service-ref** -Reference to `OidcUserService`. +Reference to the OpenID Connect `OAuth2UserService`. [[nsa-oauth2-login-login-processing-url]] * **login-processing-url** -Specifies the URL to validate the credentials. +The URI where the filter processes authentication requests. [[nsa-oauth2-login-login-page]] * **login-page** -Specifies the URL to send users to if login is required +The URI to send users to login. [[nsa-oauth2-login-authentication-success-handler-ref]] * **authentication-success-handler-ref** -Specifies authentication success handler +Reference to the `AuthenticationSuccessHandler`. [[nsa-oauth2-login-authentication-failure-handler-ref]] * **authentication-failure-handler-ref** -Specifies authentication failure handler +Reference to the `AuthenticationFailureHandler`. [[nsa-oauth2-login-jwt-decoder-factory-ref]] * **jwt-decoder-factory-ref** -Specifies JWT decoder factory for OidcAuthorizationCodeAuthenticationProvider +Reference to the `JwtDecoderFactory` used by `OidcAuthorizationCodeAuthenticationProvider`. [[nsa-client-registrations]] diff --git a/oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/endpoint/TestOAuth2AccessTokenResponses.java b/oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/endpoint/TestOAuth2AccessTokenResponses.java index 15ba57487b..3cee5f9ef1 100644 --- a/oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/endpoint/TestOAuth2AccessTokenResponses.java +++ b/oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/endpoint/TestOAuth2AccessTokenResponses.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2020 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. @@ -17,6 +17,10 @@ package org.springframework.security.oauth2.core.endpoint; import org.springframework.security.oauth2.core.OAuth2AccessToken; +import org.springframework.security.oauth2.core.oidc.endpoint.OidcParameterNames; + +import java.util.HashMap; +import java.util.Map; /** * @author Rob Winch @@ -27,4 +31,11 @@ public class TestOAuth2AccessTokenResponses { return OAuth2AccessTokenResponse.withToken("token") .tokenType(OAuth2AccessToken.TokenType.BEARER); } + + public static OAuth2AccessTokenResponse.Builder oidcAccessTokenResponse() { + Map additionalParameters = new HashMap<>(); + additionalParameters.put(OidcParameterNames.ID_TOKEN, "id-token"); + return accessTokenResponse() + .additionalParameters(additionalParameters); + } } diff --git a/oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/endpoint/TestOAuth2AuthorizationRequests.java b/oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/endpoint/TestOAuth2AuthorizationRequests.java index 4cb6b09995..b160cdbf32 100644 --- a/oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/endpoint/TestOAuth2AuthorizationRequests.java +++ b/oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/endpoint/TestOAuth2AuthorizationRequests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2020 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. @@ -36,4 +36,8 @@ public class TestOAuth2AuthorizationRequests { .state("state") .attributes(attributes); } + + public static OAuth2AuthorizationRequest.Builder oidcRequest() { + return request().scope("openid"); + } }