Re-factor OAuth2AuthorizationCodeAuthenticationToken
Fixes gh-4730
This commit is contained in:
parent
64d8c8b8a9
commit
0c68eb1821
|
@ -1014,7 +1014,7 @@ public final class HttpSecurity extends
|
|||
* }
|
||||
*
|
||||
* @Bean
|
||||
* public AuthorizationGrantTokenExchanger<OAuth2AuthorizationCodeAuthenticationToken> authorizationCodeTokenExchanger() {
|
||||
* public AuthorizationGrantTokenExchanger<OAuth2LoginAuthenticationToken> authorizationCodeTokenExchanger() {
|
||||
* // Custom implementation that exchanges an "Authorization Code Grant" for an "Access Token"
|
||||
* return new AuthorizationCodeTokenExchangerImpl();
|
||||
* }
|
||||
|
|
|
@ -1,47 +0,0 @@
|
|||
/*
|
||||
* Copyright 2002-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;
|
||||
|
||||
import org.springframework.security.authentication.AbstractAuthenticationToken;
|
||||
import org.springframework.security.core.SpringSecurityCoreVersion;
|
||||
import org.springframework.security.oauth2.core.AuthorizationGrantType;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
/**
|
||||
* Base implementation of an {@link AbstractAuthenticationToken} that holds
|
||||
* an <i>authorization grant</i> credential for a specific {@link AuthorizationGrantType}.
|
||||
*
|
||||
* @author Joe Grandja
|
||||
* @since 5.0
|
||||
* @see AuthorizationGrantType
|
||||
* @see <a target="_blank" href="https://tools.ietf.org/html/rfc6749#section-1.3">Section 1.3 Authorization Grant</a>
|
||||
*/
|
||||
public abstract class AbstractOAuth2AuthorizationGrantAuthenticationToken extends AbstractAuthenticationToken {
|
||||
private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;
|
||||
private final AuthorizationGrantType authorizationGrantType;
|
||||
|
||||
protected AbstractOAuth2AuthorizationGrantAuthenticationToken(AuthorizationGrantType authorizationGrantType) {
|
||||
super(Collections.emptyList());
|
||||
Assert.notNull(authorizationGrantType, "authorizationGrantType cannot be null");
|
||||
this.authorizationGrantType = authorizationGrantType;
|
||||
}
|
||||
|
||||
public AuthorizationGrantType getGrantType() {
|
||||
return this.authorizationGrantType;
|
||||
}
|
||||
}
|
|
@ -19,7 +19,6 @@ import org.springframework.security.authentication.AbstractAuthenticationToken;
|
|||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.SpringSecurityCoreVersion;
|
||||
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
|
||||
import org.springframework.security.oauth2.core.user.OAuth2User;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
|
@ -30,34 +29,33 @@ import java.util.Collection;
|
|||
* that represents an <i>OAuth 2.0</i> {@link Authentication}.
|
||||
* <p>
|
||||
* This {@link Authentication} associates an {@link OAuth2User} <code>Principal</code>
|
||||
* to an {@link OAuth2AuthorizedClient}, which the End-User (Principal) granted authorization to
|
||||
* to the identifier of the {@link #getAuthorizedClientRegistrationId() Authorized Client},
|
||||
* which the End-User (Principal) granted authorization to
|
||||
* so that it can access its protected resource(s) at the <i>UserInfo Endpoint</i>.
|
||||
*
|
||||
* @author Joe Grandja
|
||||
* @since 5.0
|
||||
* @see AbstractAuthenticationToken
|
||||
* @see OAuth2AuthorizedClient
|
||||
* @see OAuth2User
|
||||
*
|
||||
* @param <U> The type of <i>OAuth 2.0 User</i>
|
||||
* @param <C> The type of <i>Authorized Client</i>
|
||||
*/
|
||||
public class OAuth2AuthenticationToken<U extends OAuth2User, C extends OAuth2AuthorizedClient> extends AbstractAuthenticationToken {
|
||||
public class OAuth2AuthenticationToken extends AbstractAuthenticationToken {
|
||||
private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;
|
||||
private final U principal;
|
||||
private final C authorizedClient;
|
||||
private final OAuth2User principal;
|
||||
private final String authorizedClientRegistrationId;
|
||||
|
||||
public OAuth2AuthenticationToken(U principal, Collection<? extends GrantedAuthority> authorities, C authorizedClient) {
|
||||
public OAuth2AuthenticationToken(OAuth2User principal,
|
||||
Collection<? extends GrantedAuthority> authorities,
|
||||
String authorizedClientRegistrationId) {
|
||||
super(authorities);
|
||||
Assert.notNull(principal, "principal cannot be null");
|
||||
Assert.notNull(authorizedClient, "authorizedClient cannot be null");
|
||||
Assert.hasText(authorizedClientRegistrationId, "authorizedClientRegistrationId cannot be empty");
|
||||
this.principal = principal;
|
||||
this.authorizedClient = authorizedClient;
|
||||
this.authorizedClientRegistrationId = authorizedClientRegistrationId;
|
||||
this.setAuthenticated(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public U getPrincipal() {
|
||||
public OAuth2User getPrincipal() {
|
||||
return this.principal;
|
||||
}
|
||||
|
||||
|
@ -67,7 +65,7 @@ public class OAuth2AuthenticationToken<U extends OAuth2User, C extends OAuth2Aut
|
|||
return "";
|
||||
}
|
||||
|
||||
public C getAuthorizedClient() {
|
||||
return this.authorizedClient;
|
||||
public String getAuthorizedClientRegistrationId() {
|
||||
return this.authorizedClientRegistrationId;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,69 +0,0 @@
|
|||
/*
|
||||
* Copyright 2002-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;
|
||||
|
||||
import org.springframework.security.oauth2.client.registration.ClientRegistration;
|
||||
import org.springframework.security.oauth2.core.AuthorizationGrantType;
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationExchange;
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationResponse;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* An implementation of an {@link AbstractOAuth2AuthorizationGrantAuthenticationToken} that holds
|
||||
* an <i>authorization code grant</i> credential for a specific client identified in {@link #getClientRegistration()}.
|
||||
*
|
||||
* @author Joe Grandja
|
||||
* @since 5.0
|
||||
* @see AbstractOAuth2AuthorizationGrantAuthenticationToken
|
||||
* @see ClientRegistration
|
||||
* @see OAuth2AuthorizationRequest
|
||||
* @see OAuth2AuthorizationResponse
|
||||
* @see <a target="_blank" href="https://tools.ietf.org/html/rfc6749#section-1.3.1">Section 1.3.1 Authorization Code Grant</a>
|
||||
*/
|
||||
public class OAuth2AuthorizationCodeAuthenticationToken extends AbstractOAuth2AuthorizationGrantAuthenticationToken {
|
||||
private final ClientRegistration clientRegistration;
|
||||
private final OAuth2AuthorizationExchange authorizationExchange;
|
||||
|
||||
public OAuth2AuthorizationCodeAuthenticationToken(ClientRegistration clientRegistration,
|
||||
OAuth2AuthorizationExchange authorizationExchange) {
|
||||
|
||||
super(AuthorizationGrantType.AUTHORIZATION_CODE);
|
||||
Assert.notNull(clientRegistration, "clientRegistration cannot be null");
|
||||
Assert.notNull(authorizationExchange, "authorizationExchange cannot be null");
|
||||
this.clientRegistration = clientRegistration;
|
||||
this.authorizationExchange = authorizationExchange;
|
||||
this.setAuthenticated(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getPrincipal() {
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getCredentials() {
|
||||
return "";
|
||||
}
|
||||
|
||||
public ClientRegistration getClientRegistration() {
|
||||
return this.clientRegistration;
|
||||
}
|
||||
|
||||
public OAuth2AuthorizationExchange getAuthorizationExchange() {
|
||||
return this.authorizationExchange;
|
||||
}
|
||||
}
|
|
@ -20,7 +20,6 @@ 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.OAuth2AuthorizedClient;
|
||||
import org.springframework.security.oauth2.client.endpoint.AuthorizationGrantTokenExchanger;
|
||||
import org.springframework.security.oauth2.client.endpoint.OAuth2AuthorizationCodeGrantRequest;
|
||||
import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest;
|
||||
|
@ -37,12 +36,12 @@ 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>.
|
||||
* An implementation of an {@link AuthenticationProvider} for <i>OAuth 2.0 Login</i>,
|
||||
* which leverages the <i>OAuth 2.0 Authorization Code Grant</i> Flow.
|
||||
*
|
||||
* 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.
|
||||
* 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}
|
||||
|
@ -50,10 +49,9 @@ import java.util.Collection;
|
|||
*
|
||||
* @author Joe Grandja
|
||||
* @since 5.0
|
||||
* @see OAuth2AuthorizationCodeAuthenticationToken
|
||||
* @see OAuth2AuthenticationToken
|
||||
* @see OAuth2LoginAuthenticationToken
|
||||
* @see AuthorizationGrantTokenExchanger
|
||||
* @see OAuth2UserService
|
||||
* @see OAuth2AuthorizedClient
|
||||
* @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>
|
||||
|
@ -78,8 +76,8 @@ public class OAuth2LoginAuthenticationProvider implements AuthenticationProvider
|
|||
|
||||
@Override
|
||||
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
|
||||
OAuth2AuthorizationCodeAuthenticationToken authorizationCodeAuthentication =
|
||||
(OAuth2AuthorizationCodeAuthenticationToken) authentication;
|
||||
OAuth2LoginAuthenticationToken authorizationCodeAuthentication =
|
||||
(OAuth2LoginAuthenticationToken) authentication;
|
||||
|
||||
// Section 3.1.2.1 Authentication Request - http://openid.net/specs/openid-connect-core-1_0.html#AuthRequest
|
||||
// scope
|
||||
|
@ -124,14 +122,15 @@ public class OAuth2LoginAuthenticationProvider implements AuthenticationProvider
|
|||
OAuth2User oauth2User = this.userService.loadUser(
|
||||
new OAuth2UserRequest(authorizationCodeAuthentication.getClientRegistration(), accessToken));
|
||||
|
||||
OAuth2AuthorizedClient oauth2AuthorizedClient = new OAuth2AuthorizedClient(
|
||||
authorizationCodeAuthentication.getClientRegistration(), oauth2User.getName(), accessToken);
|
||||
|
||||
Collection<? extends GrantedAuthority> mappedAuthorities =
|
||||
this.authoritiesMapper.mapAuthorities(oauth2User.getAuthorities());
|
||||
|
||||
OAuth2AuthenticationToken<OAuth2User, OAuth2AuthorizedClient> authenticationResult =
|
||||
new OAuth2AuthenticationToken<>(oauth2User, mappedAuthorities, oauth2AuthorizedClient);
|
||||
OAuth2LoginAuthenticationToken authenticationResult = new OAuth2LoginAuthenticationToken(
|
||||
oauth2User,
|
||||
mappedAuthorities,
|
||||
authorizationCodeAuthentication.getClientRegistration(),
|
||||
authorizationCodeAuthentication.getAuthorizationExchange(),
|
||||
accessToken);
|
||||
authenticationResult.setDetails(authorizationCodeAuthentication.getDetails());
|
||||
|
||||
return authenticationResult;
|
||||
|
@ -144,6 +143,6 @@ public class OAuth2LoginAuthenticationProvider implements AuthenticationProvider
|
|||
|
||||
@Override
|
||||
public boolean supports(Class<?> authentication) {
|
||||
return OAuth2AuthorizationCodeAuthenticationToken.class.isAssignableFrom(authentication);
|
||||
return OAuth2LoginAuthenticationToken.class.isAssignableFrom(authentication);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,116 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
import org.springframework.security.authentication.AbstractAuthenticationToken;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.SpringSecurityCoreVersion;
|
||||
import org.springframework.security.oauth2.client.registration.ClientRegistration;
|
||||
import org.springframework.security.oauth2.core.OAuth2AccessToken;
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationExchange;
|
||||
import org.springframework.security.oauth2.core.user.OAuth2User;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
/**
|
||||
* An {@link AbstractAuthenticationToken} for <i>OAuth 2.0 Login</i>,
|
||||
* which leverages the <i>OAuth 2.0 Authorization Code Grant</i> Flow.
|
||||
*
|
||||
* @author Joe Grandja
|
||||
* @since 5.0
|
||||
* @see AbstractAuthenticationToken
|
||||
* @see OAuth2User
|
||||
* @see ClientRegistration
|
||||
* @see OAuth2AuthorizationExchange
|
||||
* @see OAuth2AccessToken
|
||||
* @see <a target="_blank" href="https://tools.ietf.org/html/rfc6749#section-4.1">Section 4.1 Authorization Code Grant Flow</a>
|
||||
*/
|
||||
public class OAuth2LoginAuthenticationToken extends AbstractAuthenticationToken {
|
||||
private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;
|
||||
private OAuth2User principal;
|
||||
private ClientRegistration clientRegistration;
|
||||
private OAuth2AuthorizationExchange authorizationExchange;
|
||||
private OAuth2AccessToken accessToken;
|
||||
|
||||
/**
|
||||
* This constructor should be used when the Authorization Request/Response is complete.
|
||||
*
|
||||
* @param clientRegistration
|
||||
* @param authorizationExchange
|
||||
*/
|
||||
public OAuth2LoginAuthenticationToken(ClientRegistration clientRegistration,
|
||||
OAuth2AuthorizationExchange authorizationExchange) {
|
||||
|
||||
super(Collections.emptyList());
|
||||
Assert.notNull(clientRegistration, "clientRegistration cannot be null");
|
||||
Assert.notNull(authorizationExchange, "authorizationExchange cannot be null");
|
||||
this.clientRegistration = clientRegistration;
|
||||
this.authorizationExchange = authorizationExchange;
|
||||
this.setAuthenticated(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* This constructor should be used when the Access Token Request/Response is complete,
|
||||
* which indicates that the Authorization Code Grant flow has fully completed
|
||||
* and OAuth 2.0 Login has been achieved.
|
||||
*
|
||||
* @param principal
|
||||
* @param authorities
|
||||
* @param clientRegistration
|
||||
* @param authorizationExchange
|
||||
* @param accessToken
|
||||
*/
|
||||
public OAuth2LoginAuthenticationToken(OAuth2User principal,
|
||||
Collection<? extends GrantedAuthority> authorities,
|
||||
ClientRegistration clientRegistration,
|
||||
OAuth2AuthorizationExchange authorizationExchange,
|
||||
OAuth2AccessToken accessToken) {
|
||||
super(authorities);
|
||||
Assert.notNull(principal, "principal cannot be null");
|
||||
Assert.notNull(clientRegistration, "clientRegistration cannot be null");
|
||||
Assert.notNull(authorizationExchange, "authorizationExchange cannot be null");
|
||||
Assert.notNull(accessToken, "accessToken cannot be null");
|
||||
this.principal = principal;
|
||||
this.clientRegistration = clientRegistration;
|
||||
this.authorizationExchange = authorizationExchange;
|
||||
this.accessToken = accessToken;
|
||||
this.setAuthenticated(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public OAuth2User getPrincipal() {
|
||||
return this.principal;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getCredentials() {
|
||||
return "";
|
||||
}
|
||||
|
||||
public ClientRegistration getClientRegistration() {
|
||||
return this.clientRegistration;
|
||||
}
|
||||
|
||||
public OAuth2AuthorizationExchange getAuthorizationExchange() {
|
||||
return this.authorizationExchange;
|
||||
}
|
||||
|
||||
public OAuth2AccessToken getAccessToken() {
|
||||
return this.accessToken;
|
||||
}
|
||||
}
|
|
@ -20,12 +20,10 @@ 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.OAuth2LoginAuthenticationToken;
|
||||
import org.springframework.security.oauth2.client.endpoint.AuthorizationGrantTokenExchanger;
|
||||
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
|
||||
import org.springframework.security.oauth2.client.authentication.OAuth2AuthorizationCodeAuthenticationToken;
|
||||
import org.springframework.security.oauth2.client.endpoint.OAuth2AuthorizationCodeGrantRequest;
|
||||
import org.springframework.security.oauth2.client.jwt.JwtDecoderRegistry;
|
||||
import org.springframework.security.oauth2.client.oidc.OidcAuthorizedClient;
|
||||
import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserRequest;
|
||||
import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserService;
|
||||
import org.springframework.security.oauth2.client.registration.ClientRegistration;
|
||||
|
@ -55,19 +53,18 @@ import java.util.List;
|
|||
* 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.
|
||||
* 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)
|
||||
* 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 OAuth2AuthorizationCodeAuthenticationToken
|
||||
* @see OAuth2AuthenticationToken
|
||||
* @see OidcAuthorizationCodeAuthenticationToken
|
||||
* @see AuthorizationGrantTokenExchanger
|
||||
* @see OidcUserService
|
||||
* @see OidcAuthorizedClient
|
||||
* @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>
|
||||
|
@ -97,8 +94,8 @@ public class OidcAuthorizationCodeAuthenticationProvider implements Authenticati
|
|||
|
||||
@Override
|
||||
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
|
||||
OAuth2AuthorizationCodeAuthenticationToken authorizationCodeAuthentication =
|
||||
(OAuth2AuthorizationCodeAuthenticationToken) authentication;
|
||||
OAuth2LoginAuthenticationToken authorizationCodeAuthentication =
|
||||
(OAuth2LoginAuthenticationToken) authentication;
|
||||
|
||||
// Section 3.1.2.1 Authentication Request - http://openid.net/specs/openid-connect-core-1_0.html#AuthRequest
|
||||
// scope
|
||||
|
@ -160,14 +157,16 @@ public class OidcAuthorizationCodeAuthenticationProvider implements Authenticati
|
|||
OidcUser oidcUser = this.userService.loadUser(
|
||||
new OidcUserRequest(clientRegistration, accessToken, idToken));
|
||||
|
||||
OidcAuthorizedClient oidcAuthorizedClient = new OidcAuthorizedClient(
|
||||
clientRegistration, oidcUser.getName(), accessToken, idToken);
|
||||
|
||||
Collection<? extends GrantedAuthority> mappedAuthorities =
|
||||
this.authoritiesMapper.mapAuthorities(oidcUser.getAuthorities());
|
||||
|
||||
OAuth2AuthenticationToken<OidcUser, OidcAuthorizedClient> authenticationResult =
|
||||
new OAuth2AuthenticationToken<>(oidcUser, mappedAuthorities, oidcAuthorizedClient);
|
||||
OidcAuthorizationCodeAuthenticationToken authenticationResult = new OidcAuthorizationCodeAuthenticationToken(
|
||||
oidcUser,
|
||||
mappedAuthorities,
|
||||
authorizationCodeAuthentication.getClientRegistration(),
|
||||
authorizationCodeAuthentication.getAuthorizationExchange(),
|
||||
accessToken,
|
||||
idToken);
|
||||
authenticationResult.setDetails(authorizationCodeAuthentication.getDetails());
|
||||
|
||||
return authenticationResult;
|
||||
|
@ -180,7 +179,7 @@ public class OidcAuthorizationCodeAuthenticationProvider implements Authenticati
|
|||
|
||||
@Override
|
||||
public boolean supports(Class<?> authentication) {
|
||||
return OAuth2AuthorizationCodeAuthenticationToken.class.isAssignableFrom(authentication);
|
||||
return OAuth2LoginAuthenticationToken.class.isAssignableFrom(authentication);
|
||||
}
|
||||
|
||||
private void validateIdToken(OidcIdToken idToken, ClientRegistration clientRegistration) {
|
||||
|
|
|
@ -0,0 +1,81 @@
|
|||
/*
|
||||
* 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.oidc.authentication;
|
||||
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.oauth2.client.authentication.OAuth2LoginAuthenticationToken;
|
||||
import org.springframework.security.oauth2.client.registration.ClientRegistration;
|
||||
import org.springframework.security.oauth2.core.OAuth2AccessToken;
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationExchange;
|
||||
import org.springframework.security.oauth2.core.oidc.OidcIdToken;
|
||||
import org.springframework.security.oauth2.core.oidc.user.OidcUser;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* An {@link OAuth2LoginAuthenticationToken} for <i>OpenID Connect 1.0 Authentication</i>,
|
||||
* which leverages the <i>Authorization Code Flow</i>.
|
||||
*
|
||||
* @author Joe Grandja
|
||||
* @since 5.0
|
||||
* @see OAuth2LoginAuthenticationToken
|
||||
* @see OidcIdToken
|
||||
* @see <a target="_blank" href="http://openid.net/specs/openid-connect-core-1_0.html#CodeFlowAuth">3.1 Authorization Code Flow</a>
|
||||
*/
|
||||
public class OidcAuthorizationCodeAuthenticationToken extends OAuth2LoginAuthenticationToken {
|
||||
private OidcIdToken idToken;
|
||||
|
||||
/**
|
||||
* This constructor should be used when the Authentication Request/Response is complete.
|
||||
*
|
||||
* @param clientRegistration
|
||||
* @param authorizationExchange
|
||||
*/
|
||||
public OidcAuthorizationCodeAuthenticationToken(ClientRegistration clientRegistration,
|
||||
OAuth2AuthorizationExchange authorizationExchange) {
|
||||
|
||||
super(clientRegistration, authorizationExchange);
|
||||
}
|
||||
|
||||
/**
|
||||
* This constructor should be used when the Token Request/Response is complete,
|
||||
* which indicates that the Authorization Code Flow has fully completed
|
||||
* and OpenID Connect 1.0 Authentication has been achieved.
|
||||
*
|
||||
* @param principal
|
||||
* @param authorities
|
||||
* @param clientRegistration
|
||||
* @param authorizationExchange
|
||||
* @param accessToken
|
||||
* @param idToken
|
||||
*/
|
||||
public OidcAuthorizationCodeAuthenticationToken(OidcUser principal,
|
||||
Collection<? extends GrantedAuthority> authorities,
|
||||
ClientRegistration clientRegistration,
|
||||
OAuth2AuthorizationExchange authorizationExchange,
|
||||
OAuth2AccessToken accessToken,
|
||||
OidcIdToken idToken) {
|
||||
|
||||
super(principal, authorities, clientRegistration, authorizationExchange, accessToken);
|
||||
Assert.notNull(idToken, "idToken cannot be null");
|
||||
this.idToken = idToken;
|
||||
}
|
||||
|
||||
public OidcIdToken getIdToken() {
|
||||
return this.idToken;
|
||||
}
|
||||
}
|
|
@ -21,8 +21,8 @@ import org.springframework.security.core.AuthenticationException;
|
|||
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
|
||||
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService;
|
||||
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
|
||||
import org.springframework.security.oauth2.client.authentication.OAuth2AuthorizationCodeAuthenticationToken;
|
||||
import org.springframework.security.oauth2.client.authentication.OAuth2LoginAuthenticationProvider;
|
||||
import org.springframework.security.oauth2.client.authentication.OAuth2LoginAuthenticationToken;
|
||||
import org.springframework.security.oauth2.client.registration.ClientRegistration;
|
||||
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
|
||||
import org.springframework.security.oauth2.client.token.InMemoryAccessTokenRepository;
|
||||
|
@ -35,7 +35,6 @@ import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationExch
|
|||
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationResponse;
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
|
||||
import org.springframework.security.oauth2.core.user.OAuth2User;
|
||||
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
@ -60,7 +59,7 @@ import java.io.IOException;
|
|||
* and redirect the end-user's user-agent back to this <code>Filter</code> (the client).
|
||||
* </li>
|
||||
* <li>
|
||||
* This <code>Filter</code> will then create an {@link OAuth2AuthorizationCodeAuthenticationToken} with
|
||||
* This <code>Filter</code> will then create an {@link OAuth2LoginAuthenticationToken} with
|
||||
* the {@link OAuth2ParameterNames#CODE} received in the previous step and delegate it to
|
||||
* {@link OAuth2LoginAuthenticationProvider#authenticate(Authentication)} (indirectly via {@link AuthenticationManager}).
|
||||
* </li>
|
||||
|
@ -69,7 +68,7 @@ import java.io.IOException;
|
|||
* @author Joe Grandja
|
||||
* @since 5.0
|
||||
* @see AbstractAuthenticationProcessingFilter
|
||||
* @see OAuth2AuthorizationCodeAuthenticationToken
|
||||
* @see OAuth2LoginAuthenticationToken
|
||||
* @see OAuth2AuthenticationToken
|
||||
* @see OAuth2LoginAuthenticationProvider
|
||||
* @see OAuth2AuthorizationRequest
|
||||
|
@ -136,19 +135,29 @@ public class OAuth2LoginAuthenticationFilter extends AbstractAuthenticationProce
|
|||
.redirectUri(authorizationRequest.getRedirectUri())
|
||||
.build();
|
||||
|
||||
OAuth2AuthorizationCodeAuthenticationToken authorizationCodeAuthentication = new OAuth2AuthorizationCodeAuthenticationToken(
|
||||
OAuth2LoginAuthenticationToken authenticationRequest = new OAuth2LoginAuthenticationToken(
|
||||
clientRegistration, new OAuth2AuthorizationExchange(authorizationRequest, authorizationResponse));
|
||||
authorizationCodeAuthentication.setDetails(this.authenticationDetailsSource.buildDetails(request));
|
||||
authenticationRequest.setDetails(this.authenticationDetailsSource.buildDetails(request));
|
||||
|
||||
OAuth2AuthenticationToken<OAuth2User, OAuth2AuthorizedClient> oauth2Authentication =
|
||||
(OAuth2AuthenticationToken<OAuth2User, OAuth2AuthorizedClient>) this.getAuthenticationManager().authenticate(authorizationCodeAuthentication);
|
||||
OAuth2LoginAuthenticationToken authenticationResult =
|
||||
(OAuth2LoginAuthenticationToken)this.getAuthenticationManager().authenticate(authenticationRequest);
|
||||
|
||||
OAuth2AuthenticationToken oauth2Authentication = new OAuth2AuthenticationToken(
|
||||
authenticationResult.getPrincipal(),
|
||||
authenticationResult.getAuthorities(),
|
||||
authenticationResult.getClientRegistration().getRegistrationId());
|
||||
|
||||
OAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(
|
||||
authenticationResult.getClientRegistration(),
|
||||
oauth2Authentication.getName(),
|
||||
authenticationResult.getAccessToken());
|
||||
|
||||
this.authorizedClientService.saveAuthorizedClient(
|
||||
oauth2Authentication.getAuthorizedClient(), oauth2Authentication);
|
||||
authorizedClient, oauth2Authentication);
|
||||
|
||||
this.accessTokenRepository.saveToken(
|
||||
oauth2Authentication.getAuthorizedClient().getAccessToken(),
|
||||
oauth2Authentication.getAuthorizedClient().getClientRegistration(),
|
||||
authorizedClient.getAccessToken(),
|
||||
authorizedClient.getClientRegistration(),
|
||||
oauth2Authentication);
|
||||
|
||||
return oauth2Authentication;
|
||||
|
|
|
@ -27,9 +27,9 @@ import org.springframework.security.core.Authentication;
|
|||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.core.authority.AuthorityUtils;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
|
||||
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService;
|
||||
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
|
||||
import org.springframework.security.oauth2.client.authentication.OAuth2LoginAuthenticationToken;
|
||||
import org.springframework.security.oauth2.client.registration.ClientRegistration;
|
||||
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
|
||||
import org.springframework.security.oauth2.core.OAuth2AccessToken;
|
||||
|
@ -48,6 +48,7 @@ import java.util.HashMap;
|
|||
import java.util.Map;
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* Tests {@link OAuth2LoginAuthenticationFilter}.
|
||||
|
@ -99,13 +100,18 @@ public class OAuth2LoginAuthenticationFilterTests {
|
|||
@Test
|
||||
public void doFilterWhenAuthorizationCodeSuccessResponseThenAuthenticationSuccessHandlerIsCalled() throws Exception {
|
||||
ClientRegistration clientRegistration = TestUtil.githubClientRegistration();
|
||||
OAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(
|
||||
clientRegistration, "principal", mock(OAuth2AccessToken.class));
|
||||
OAuth2User oauth2User = mock(OAuth2User.class);
|
||||
when(oauth2User.getName()).thenReturn("principal name");
|
||||
OAuth2LoginAuthenticationToken loginAuthentication = mock(OAuth2LoginAuthenticationToken.class);
|
||||
when(loginAuthentication.getPrincipal()).thenReturn(oauth2User);
|
||||
when(loginAuthentication.getClientRegistration()).thenReturn(clientRegistration);
|
||||
when(loginAuthentication.getAccessToken()).thenReturn(mock(OAuth2AccessToken.class));
|
||||
|
||||
OAuth2AuthenticationToken userAuthentication = new OAuth2AuthenticationToken(
|
||||
mock(OAuth2User.class), AuthorityUtils.createAuthorityList("ROLE_USER"), authorizedClient);
|
||||
oauth2User, AuthorityUtils.NO_AUTHORITIES, clientRegistration.getRegistrationId());
|
||||
SecurityContextHolder.getContext().setAuthentication(userAuthentication);
|
||||
AuthenticationManager authenticationManager = mock(AuthenticationManager.class);
|
||||
Mockito.when(authenticationManager.authenticate(Matchers.any(Authentication.class))).thenReturn(userAuthentication);
|
||||
when(authenticationManager.authenticate(Matchers.any(Authentication.class))).thenReturn(loginAuthentication);
|
||||
|
||||
OAuth2LoginAuthenticationFilter filter = Mockito.spy(setupFilter(authenticationManager, clientRegistration));
|
||||
AuthenticationSuccessHandler successHandler = mock(AuthenticationSuccessHandler.class);
|
||||
|
|
|
@ -30,12 +30,16 @@ import org.springframework.boot.SpringBootConfiguration;
|
|||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.oauth2.client.InMemoryOAuth2AuthorizedClientService;
|
||||
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
|
||||
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService;
|
||||
import org.springframework.security.oauth2.client.endpoint.AuthorizationGrantTokenExchanger;
|
||||
import org.springframework.security.oauth2.client.endpoint.OAuth2AuthorizationCodeGrantRequest;
|
||||
import org.springframework.security.oauth2.client.registration.ClientRegistration;
|
||||
|
@ -388,5 +392,13 @@ public class OAuth2LoginApplicationTests {
|
|||
@EnableAutoConfiguration
|
||||
@ComponentScan(basePackages = "sample.web")
|
||||
public static class SpringBootApplicationTestConfig {
|
||||
|
||||
@Autowired
|
||||
private ClientRegistrationRepository clientRegistrationRepository;
|
||||
|
||||
@Bean
|
||||
public OAuth2AuthorizedClientService<OAuth2AuthorizedClient> authorizedClientService() {
|
||||
return new InMemoryOAuth2AuthorizedClientService<>(this.clientRegistrationRepository);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* Copyright 2002-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 sample.config;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.oauth2.client.InMemoryOAuth2AuthorizedClientService;
|
||||
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
|
||||
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService;
|
||||
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
|
||||
|
||||
/**
|
||||
* @author Joe Grandja
|
||||
*/
|
||||
@Configuration
|
||||
public class OAuth2LoginConfig {
|
||||
|
||||
@Autowired
|
||||
private ClientRegistrationRepository clientRegistrationRepository;
|
||||
|
||||
@Bean
|
||||
public OAuth2AuthorizedClientService<OAuth2AuthorizedClient> authorizedClientService() {
|
||||
return new InMemoryOAuth2AuthorizedClientService<>(this.clientRegistrationRepository);
|
||||
}
|
||||
}
|
|
@ -15,10 +15,11 @@
|
|||
*/
|
||||
package sample.web;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.security.core.annotation.AuthenticationPrincipal;
|
||||
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
|
||||
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService;
|
||||
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
|
||||
import org.springframework.security.oauth2.core.user.OAuth2User;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
@ -37,21 +38,26 @@ import java.util.Map;
|
|||
@Controller
|
||||
public class MainController {
|
||||
|
||||
@Autowired
|
||||
private OAuth2AuthorizedClientService authorizedClientService;
|
||||
|
||||
@RequestMapping("/")
|
||||
public String index(Model model, @AuthenticationPrincipal OAuth2User user, OAuth2AuthenticationToken authentication) {
|
||||
model.addAttribute("userName", user.getName());
|
||||
model.addAttribute("clientName", authentication.getAuthorizedClient().getClientRegistration().getClientName());
|
||||
public String index(Model model, OAuth2AuthenticationToken authentication) {
|
||||
OAuth2AuthorizedClient authorizedClient = this.getAuthorizedClient(authentication);
|
||||
model.addAttribute("userName", authentication.getName());
|
||||
model.addAttribute("clientName", authorizedClient.getClientRegistration().getClientName());
|
||||
return "index";
|
||||
}
|
||||
|
||||
@RequestMapping("/userinfo")
|
||||
public String userinfo(Model model, OAuth2AuthenticationToken authentication) {
|
||||
OAuth2AuthorizedClient authorizedClient = this.getAuthorizedClient(authentication);
|
||||
Map userAttributes = Collections.emptyMap();
|
||||
String userInfoEndpointUri = authentication.getAuthorizedClient().getClientRegistration()
|
||||
String userInfoEndpointUri = authorizedClient.getClientRegistration()
|
||||
.getProviderDetails().getUserInfoEndpoint().getUri();
|
||||
if (!StringUtils.isEmpty(userInfoEndpointUri)) { // userInfoEndpointUri is optional for OIDC Clients
|
||||
userAttributes = WebClient.builder()
|
||||
.filter(oauth2Credentials(authentication))
|
||||
.filter(oauth2Credentials(authorizedClient))
|
||||
.build()
|
||||
.get()
|
||||
.uri(userInfoEndpointUri)
|
||||
|
@ -63,11 +69,16 @@ public class MainController {
|
|||
return "userinfo";
|
||||
}
|
||||
|
||||
private ExchangeFilterFunction oauth2Credentials(OAuth2AuthenticationToken authentication) {
|
||||
private OAuth2AuthorizedClient getAuthorizedClient(OAuth2AuthenticationToken authentication) {
|
||||
return this.authorizedClientService.loadAuthorizedClient(
|
||||
authentication.getAuthorizedClientRegistrationId(), authentication);
|
||||
}
|
||||
|
||||
private ExchangeFilterFunction oauth2Credentials(OAuth2AuthorizedClient authorizedClient) {
|
||||
return ExchangeFilterFunction.ofRequestProcessor(
|
||||
clientRequest -> {
|
||||
ClientRequest authorizedRequest = ClientRequest.from(clientRequest)
|
||||
.header(HttpHeaders.AUTHORIZATION, "Bearer " + authentication.getAuthorizedClient().getAccessToken().getTokenValue())
|
||||
.header(HttpHeaders.AUTHORIZATION, "Bearer " + authorizedClient.getAccessToken().getTokenValue())
|
||||
.build();
|
||||
return Mono.just(authorizedRequest);
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue