Fix package tangles -> OAuth2/Oidc AuthenticationProvider's

Fixes gh-4614
This commit is contained in:
Joe Grandja 2017-10-16 20:27:49 -04:00
parent 983d019ee8
commit 7b8d131386
13 changed files with 160 additions and 94 deletions

View File

@ -36,6 +36,7 @@ import org.springframework.security.oauth2.client.web.AuthorizationRequestReposi
import org.springframework.security.oauth2.client.web.AuthorizationRequestUriBuilder;
import org.springframework.security.oauth2.core.AccessToken;
import org.springframework.security.oauth2.core.user.OAuth2User;
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.RequestMatcher;
@ -195,19 +196,6 @@ public final class OAuth2LoginConfigurer<B extends HttpSecurityBuilder<B>> exten
return this;
}
private OAuth2UserService getUserService() {
if (this.userService == null) {
List<OAuth2UserService> userServices = new ArrayList<>();
userServices.add(new DefaultOAuth2UserService());
userServices.add(new OidcUserService());
if (!this.customUserTypes.isEmpty()) {
userServices.add(new CustomUserTypesOAuth2UserService(this.customUserTypes));
}
this.userService = new DelegatingOAuth2UserService(userServices);
}
return this.userService;
}
public OAuth2LoginConfigurer<B> and() {
return OAuth2LoginConfigurer.this;
}
@ -219,13 +207,35 @@ public final class OAuth2LoginConfigurer<B extends HttpSecurityBuilder<B>> exten
this.authorizationCodeGrantConfigurer.setBuilder(http);
this.authorizationCodeGrantConfigurer.init(http);
OAuth2UserService userService = this.userInfoEndpointConfig.userService;
if (userService == null) {
if (!this.userInfoEndpointConfig.customUserTypes.isEmpty()) {
List<OAuth2UserService> 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(this.userInfoEndpointConfig.getUserService());
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);
}

View File

@ -65,10 +65,9 @@ public class AuthorizationCodeAuthenticationProvider implements AuthenticationPr
// 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 the openid scope value is not present, the behavior is entirely unspecified.
if (authorizationCodeAuthentication.getAuthorizationRequest().getScope().contains("openid")) {
// The OpenID Connect implementation of Authorization Code AuthenticationProvider
// should handle OpenID Connect Authentication Requests so don't handle and return null
// This is an OpenID Connect Authentication Request so return null
// and let OidcAuthorizationCodeAuthenticationProvider handle it instead
return null;
}

View File

@ -20,7 +20,6 @@ import org.springframework.beans.PropertyAccessorFactory;
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationException;
import org.springframework.security.oauth2.client.authentication.OAuth2ClientAuthenticationToken;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.security.oauth2.oidc.client.authentication.OidcClientAuthenticationToken;
import org.springframework.util.Assert;
import java.net.URI;
@ -70,9 +69,6 @@ public class CustomUserTypesOAuth2UserService implements OAuth2UserService {
}
Map<String, Object> userAttributes = this.userInfoRetriever.retrieve(clientAuthentication);
if (OidcClientAuthenticationToken.class.isAssignableFrom(clientAuthentication.getClass())) {
userAttributes.putAll(((OidcClientAuthenticationToken)clientAuthentication).getIdToken().getClaims());
}
BeanWrapper wrapper = PropertyAccessorFactory.forBeanPropertyAccess(customUser);
wrapper.setAutoGrowNestedPaths(true);

View File

