Refactor OAuth2 AuthenticationProvider's

Fixes gh-4689
This commit is contained in:
Joe Grandja 2017-10-24 15:11:35 -04:00
parent 0fb32a052e
commit 049080290e
10 changed files with 112 additions and 337 deletions

View File

@ -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<B extends HttpSecurityBuilder<B>> exten
authorizationCodeTokenExchanger = new NimbusAuthorizationCodeTokenExchanger();
}
OAuth2UserService oauth2UserService = this.userInfoEndpointConfig.userService;
if (oauth2UserService == null) {
if (!this.userInfoEndpointConfig.customUserTypes.isEmpty()) {
List<OAuth2UserService> 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<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(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

@ -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 <i>OAuth 2.0 Authorization Code Grant Flow</i>.
@ -34,12 +41,19 @@ import org.springframework.util.Assert;
* This {@link AuthenticationProvider} is responsible for authenticating
* an <i>authorization code</i> credential with the authorization server's <i>Token Endpoint</i>
* and if valid, exchanging it for an <i>access token</i> credential.
* <p>
* It will also obtain the user attributes of the <i>End-User</i> (resource owner)
* from the <i>UserInfo Endpoint</i> using an {@link OAuth2UserService}
* which will create a <code>Principal</code> 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 <a target="_blank" href="https://tools.ietf.org/html/rfc6749#section-4.1">Section 4.1 Authorization Code Grant Flow</a>
* @see <a target="_blank" href="https://tools.ietf.org/html/rfc6749#section-4.1.3">Section 4.1.3 Access Token Request</a>
* @see <a target="_blank" href="https://tools.ietf.org/html/rfc6749#section-4.1.4">Section 4.1.4 Access Token Response</a>
@ -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<AuthorizationCodeAuthenticationToken> authorizationCodeTokenExchanger;
private final OAuth2UserService userService;
private SecurityTokenRepository<AccessToken> accessTokenRepository = new InMemoryAccessTokenRepository();
private GrantedAuthoritiesMapper authoritiesMapper = (authorities -> authorities);
public AuthorizationCodeAuthenticationProvider(
AuthorizationGrantTokenExchanger<AuthorizationCodeAuthenticationToken> authorizationCodeTokenExchanger) {
AuthorizationGrantTokenExchanger<AuthorizationCodeAuthenticationToken> 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<? extends GrantedAuthority> mappedAuthorities =
this.authoritiesMapper.mapAuthorities(oauth2User.getAuthorities());
OAuth2AuthenticationToken authenticationResult = new OAuth2AuthenticationToken(
oauth2User, mappedAuthorities, clientAuthentication);
authenticationResult.setDetails(clientAuthentication.getDetails());
return authenticationResult;
}
public final void setAccessTokenRepository(SecurityTokenRepository<AccessToken> 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);

View File

@ -27,7 +27,7 @@ import java.util.Collection;
/**
* An implementation of an {@link AbstractAuthenticationToken}
* that represents an <i>OAuth 2.0 User</i> {@link Authentication}.
* that represents an <i>OAuth 2.0</i> {@link Authentication}.
*
* <p>
* This {@link Authentication} associates an {@link OAuth2User} principal to an
@ -38,12 +38,12 @@ 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<? extends GrantedAuthority> authorities,
public OAuth2AuthenticationToken(OAuth2User principal, Collection<? extends GrantedAuthority> authorities,
OAuth2ClientAuthenticationToken clientAuthentication) {
super(authorities);
Assert.notNull(clientAuthentication, "clientAuthentication cannot be null");

View File

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

View File

@ -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) {

View File

@ -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 <i>OpenID Connect Core 1.0 Authorization Code Grant Flow</i>.
*
* <p>
* This {@link AuthenticationProvider} is responsible for authenticating
* an <i>authorization code</i> credential with the authorization server's <i>Token Endpoint</i>
* and if valid, exchanging it for an <i>access token</i> credential.
* <p>
* It will also obtain the user attributes of the <i>End-User</i> (resource owner)
* from the <i>UserInfo Endpoint</i> using an {@link OAuth2UserService}
* which will create a <code>Principal</code> 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 <a target="_blank" href="http://openid.net/specs/openid-connect-core-1_0.html#CodeFlowAuth">Section 3.1 Authorization Code Grant Flow</a>
* @see <a target="_blank" href="http://openid.net/specs/openid-connect-core-1_0.html#TokenRequest">Section 3.1.3.1 Token Request</a>
* @see <a target="_blank" href="http://openid.net/specs/openid-connect-core-1_0.html#TokenResponse">Section 3.1.3.3 Token Response</a>
@ -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<AuthorizationCodeAuthenticationToken> authorizationCodeTokenExchanger;
private final OAuth2UserService userService;
private final JwtDecoderRegistry jwtDecoderRegistry;
private SecurityTokenRepository<AccessToken> accessTokenRepository = new InMemoryAccessTokenRepository();
private GrantedAuthoritiesMapper authoritiesMapper = (authorities -> authorities);
public OidcAuthorizationCodeAuthenticationProvider(
AuthorizationGrantTokenExchanger<AuthorizationCodeAuthenticationToken> 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<? extends GrantedAuthority> mappedAuthorities =
this.authoritiesMapper.mapAuthorities(oauth2User.getAuthorities());
OAuth2AuthenticationToken authenticationResult = new OAuth2AuthenticationToken(
oauth2User, mappedAuthorities, clientAuthentication);
authenticationResult.setDetails(clientAuthentication.getDetails());
return authenticationResult;
}
public final void setAccessTokenRepository(SecurityTokenRepository<AccessToken> 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);

View File

@ -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
* <i>OpenID Connect 1.0 User</i> {@link Authentication}.
*
* <p>
* This {@link Authentication} associates an {@link OidcUser} principal to an
* {@link OidcClientAuthenticationToken} which represents the <i>&quot;Authorized Client&quot;</i>.
*
* @author Joe Grandja
* @since 5.0
* @see OidcUser
* @see OidcClientAuthenticationToken
* @see OAuth2UserAuthenticationToken
*/
public class OidcUserAuthenticationToken extends OAuth2UserAuthenticationToken {
public OidcUserAuthenticationToken(OidcUser principal, Collection<? extends GrantedAuthority> authorities,
OidcClientAuthenticationToken clientAuthentication) {
super(principal, authorities, clientAuthentication);
}
}

View File

@ -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 <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

@ -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);

View File

@ -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)