diff --git a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OAuth2LoginConfigurer.java b/config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OAuth2LoginConfigurer.java index aa0e960496..bb0455d99e 100644 --- a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OAuth2LoginConfigurer.java +++ b/config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OAuth2LoginConfigurer.java @@ -29,7 +29,6 @@ import org.springframework.security.oauth2.client.authentication.jwt.NimbusJwtDe import org.springframework.security.oauth2.client.authentication.userinfo.CustomUserTypesOAuth2UserService; import org.springframework.security.oauth2.client.authentication.userinfo.DefaultOAuth2UserService; import org.springframework.security.oauth2.client.authentication.userinfo.DelegatingOAuth2UserService; -import org.springframework.security.oauth2.client.authentication.userinfo.OAuth2UserAuthenticationProvider; import org.springframework.security.oauth2.client.authentication.userinfo.OAuth2UserService; import org.springframework.security.oauth2.client.registration.ClientRegistration; import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; @@ -42,7 +41,6 @@ import org.springframework.security.oauth2.core.AccessToken; import org.springframework.security.oauth2.core.endpoint.AuthorizationRequestUriBuilder; import org.springframework.security.oauth2.core.user.OAuth2User; import org.springframework.security.oauth2.oidc.client.authentication.OidcAuthorizationCodeAuthenticationProvider; -import org.springframework.security.oauth2.oidc.client.authentication.userinfo.OidcUserAuthenticationProvider; import org.springframework.security.oauth2.oidc.client.authentication.userinfo.OidcUserService; import org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter; import org.springframework.security.web.util.matcher.AntPathRequestMatcher; @@ -232,58 +230,53 @@ public final class OAuth2LoginConfigurer> exten authorizationCodeTokenExchanger = new NimbusAuthorizationCodeTokenExchanger(); } + OAuth2UserService oauth2UserService = this.userInfoEndpointConfig.userService; + if (oauth2UserService == null) { + if (!this.userInfoEndpointConfig.customUserTypes.isEmpty()) { + List userServices = new ArrayList<>(); + userServices.add(new CustomUserTypesOAuth2UserService(this.userInfoEndpointConfig.customUserTypes)); + userServices.add(new DefaultOAuth2UserService()); + oauth2UserService = new DelegatingOAuth2UserService(userServices); + } else { + oauth2UserService = new DefaultOAuth2UserService(); + } + } + JwtDecoderRegistry jwtDecoderRegistry = this.tokenEndpointConfig.jwtDecoderRegistry; if (jwtDecoderRegistry == null) { jwtDecoderRegistry = new NimbusJwtDecoderRegistry(); } AuthorizationCodeAuthenticationProvider oauth2AuthorizationCodeAuthenticationProvider = - new AuthorizationCodeAuthenticationProvider(authorizationCodeTokenExchanger); + new AuthorizationCodeAuthenticationProvider(authorizationCodeTokenExchanger, oauth2UserService); if (this.tokenEndpointConfig.accessTokenRepository != null) { oauth2AuthorizationCodeAuthenticationProvider.setAccessTokenRepository( this.tokenEndpointConfig.accessTokenRepository); } + if (this.userInfoEndpointConfig.userAuthoritiesMapper != null) { + oauth2AuthorizationCodeAuthenticationProvider.setAuthoritiesMapper( + this.userInfoEndpointConfig.userAuthoritiesMapper); + } http.authenticationProvider(this.postProcess(oauth2AuthorizationCodeAuthenticationProvider)); + OAuth2UserService oidcUserService = this.userInfoEndpointConfig.userService; + if (oidcUserService == null) { + oidcUserService = new OidcUserService(); + } + OidcAuthorizationCodeAuthenticationProvider oidcAuthorizationCodeAuthenticationProvider = - new OidcAuthorizationCodeAuthenticationProvider(authorizationCodeTokenExchanger, jwtDecoderRegistry); + new OidcAuthorizationCodeAuthenticationProvider( + authorizationCodeTokenExchanger, oidcUserService, jwtDecoderRegistry); if (this.tokenEndpointConfig.accessTokenRepository != null) { oidcAuthorizationCodeAuthenticationProvider.setAccessTokenRepository( this.tokenEndpointConfig.accessTokenRepository); } + if (this.userInfoEndpointConfig.userAuthoritiesMapper != null) { + oidcAuthorizationCodeAuthenticationProvider.setAuthoritiesMapper( + this.userInfoEndpointConfig.userAuthoritiesMapper); + } http.authenticationProvider(this.postProcess(oidcAuthorizationCodeAuthenticationProvider)); - OAuth2UserService userService = this.userInfoEndpointConfig.userService; - if (userService == null) { - if (!this.userInfoEndpointConfig.customUserTypes.isEmpty()) { - List userServices = new ArrayList<>(); - userServices.add(new CustomUserTypesOAuth2UserService(this.userInfoEndpointConfig.customUserTypes)); - userServices.add(new DefaultOAuth2UserService()); - userService = new DelegatingOAuth2UserService(userServices); - } else { - userService = new DefaultOAuth2UserService(); - } - } - - OAuth2UserAuthenticationProvider oauth2UserAuthenticationProvider = - new OAuth2UserAuthenticationProvider(userService); - if (this.userInfoEndpointConfig.userAuthoritiesMapper != null) { - oauth2UserAuthenticationProvider.setAuthoritiesMapper(this.userInfoEndpointConfig.userAuthoritiesMapper); - } - http.authenticationProvider(this.postProcess(oauth2UserAuthenticationProvider)); - - userService = this.userInfoEndpointConfig.userService; - if (userService == null) { - userService = new OidcUserService(); - } - - OidcUserAuthenticationProvider oidcUserAuthenticationProvider = - new OidcUserAuthenticationProvider(userService); - if (this.userInfoEndpointConfig.userAuthoritiesMapper != null) { - oidcUserAuthenticationProvider.setAuthoritiesMapper(this.userInfoEndpointConfig.userAuthoritiesMapper); - } - http.authenticationProvider(this.postProcess(oidcUserAuthenticationProvider)); - this.initDefaultLoginFilter(http); } diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/authentication/AuthorizationCodeAuthenticationProvider.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/authentication/AuthorizationCodeAuthenticationProvider.java index adfaec111e..0ec311859c 100644 --- a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/authentication/AuthorizationCodeAuthenticationProvider.java +++ b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/authentication/AuthorizationCodeAuthenticationProvider.java @@ -18,6 +18,10 @@ package org.springframework.security.oauth2.client.authentication; import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper; +import org.springframework.security.oauth2.client.authentication.userinfo.OAuth2AuthenticationToken; +import org.springframework.security.oauth2.client.authentication.userinfo.OAuth2UserService; import org.springframework.security.oauth2.client.token.InMemoryAccessTokenRepository; import org.springframework.security.oauth2.client.token.SecurityTokenRepository; import org.springframework.security.oauth2.core.AccessToken; @@ -25,8 +29,11 @@ import org.springframework.security.oauth2.core.OAuth2Error; import org.springframework.security.oauth2.core.endpoint.AuthorizationRequest; import org.springframework.security.oauth2.core.endpoint.AuthorizationResponse; import org.springframework.security.oauth2.core.endpoint.TokenResponse; +import org.springframework.security.oauth2.core.user.OAuth2User; import org.springframework.util.Assert; +import java.util.Collection; + /** * An implementation of an {@link AuthenticationProvider} * for the OAuth 2.0 Authorization Code Grant Flow. @@ -34,12 +41,19 @@ import org.springframework.util.Assert; * This {@link AuthenticationProvider} is responsible for authenticating * an authorization code credential with the authorization server's Token Endpoint * and if valid, exchanging it for an access token credential. + *

+ * It will also obtain the user attributes of the End-User (resource owner) + * from the UserInfo Endpoint using an {@link OAuth2UserService} + * which will create a Principal in the form of an {@link OAuth2User}. * * @author Joe Grandja * @since 5.0 * @see AuthorizationCodeAuthenticationToken - * @see OAuth2ClientAuthenticationToken * @see SecurityTokenRepository + * @see OAuth2AuthenticationToken + * @see OAuth2ClientAuthenticationToken + * @see OAuth2UserService + * @see OAuth2User * @see Section 4.1 Authorization Code Grant Flow * @see Section 4.1.3 Access Token Request * @see Section 4.1.4 Access Token Response @@ -48,13 +62,18 @@ public class AuthorizationCodeAuthenticationProvider implements AuthenticationPr private static final String INVALID_STATE_PARAMETER_ERROR_CODE = "invalid_state_parameter"; private static final String INVALID_REDIRECT_URI_PARAMETER_ERROR_CODE = "invalid_redirect_uri_parameter"; private final AuthorizationGrantTokenExchanger authorizationCodeTokenExchanger; + private final OAuth2UserService userService; private SecurityTokenRepository accessTokenRepository = new InMemoryAccessTokenRepository(); + private GrantedAuthoritiesMapper authoritiesMapper = (authorities -> authorities); public AuthorizationCodeAuthenticationProvider( - AuthorizationGrantTokenExchanger authorizationCodeTokenExchanger) { + AuthorizationGrantTokenExchanger authorizationCodeTokenExchanger, + OAuth2UserService userService) { Assert.notNull(authorizationCodeTokenExchanger, "authorizationCodeTokenExchanger cannot be null"); + Assert.notNull(userService, "userService cannot be null"); this.authorizationCodeTokenExchanger = authorizationCodeTokenExchanger; + this.userService = userService; } @Override @@ -107,7 +126,16 @@ public class AuthorizationCodeAuthenticationProvider implements AuthenticationPr clientAuthentication.getAccessToken(), clientAuthentication.getClientRegistration()); - return clientAuthentication; + OAuth2User oauth2User = this.userService.loadUser(clientAuthentication); + + Collection mappedAuthorities = + this.authoritiesMapper.mapAuthorities(oauth2User.getAuthorities()); + + OAuth2AuthenticationToken authenticationResult = new OAuth2AuthenticationToken( + oauth2User, mappedAuthorities, clientAuthentication); + authenticationResult.setDetails(clientAuthentication.getDetails()); + + return authenticationResult; } public final void setAccessTokenRepository(SecurityTokenRepository accessTokenRepository) { @@ -115,6 +143,11 @@ public class AuthorizationCodeAuthenticationProvider implements AuthenticationPr this.accessTokenRepository = accessTokenRepository; } + public final void setAuthoritiesMapper(GrantedAuthoritiesMapper authoritiesMapper) { + Assert.notNull(authoritiesMapper, "authoritiesMapper cannot be null"); + this.authoritiesMapper = authoritiesMapper; + } + @Override public boolean supports(Class authentication) { return AuthorizationCodeAuthenticationToken.class.isAssignableFrom(authentication); diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/authentication/userinfo/OAuth2UserAuthenticationToken.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/authentication/userinfo/OAuth2AuthenticationToken.java similarity index 87% rename from oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/authentication/userinfo/OAuth2UserAuthenticationToken.java rename to oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/authentication/userinfo/OAuth2AuthenticationToken.java index 0cd739e7d8..d25b56aa94 100644 --- a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/authentication/userinfo/OAuth2UserAuthenticationToken.java +++ b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/authentication/userinfo/OAuth2AuthenticationToken.java @@ -27,7 +27,7 @@ import java.util.Collection; /** * An implementation of an {@link AbstractAuthenticationToken} - * that represents an OAuth 2.0 User {@link Authentication}. + * that represents an OAuth 2.0 {@link Authentication}. * *

* This {@link Authentication} associates an {@link OAuth2User} principal to an @@ -38,13 +38,13 @@ import java.util.Collection; * @see OAuth2User * @see OAuth2ClientAuthenticationToken */ -public class OAuth2UserAuthenticationToken extends AbstractAuthenticationToken { +public class OAuth2AuthenticationToken extends AbstractAuthenticationToken { private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID; private final OAuth2User principal; private final OAuth2ClientAuthenticationToken clientAuthentication; - public OAuth2UserAuthenticationToken(OAuth2User principal, Collection authorities, - OAuth2ClientAuthenticationToken clientAuthentication) { + public OAuth2AuthenticationToken(OAuth2User principal, Collection authorities, + OAuth2ClientAuthenticationToken clientAuthentication) { super(authorities); Assert.notNull(clientAuthentication, "clientAuthentication cannot be null"); this.principal = principal; diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/authentication/userinfo/OAuth2UserAuthenticationProvider.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/authentication/userinfo/OAuth2UserAuthenticationProvider.java deleted file mode 100644 index 09cdaf37c2..0000000000 --- a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/authentication/userinfo/OAuth2UserAuthenticationProvider.java +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright 2012-2017 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.security.oauth2.client.authentication.userinfo; - -import org.springframework.security.authentication.AuthenticationProvider; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.AuthenticationException; -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.oauth2.client.authentication.OAuth2ClientAuthenticationToken; -import org.springframework.security.oauth2.client.registration.ClientRegistration; -import org.springframework.security.oauth2.core.user.OAuth2User; -import org.springframework.util.Assert; - -import java.util.Collection; - -/** - * An implementation of an {@link AuthenticationProvider} that is responsible - * for obtaining the user attributes of the End-User (resource owner) - * from the UserInfo Endpoint and creating a Principal - * in the form of an {@link OAuth2User}. - * - *

- * The {@link OAuth2UserAuthenticationProvider} uses an {@link OAuth2UserService} - * for loading the {@link OAuth2User} and then associating it - * to the returned {@link OAuth2UserAuthenticationToken}. - * - * @author Joe Grandja - * @since 5.0 - * @see OAuth2UserAuthenticationToken - * @see OAuth2ClientAuthenticationToken - * @see OAuth2UserService - * @see OAuth2User - */ -public class OAuth2UserAuthenticationProvider implements AuthenticationProvider { - private final OAuth2UserService userService; - private GrantedAuthoritiesMapper authoritiesMapper = (authorities -> authorities); - - public OAuth2UserAuthenticationProvider(OAuth2UserService userService) { - Assert.notNull(userService, "userService cannot be null"); - this.userService = userService; - } - - @Override - public Authentication authenticate(Authentication authentication) throws AuthenticationException { - OAuth2ClientAuthenticationToken clientAuthentication = (OAuth2ClientAuthenticationToken)authentication; - - // Section 3.1.2.1 Authentication Request - http://openid.net/specs/openid-connect-core-1_0.html#AuthRequest - // scope - // REQUIRED. OpenID Connect requests MUST contain the "openid" scope value. - if (clientAuthentication.getAuthorizedScopes().contains("openid")) { - // This is an OpenID Connect Authentication Request so return null - // and let OidcUserAuthenticationProvider handle it instead - return null; - } - - if (this.userAuthenticated() && this.userAuthenticatedSameProviderAs(clientAuthentication)) { - // Create a new user authentication (using same principal) - // but with a different client authentication association - OAuth2UserAuthenticationToken currentUserAuthentication = - (OAuth2UserAuthenticationToken)SecurityContextHolder.getContext().getAuthentication(); - - return new OAuth2UserAuthenticationToken( - (OAuth2User)currentUserAuthentication.getPrincipal(), - currentUserAuthentication.getAuthorities(), - clientAuthentication); - } - - OAuth2User oauth2User = this.userService.loadUser(clientAuthentication); - - Collection mappedAuthorities = - this.authoritiesMapper.mapAuthorities(oauth2User.getAuthorities()); - - OAuth2UserAuthenticationToken authenticationResult = new OAuth2UserAuthenticationToken( - oauth2User, mappedAuthorities, clientAuthentication); - authenticationResult.setDetails(clientAuthentication.getDetails()); - - return authenticationResult; - } - - @Override - public boolean supports(Class authentication) { - return OAuth2ClientAuthenticationToken.class.isAssignableFrom(authentication); - } - - public final void setAuthoritiesMapper(GrantedAuthoritiesMapper authoritiesMapper) { - Assert.notNull(authoritiesMapper, "authoritiesMapper cannot be null"); - this.authoritiesMapper = authoritiesMapper; - } - - private boolean userAuthenticated() { - Authentication currentAuthentication = SecurityContextHolder.getContext().getAuthentication(); - return currentAuthentication != null && - currentAuthentication instanceof OAuth2UserAuthenticationToken && - currentAuthentication.isAuthenticated(); - } - - private boolean userAuthenticatedSameProviderAs(OAuth2ClientAuthenticationToken clientAuthentication) { - OAuth2UserAuthenticationToken currentUserAuthentication = - (OAuth2UserAuthenticationToken)SecurityContextHolder.getContext().getAuthentication(); - - String userProviderId = this.getProviderIdentifier( - currentUserAuthentication.getClientAuthentication().getClientRegistration()); - String clientProviderId = this.getProviderIdentifier( - clientAuthentication.getClientRegistration()); - - return userProviderId.equals(clientProviderId); - } - - private String getProviderIdentifier(ClientRegistration clientRegistration) { - StringBuilder builder = new StringBuilder(); - builder.append("[").append(clientRegistration.getProviderDetails().getAuthorizationUri()).append("]"); - builder.append("[").append(clientRegistration.getProviderDetails().getTokenUri()).append("]"); - builder.append("[").append(clientRegistration.getProviderDetails().getUserInfoEndpoint().getUri()).append("]"); - return builder.toString(); - } -} diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/OAuth2LoginAuthenticationFilter.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/OAuth2LoginAuthenticationFilter.java index ba16d541c7..35b296d40b 100644 --- a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/OAuth2LoginAuthenticationFilter.java +++ b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/OAuth2LoginAuthenticationFilter.java @@ -21,7 +21,6 @@ import org.springframework.security.core.AuthenticationException; import org.springframework.security.oauth2.client.authentication.AuthorizationCodeAuthenticationProvider; import org.springframework.security.oauth2.client.authentication.AuthorizationCodeAuthenticationToken; import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationException; -import org.springframework.security.oauth2.client.authentication.OAuth2ClientAuthenticationToken; import org.springframework.security.oauth2.client.registration.ClientRegistration; import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; import org.springframework.security.oauth2.core.OAuth2Error; @@ -128,10 +127,7 @@ public class OAuth2LoginAuthenticationFilter extends AbstractAuthenticationProce clientRegistration, new AuthorizationExchange(authorizationRequest, authorizationResponse)); authorizationCodeAuthentication.setDetails(this.authenticationDetailsSource.buildDetails(request)); - OAuth2ClientAuthenticationToken clientAuthentication = - (OAuth2ClientAuthenticationToken)this.getAuthenticationManager().authenticate(authorizationCodeAuthentication); - - return this.getAuthenticationManager().authenticate(clientAuthentication); + return this.getAuthenticationManager().authenticate(authorizationCodeAuthentication); } public final void setClientRegistrationRepository(ClientRegistrationRepository clientRegistrationRepository) { diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/oidc/client/authentication/OidcAuthorizationCodeAuthenticationProvider.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/oidc/client/authentication/OidcAuthorizationCodeAuthenticationProvider.java index ca76c1d66d..42cd675585 100644 --- a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/oidc/client/authentication/OidcAuthorizationCodeAuthenticationProvider.java +++ b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/oidc/client/authentication/OidcAuthorizationCodeAuthenticationProvider.java @@ -18,10 +18,14 @@ package org.springframework.security.oauth2.oidc.client.authentication; import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper; import org.springframework.security.oauth2.client.authentication.AuthorizationCodeAuthenticationToken; import org.springframework.security.oauth2.client.authentication.AuthorizationGrantTokenExchanger; import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationException; import org.springframework.security.oauth2.client.authentication.jwt.JwtDecoderRegistry; +import org.springframework.security.oauth2.client.authentication.userinfo.OAuth2AuthenticationToken; +import org.springframework.security.oauth2.client.authentication.userinfo.OAuth2UserService; import org.springframework.security.oauth2.client.registration.ClientRegistration; import org.springframework.security.oauth2.client.token.InMemoryAccessTokenRepository; import org.springframework.security.oauth2.client.token.SecurityTokenRepository; @@ -30,26 +34,37 @@ import org.springframework.security.oauth2.core.OAuth2Error; import org.springframework.security.oauth2.core.endpoint.AuthorizationRequest; import org.springframework.security.oauth2.core.endpoint.AuthorizationResponse; import org.springframework.security.oauth2.core.endpoint.TokenResponse; +import org.springframework.security.oauth2.core.user.OAuth2User; import org.springframework.security.oauth2.jwt.Jwt; import org.springframework.security.oauth2.jwt.JwtDecoder; +import org.springframework.security.oauth2.oidc.client.authentication.userinfo.OidcUserService; import org.springframework.security.oauth2.oidc.core.IdToken; import org.springframework.security.oauth2.oidc.core.OidcScope; import org.springframework.security.oauth2.oidc.core.endpoint.OidcParameter; +import org.springframework.security.oauth2.oidc.core.user.OidcUser; import org.springframework.util.Assert; +import java.util.Collection; + /** * An implementation of an {@link AuthenticationProvider} * for the OpenID Connect Core 1.0 Authorization Code Grant Flow. - * + *

* This {@link AuthenticationProvider} is responsible for authenticating * an authorization code credential with the authorization server's Token Endpoint * and if valid, exchanging it for an access token credential. + *

+ * It will also obtain the user attributes of the End-User (resource owner) + * from the UserInfo Endpoint using an {@link OAuth2UserService} + * which will create a Principal in the form of an {@link OidcUser}. * * @author Joe Grandja * @since 5.0 * @see AuthorizationCodeAuthenticationToken - * @see OidcClientAuthenticationToken * @see SecurityTokenRepository + * @see OidcClientAuthenticationToken + * @see OidcUserService + * @see OidcUser * @see Section 3.1 Authorization Code Grant Flow * @see Section 3.1.3.1 Token Request * @see Section 3.1.3.3 Token Response @@ -58,16 +73,21 @@ public class OidcAuthorizationCodeAuthenticationProvider implements Authenticati private static final String INVALID_STATE_PARAMETER_ERROR_CODE = "invalid_state_parameter"; private static final String INVALID_REDIRECT_URI_PARAMETER_ERROR_CODE = "invalid_redirect_uri_parameter"; private final AuthorizationGrantTokenExchanger authorizationCodeTokenExchanger; + private final OAuth2UserService userService; private final JwtDecoderRegistry jwtDecoderRegistry; private SecurityTokenRepository accessTokenRepository = new InMemoryAccessTokenRepository(); + private GrantedAuthoritiesMapper authoritiesMapper = (authorities -> authorities); public OidcAuthorizationCodeAuthenticationProvider( AuthorizationGrantTokenExchanger authorizationCodeTokenExchanger, + OAuth2UserService userService, JwtDecoderRegistry jwtDecoderRegistry) { Assert.notNull(authorizationCodeTokenExchanger, "authorizationCodeTokenExchanger cannot be null"); + Assert.notNull(userService, "userService cannot be null"); Assert.notNull(jwtDecoderRegistry, "jwtDecoderRegistry cannot be null"); this.authorizationCodeTokenExchanger = authorizationCodeTokenExchanger; + this.userService = userService; this.jwtDecoderRegistry = jwtDecoderRegistry; } @@ -136,7 +156,16 @@ public class OidcAuthorizationCodeAuthenticationProvider implements Authenticati clientAuthentication.getAccessToken(), clientAuthentication.getClientRegistration()); - return clientAuthentication; + OAuth2User oauth2User = this.userService.loadUser(clientAuthentication); + + Collection mappedAuthorities = + this.authoritiesMapper.mapAuthorities(oauth2User.getAuthorities()); + + OAuth2AuthenticationToken authenticationResult = new OAuth2AuthenticationToken( + oauth2User, mappedAuthorities, clientAuthentication); + authenticationResult.setDetails(clientAuthentication.getDetails()); + + return authenticationResult; } public final void setAccessTokenRepository(SecurityTokenRepository accessTokenRepository) { @@ -144,6 +173,11 @@ public class OidcAuthorizationCodeAuthenticationProvider implements Authenticati this.accessTokenRepository = accessTokenRepository; } + public final void setAuthoritiesMapper(GrantedAuthoritiesMapper authoritiesMapper) { + Assert.notNull(authoritiesMapper, "authoritiesMapper cannot be null"); + this.authoritiesMapper = authoritiesMapper; + } + @Override public boolean supports(Class authentication) { return AuthorizationCodeAuthenticationToken.class.isAssignableFrom(authentication); diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/oidc/client/authentication/OidcUserAuthenticationToken.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/oidc/client/authentication/OidcUserAuthenticationToken.java deleted file mode 100644 index 47df8bbe13..0000000000 --- a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/oidc/client/authentication/OidcUserAuthenticationToken.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2012-2017 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.security.oauth2.oidc.client.authentication; - -import org.springframework.security.core.Authentication; -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.oauth2.client.authentication.userinfo.OAuth2UserAuthenticationToken; -import org.springframework.security.oauth2.oidc.core.user.OidcUser; - -import java.util.Collection; - -/** - * An {@link OAuth2UserAuthenticationToken} that represents an - * OpenID Connect 1.0 User {@link Authentication}. - * - *

- * This {@link Authentication} associates an {@link OidcUser} principal to an - * {@link OidcClientAuthenticationToken} which represents the "Authorized Client". - * - * @author Joe Grandja - * @since 5.0 - * @see OidcUser - * @see OidcClientAuthenticationToken - * @see OAuth2UserAuthenticationToken - */ -public class OidcUserAuthenticationToken extends OAuth2UserAuthenticationToken { - - public OidcUserAuthenticationToken(OidcUser principal, Collection authorities, - OidcClientAuthenticationToken clientAuthentication) { - super(principal, authorities, clientAuthentication); - } - -} diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/oidc/client/authentication/userinfo/OidcUserAuthenticationProvider.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/oidc/client/authentication/userinfo/OidcUserAuthenticationProvider.java deleted file mode 100644 index 525e295f61..0000000000 --- a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/oidc/client/authentication/userinfo/OidcUserAuthenticationProvider.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright 2012-2017 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.security.oauth2.oidc.client.authentication.userinfo; - -import org.springframework.security.authentication.AuthenticationProvider; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.AuthenticationException; -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.oauth2.client.authentication.userinfo.OAuth2UserService; -import org.springframework.security.oauth2.core.user.OAuth2User; -import org.springframework.security.oauth2.oidc.client.authentication.OidcClientAuthenticationToken; -import org.springframework.security.oauth2.oidc.client.authentication.OidcUserAuthenticationToken; -import org.springframework.security.oauth2.oidc.core.user.OidcUser; -import org.springframework.util.Assert; - -import java.util.Collection; - -/** - * An implementation of an {@link AuthenticationProvider} that is responsible - * for obtaining the user attributes of the End-User (resource owner) - * from the UserInfo Endpoint and creating a Principal - * in the form of an {@link OidcUser}. - * - *

- * The {@link OidcUserAuthenticationProvider} uses an {@link OidcUserService} - * for loading the {@link OidcUser} and then associating it - * to the returned {@link OidcUserAuthenticationToken}. - * - * @author Joe Grandja - * @since 5.0 - * @see OidcUserAuthenticationToken - * @see OidcClientAuthenticationToken - * @see OidcUserService - * @see OidcUser - */ -public class OidcUserAuthenticationProvider implements AuthenticationProvider { - private final OAuth2UserService userService; - private GrantedAuthoritiesMapper authoritiesMapper = (authorities -> authorities); - - public OidcUserAuthenticationProvider(OAuth2UserService userService) { - Assert.notNull(userService, "userService cannot be null"); - this.userService = userService; - } - - @Override - public Authentication authenticate(Authentication authentication) throws AuthenticationException { - OidcClientAuthenticationToken clientAuthentication = (OidcClientAuthenticationToken) authentication; - - if (this.userAuthenticated()) { - // Create a new user authentication (using same principal) - // but with a different client authentication association - OidcUserAuthenticationToken currentUserAuthentication = - (OidcUserAuthenticationToken) SecurityContextHolder.getContext().getAuthentication(); - - return new OidcUserAuthenticationToken( - (OidcUser) currentUserAuthentication.getPrincipal(), - currentUserAuthentication.getAuthorities(), - clientAuthentication); - } - - OAuth2User oauth2User = this.userService.loadUser(clientAuthentication); - - Collection mappedAuthorities = - this.authoritiesMapper.mapAuthorities(oauth2User.getAuthorities()); - - OidcUserAuthenticationToken authenticationResult = new OidcUserAuthenticationToken( - (OidcUser)oauth2User, mappedAuthorities, clientAuthentication); - authenticationResult.setDetails(clientAuthentication.getDetails()); - - return authenticationResult; - } - - @Override - public boolean supports(Class authentication) { - return OidcClientAuthenticationToken.class.isAssignableFrom(authentication); - } - - public final void setAuthoritiesMapper(GrantedAuthoritiesMapper authoritiesMapper) { - Assert.notNull(authoritiesMapper, "authoritiesMapper cannot be null"); - this.authoritiesMapper = authoritiesMapper; - } - - private boolean userAuthenticated() { - Authentication currentAuthentication = SecurityContextHolder.getContext().getAuthentication(); - return currentAuthentication != null && - currentAuthentication instanceof OidcUserAuthenticationToken && - currentAuthentication.isAuthenticated(); - } -} diff --git a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/web/OAuth2LoginAuthenticationFilterTests.java b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/web/OAuth2LoginAuthenticationFilterTests.java index c21009518b..3ce9d8c54a 100644 --- a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/web/OAuth2LoginAuthenticationFilterTests.java +++ b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/web/OAuth2LoginAuthenticationFilterTests.java @@ -29,7 +29,7 @@ import org.springframework.security.core.authority.AuthorityUtils; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationException; import org.springframework.security.oauth2.client.authentication.OAuth2ClientAuthenticationToken; -import org.springframework.security.oauth2.client.authentication.userinfo.OAuth2UserAuthenticationToken; +import org.springframework.security.oauth2.client.authentication.userinfo.OAuth2AuthenticationToken; import org.springframework.security.oauth2.client.registration.ClientRegistration; import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; import org.springframework.security.oauth2.core.AccessToken; @@ -100,7 +100,7 @@ public class OAuth2LoginAuthenticationFilterTests { ClientRegistration clientRegistration = TestUtil.githubClientRegistration(); OAuth2ClientAuthenticationToken clientAuthentication = new OAuth2ClientAuthenticationToken( clientRegistration, mock(AccessToken.class)); - OAuth2UserAuthenticationToken userAuthentication = new OAuth2UserAuthenticationToken( + OAuth2AuthenticationToken userAuthentication = new OAuth2AuthenticationToken( mock(OAuth2User.class), AuthorityUtils.createAuthorityList("ROLE_USER"), clientAuthentication); SecurityContextHolder.getContext().setAuthentication(userAuthentication); AuthenticationManager authenticationManager = mock(AuthenticationManager.class); diff --git a/samples/boot/oauth2login/src/main/java/sample/web/MainController.java b/samples/boot/oauth2login/src/main/java/sample/web/MainController.java index 0fda158660..94871333ac 100644 --- a/samples/boot/oauth2login/src/main/java/sample/web/MainController.java +++ b/samples/boot/oauth2login/src/main/java/sample/web/MainController.java @@ -17,7 +17,7 @@ package sample.web; import org.springframework.http.HttpHeaders; import org.springframework.security.core.annotation.AuthenticationPrincipal; -import org.springframework.security.oauth2.client.authentication.userinfo.OAuth2UserAuthenticationToken; +import org.springframework.security.oauth2.client.authentication.userinfo.OAuth2AuthenticationToken; import org.springframework.security.oauth2.core.user.OAuth2User; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; @@ -38,14 +38,14 @@ import java.util.Map; public class MainController { @RequestMapping("/") - public String index(Model model, @AuthenticationPrincipal OAuth2User user, OAuth2UserAuthenticationToken authentication) { + public String index(Model model, @AuthenticationPrincipal OAuth2User user, OAuth2AuthenticationToken authentication) { model.addAttribute("userName", user.getName()); model.addAttribute("clientName", authentication.getClientAuthentication().getClientRegistration().getClientName()); return "index"; } @RequestMapping("/userinfo") - public String userinfo(Model model, OAuth2UserAuthenticationToken authentication) { + public String userinfo(Model model, OAuth2AuthenticationToken authentication) { Map userAttributes = Collections.emptyMap(); String userInfoEndpointUri = authentication.getClientAuthentication().getClientRegistration() .getProviderDetails().getUserInfoEndpoint().getUri(); @@ -63,7 +63,7 @@ public class MainController { return "userinfo"; } - private ExchangeFilterFunction oauth2Credentials(OAuth2UserAuthenticationToken authentication) { + private ExchangeFilterFunction oauth2Credentials(OAuth2AuthenticationToken authentication) { return ExchangeFilterFunction.ofRequestProcessor( clientRequest -> { ClientRequest authorizedRequest = ClientRequest.from(clientRequest)