@ -22,7 +22,6 @@ import org.springframework.security.oauth2.client.registration.ClientRegistratio
import org.springframework.security.oauth2.core.user.DefaultOAuth2User;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.security.oauth2.core.user.OAuth2UserAuthority;
import org.springframework.security.oauth2.oidc.client.authentication.OidcClientAuthenticationToken;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
@ -54,10 +53,6 @@ public class DefaultOAuth2UserService implements OAuth2UserService {
@Override
public OAuth2User loadUser(OAuth2ClientAuthenticationToken clientAuthentication) throws OAuth2AuthenticationException {
if (OidcClientAuthenticationToken.class.isAssignableFrom(clientAuthentication.getClass())) {
return null;
}
String userNameAttributeName = clientAuthentication.getClientRegistration().getProviderDetails().getUserInfoEndpoint().getUserNameAttributeName();
if (!StringUtils.hasText(userNameAttributeName)) {
throw new IllegalArgumentException(

View File

@ -20,6 +20,8 @@ import org.springframework.security.oauth2.client.authentication.OAuth2ClientAut
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.util.Assert;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
@ -41,7 +43,7 @@ public class DelegatingOAuth2UserService implements OAuth2UserService {
public DelegatingOAuth2UserService(List<OAuth2UserService> userServices) {
Assert.notEmpty(userServices, "userServices cannot be empty");
this.userServices = userServices;
this.userServices = Collections.unmodifiableList(new ArrayList<>(userServices));
}
@Override

View File

@ -25,10 +25,6 @@ import org.springframework.security.oauth2.client.authentication.OAuth2ClientAut
import org.springframework.security.oauth2.client.registration.ClientRegistration;
import org.springframework.security.oauth2.client.registration.ClientRegistrationIdentifierStrategy;
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.client.authentication.userinfo.OidcUserService;
import org.springframework.security.oauth2.oidc.core.user.OidcUser;
import org.springframework.util.Assert;
import java.util.Collection;
@ -47,13 +43,9 @@ import java.util.Collection;
* @author Joe Grandja
* @since 5.0
* @see OAuth2UserAuthenticationToken
* @see OidcUserAuthenticationToken
* @see OAuth2ClientAuthenticationToken
* @see OidcClientAuthenticationToken
* @see OAuth2UserService
* @see OidcUserService
* @see OAuth2User
* @see OidcUser
*/
public class OAuth2UserAuthenticationProvider implements AuthenticationProvider {
private final ClientRegistrationIdentifierStrategy<String> providerIdentifierStrategy = new ProviderIdentifierStrategy();
@ -67,14 +59,26 @@ public class OAuth2UserAuthenticationProvider implements AuthenticationProvider
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
OAuth2UserAuthenticationToken userAuthentication = (OAuth2UserAuthenticationToken) authentication;
OAuth2ClientAuthenticationToken clientAuthentication = userAuthentication.getClientAuthentication();
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.getAuthorizedScope().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
return this.createUserAuthentication(
(OAuth2UserAuthenticationToken)SecurityContextHolder.getContext().getAuthentication(),
OAuth2UserAuthenticationToken currentUserAuthentication =
(OAuth2UserAuthenticationToken)SecurityContextHolder.getContext().getAuthentication();
return new OAuth2UserAuthenticationToken(
(OAuth2User)currentUserAuthentication.getPrincipal(),
currentUserAuthentication.getAuthorities(),
clientAuthentication);
}
@ -83,29 +87,23 @@ public class OAuth2UserAuthenticationProvider implements AuthenticationProvider
Collection<? extends GrantedAuthority> mappedAuthorities =
this.authoritiesMapper.mapAuthorities(oauth2User.getAuthorities());
OAuth2UserAuthenticationToken authenticationResult;
if (OidcUser.class.isAssignableFrom(oauth2User.getClass())) {
authenticationResult = new OidcUserAuthenticationToken(
(OidcUser)oauth2User, mappedAuthorities, (OidcClientAuthenticationToken)clientAuthentication);
} else {
authenticationResult = new OAuth2UserAuthenticationToken(
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;
}
@Override
public boolean supports(Class<?> authentication) {
return OAuth2UserAuthenticationToken.class.isAssignableFrom(authentication);
}
private boolean userAuthenticated() {
Authentication currentAuthentication = SecurityContextHolder.getContext().getAuthentication();
return currentAuthentication != null &&
@ -125,23 +123,6 @@ public class OAuth2UserAuthenticationProvider implements AuthenticationProvider
return userProviderId.equals(clientProviderId);
}
private OAuth2UserAuthenticationToken createUserAuthentication(
OAuth2UserAuthenticationToken currentUserAuthentication,
OAuth2ClientAuthenticationToken newClientAuthentication) {
if (OidcUserAuthenticationToken.class.isAssignableFrom(currentUserAuthentication.getClass())) {
return new OidcUserAuthenticationToken(
(OidcUser) currentUserAuthentication.getPrincipal(),
currentUserAuthentication.getAuthorities(),
newClientAuthentication);
} else {
return new OAuth2UserAuthenticationToken(
(OAuth2User)currentUserAuthentication.getPrincipal(),
currentUserAuthentication.getAuthorities(),
newClientAuthentication);
}
}
private static class ProviderIdentifierStrategy implements ClientRegistrationIdentifierStrategy<String> {
@Override

View File

@ -24,7 +24,6 @@ import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.util.Assert;
import java.util.Collection;
import java.util.Collections;
/**
* An implementation of an {@link AbstractAuthenticationToken}
@ -44,10 +43,6 @@ public class OAuth2UserAuthenticationToken extends AbstractAuthenticationToken {
private final OAuth2User principal;
private final OAuth2ClientAuthenticationToken clientAuthentication;
public OAuth2UserAuthenticationToken(OAuth2ClientAuthenticationToken clientAuthentication) {
this(null, Collections.emptyList(), clientAuthentication);
}
public OAuth2UserAuthenticationToken(OAuth2User principal, Collection<? extends GrantedAuthority> authorities,
OAuth2ClientAuthenticationToken clientAuthentication) {
super(authorities);

View File

@ -22,7 +22,6 @@ import org.springframework.security.oauth2.client.authentication.AuthorizationCo
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.authentication.userinfo.OAuth2UserAuthenticationToken;
import org.springframework.security.oauth2.client.registration.ClientRegistration;
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
import org.springframework.security.oauth2.core.OAuth2Error;
@ -129,8 +128,7 @@ public class AuthorizationCodeAuthenticationFilter extends AbstractAuthenticatio
OAuth2ClientAuthenticationToken clientAuthentication =
(OAuth2ClientAuthenticationToken)this.getAuthenticationManager().authenticate(authorizationCodeAuthentication);
return this.getAuthenticationManager().authenticate(
new OAuth2UserAuthenticationToken(clientAuthentication));
return this.getAuthenticationManager().authenticate(clientAuthentication);
}
public final RequestMatcher getAuthorizationResponseMatcher() {

View File

@ -79,9 +79,9 @@ public class OidcAuthorizationCodeAuthenticationProvider implements Authenticati
// 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 the openid scope value is not present, the behavior is entirely unspecified.
if (!authorizationCodeAuthentication.getAuthorizationRequest().getScope().contains(OidcScope.OPENID)) {
// Let the standard OAuth 2.0 Authorization Code AuthenticationProvider handle this
// This is NOT an OpenID Connect Authentication Request so return null
// and let AuthorizationCodeAuthenticationProvider handle it instead
return null;
}

View File

@ -17,12 +17,10 @@ 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.OAuth2ClientAuthenticationToken;
import org.springframework.security.oauth2.client.authentication.userinfo.OAuth2UserAuthenticationToken;
import org.springframework.security.oauth2.oidc.core.user.OidcUser;
import java.util.Collection;
import java.util.Collections;
/**
* An {@link OAuth2UserAuthenticationToken} that represents an
@ -40,17 +38,9 @@ import java.util.Collections;
*/
public class OidcUserAuthenticationToken extends OAuth2UserAuthenticationToken {
public OidcUserAuthenticationToken(OidcClientAuthenticationToken clientAuthentication) {
this(null, Collections.emptyList(), clientAuthentication);
}
public OidcUserAuthenticationToken(OidcUser principal, Collection<? extends GrantedAuthority> authorities,
OidcClientAuthenticationToken clientAuthentication) {
this(principal, authorities, (OAuth2ClientAuthenticationToken)clientAuthentication);
}
public OidcUserAuthenticationToken(OidcUser principal, Collection<? extends GrantedAuthority> authorities,
OAuth2ClientAuthenticationToken clientAuthentication) {
super(principal, authorities, clientAuthentication);
}
}

View File

@ -0,0 +1,104 @@
/*
* 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 <i>End-User</i> (resource owner)
* from the <i>UserInfo Endpoint</i> and creating a <code>Principal</code>
* in the form of an {@link OidcUser}.
*
* <p>
* 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<? extends GrantedAuthority> 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();
}
}

View File

@ -18,9 +18,9 @@ package org.springframework.security.oauth2.oidc.client.authentication.userinfo;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationException;
import org.springframework.security.oauth2.client.authentication.OAuth2ClientAuthenticationToken;
import org.springframework.security.oauth2.client.authentication.userinfo.NimbusUserInfoRetriever;
import org.springframework.security.oauth2.client.authentication.userinfo.OAuth2UserService;
import org.springframework.security.oauth2.client.authentication.userinfo.UserInfoRetriever;
import org.springframework.security.oauth2.client.authentication.userinfo.NimbusUserInfoRetriever;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.security.oauth2.oidc.client.authentication.OidcClientAuthenticationToken;
@ -58,9 +58,6 @@ public class OidcUserService implements OAuth2UserService {
@Override
public OAuth2User loadUser(OAuth2ClientAuthenticationToken clientAuthentication) throws OAuth2AuthenticationException {
if (!OidcClientAuthenticationToken.class.isAssignableFrom(clientAuthentication.getClass())) {
return null;
}
OidcClientAuthenticationToken oidcClientAuthentication = (OidcClientAuthenticationToken)clientAuthentication;
UserInfo userInfo = null;

View File

@ -359,7 +359,6 @@ public class OAuth2LoginApplicationTests {
TokenResponse tokenResponse = TokenResponse.withToken("access-token-1234")
.tokenType(AccessToken.TokenType.BEARER)
.expiresIn(60 * 1000)
.scope(Collections.singleton("openid"))
.build();
AuthorizationGrantTokenExchanger mock = mock(AuthorizationGrantTokenExchanger.class);