Add OIDC Client and User Authentication

Fixes gh-4521
This commit is contained in:
Joe Grandja 2017-09-19 20:57:56 -04:00
parent c54c622124
commit 991a154703
6 changed files with 129 additions and 23 deletions

View File

@ -32,8 +32,11 @@ import org.springframework.security.oauth2.client.web.AuthorizationGrantTokenExc
import org.springframework.security.oauth2.core.AccessToken;
import org.springframework.security.oauth2.core.endpoint.TokenResponseAttributes;
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.IdToken;
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;
@ -68,6 +71,7 @@ import java.util.Collection;
* @since 5.0
* @see AuthorizationCodeAuthenticationToken
* @see OAuth2ClientAuthenticationToken
* @see OidcClientAuthenticationToken
* @see OAuth2UserAuthenticationToken
* @see AuthorizationGrantTokenExchanger
* @see TokenResponseAttributes
@ -75,6 +79,7 @@ import java.util.Collection;
* @see IdToken
* @see OAuth2UserService
* @see OAuth2User
* @see OidcUser
* @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="http://openid.net/specs/openid-connect-core-1_0.html#CodeFlowAuth">Section 3.1 OpenID Connect Authorization Code 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>
@ -128,8 +133,12 @@ public class AuthorizationCodeAuthenticationProvider implements AuthenticationPr
idToken = new IdToken(jwt.getTokenValue(), jwt.getIssuedAt(), jwt.getExpiresAt(), jwt.getClaims());
}
OAuth2ClientAuthenticationToken oauth2ClientAuthentication =
new OAuth2ClientAuthenticationToken(clientRegistration, accessToken, idToken);
OAuth2ClientAuthenticationToken oauth2ClientAuthentication;
if (idToken != null) {
oauth2ClientAuthentication = new OidcClientAuthenticationToken(clientRegistration, accessToken, idToken);
} else {
oauth2ClientAuthentication = new OAuth2ClientAuthenticationToken(clientRegistration, accessToken);
}
oauth2ClientAuthentication.setDetails(authorizationCodeAuthentication.getDetails());
OAuth2User user = this.userInfoService.loadUser(oauth2ClientAuthentication);
@ -137,8 +146,13 @@ public class AuthorizationCodeAuthenticationProvider implements AuthenticationPr
Collection<? extends GrantedAuthority> authorities =
this.authoritiesMapper.mapAuthorities(user.getAuthorities());
OAuth2UserAuthenticationToken oauth2UserAuthentication =
new OAuth2UserAuthenticationToken(user, authorities, oauth2ClientAuthentication);
OAuth2UserAuthenticationToken oauth2UserAuthentication;
if (OidcUser.class.isAssignableFrom(user.getClass())) {
oauth2UserAuthentication = new OidcUserAuthenticationToken(
(OidcUser)user, authorities, (OidcClientAuthenticationToken)oauth2ClientAuthentication);
} else {
oauth2UserAuthentication = new OAuth2UserAuthenticationToken(user, authorities, oauth2ClientAuthentication);
}
oauth2UserAuthentication.setDetails(oauth2ClientAuthentication.getDetails());
this.accessTokenRepository.saveSecurityToken(accessToken, oauth2UserAuthentication);

View File

@ -21,7 +21,6 @@ import org.springframework.security.core.SpringSecurityCoreVersion;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.oauth2.client.registration.ClientRegistration;
import org.springframework.security.oauth2.core.AccessToken;
import org.springframework.security.oauth2.oidc.core.IdToken;
import org.springframework.util.Assert;
/**
@ -38,24 +37,19 @@ import org.springframework.util.Assert;
* @since 5.0
* @see ClientRegistration
* @see AccessToken
* @see IdToken
* @see <a target="_blank" href="https://tools.ietf.org/html/rfc6749#section-5.1">Section 5.1 Access Token Response</a>
*/
public class OAuth2ClientAuthenticationToken extends AbstractAuthenticationToken {
private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;
private final ClientRegistration clientRegistration;
private final AccessToken accessToken;
private final IdToken idToken;
public OAuth2ClientAuthenticationToken(ClientRegistration clientRegistration,
AccessToken accessToken, IdToken idToken) {
public OAuth2ClientAuthenticationToken(ClientRegistration clientRegistration, AccessToken accessToken) {
super(AuthorityUtils.NO_AUTHORITIES);
Assert.notNull(clientRegistration, "clientRegistration cannot be null");
Assert.notNull(accessToken, "accessToken cannot be null");
this.clientRegistration = clientRegistration;
this.accessToken = accessToken;
this.idToken = idToken;
this.setAuthenticated(true); // The Client is authenticated by the Authorization Server
}
@ -76,8 +70,4 @@ public class OAuth2ClientAuthenticationToken extends AbstractAuthenticationToken
public AccessToken getAccessToken() {
return this.accessToken;
}
public IdToken getIdToken() {
return this.idToken;
}
}

View File

@ -29,8 +29,8 @@ import java.util.Collection;
* that represents an <i>OAuth 2.0 User</i> {@link Authentication}.
*
* <p>
* This {@link Authentication} associates an {@link OAuth2User} principal
* to an <i>&quot;Authorized Client&quot;</i> identified in {@link #getClientAuthentication()}.
* This {@link Authentication} associates an {@link OAuth2User} principal to a
* {@link OAuth2ClientAuthenticationToken} which represents the <i>&quot;Authorized Client&quot;</i>.
*
* @author Joe Grandja
* @since 5.0

View File

@ -36,6 +36,7 @@ import org.springframework.security.oauth2.core.OAuth2Error;
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.security.oauth2.oidc.core.UserInfo;
import org.springframework.security.oauth2.oidc.core.user.DefaultOidcUser;
import org.springframework.security.oauth2.oidc.core.user.OidcUser;
@ -65,6 +66,7 @@ import java.util.Set;
* @author Joe Grandja
* @since 5.0
* @see OAuth2ClientAuthenticationToken
* @see OidcClientAuthenticationToken
* @see OAuth2User
* @see OidcUser
* @see UserInfo
@ -86,14 +88,14 @@ public class NimbusOAuth2UserService implements OAuth2UserService {
if (this.getCustomUserTypes().containsKey(userInfoUri)) {
return this.loadCustomUser(token);
}
if (token.getIdToken() != null) {
return this.loadOidcUser(token);
if (OidcClientAuthenticationToken.class.isAssignableFrom(token.getClass())) {
return this.loadOidcUser((OidcClientAuthenticationToken)token);
}
return this.loadOAuth2User(token);
}
protected OAuth2User loadOidcUser(OAuth2ClientAuthenticationToken token) throws OAuth2AuthenticationException {
protected OidcUser loadOidcUser(OidcClientAuthenticationToken token) throws OAuth2AuthenticationException {
// TODO Retrieving the UserInfo should be optional. Need to add the capability for opting in/out
Map<String, Object> userAttributes = this.getUserInfo(token);
UserInfo userInfo = new UserInfo(userAttributes);
@ -135,10 +137,9 @@ public class NimbusOAuth2UserService implements OAuth2UserService {
}
Map<String, Object> userAttributes = this.getUserInfo(token);
if (token.getIdToken() != null) {
userAttributes.putAll(token.getIdToken().getClaims());
if (OidcClientAuthenticationToken.class.isAssignableFrom(token.getClass())) {
userAttributes.putAll(((OidcClientAuthenticationToken)token).getIdToken().getClaims());
}
BeanWrapper wrapper = PropertyAccessorFactory.forBeanPropertyAccess(user);
wrapper.setAutoGrowNestedPaths(true);
wrapper.setPropertyValues(userAttributes);

View File

@ -0,0 +1,56 @@
/*
* 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.oauth2.client.authentication.OAuth2ClientAuthenticationToken;
import org.springframework.security.oauth2.client.registration.ClientRegistration;
import org.springframework.security.oauth2.core.AccessToken;
import org.springframework.security.oauth2.oidc.core.IdToken;
import org.springframework.util.Assert;
/**
* A {@link OAuth2ClientAuthenticationToken} that represents an
* <i>OpenID Connect 1.0 Client</i> {@link Authentication} (also known as <i>Relying Party</i>).
*
* <p>
* A client is considered <i>&quot;authenticated&quot;</i>,
* if it receives a successful response from the <i>Token Endpoint</i>.
* This {@link Authentication} associates the client identified in {@link #getClientRegistration()}
* to the {@link #getAccessToken()} granted by the resource owner along with the {@link #getIdToken()}
* containing Claims about the authentication of the End-User.
*
* @author Joe Grandja
* @since 5.0
* @see IdToken
* @see OAuth2ClientAuthenticationToken
* @see <a target="_blank" href="http://openid.net/specs/openid-connect-core-1_0.html#TokenResponse">3.1.3.3 Successful Token Response</a>
*/
public class OidcClientAuthenticationToken extends OAuth2ClientAuthenticationToken {
private final IdToken idToken;
public OidcClientAuthenticationToken(ClientRegistration clientRegistration,
AccessToken accessToken, IdToken idToken) {
super(clientRegistration, accessToken);
Assert.notNull(idToken, "idToken cannot be null");
this.idToken = idToken;
}
public IdToken getIdToken() {
return this.idToken;
}
}

View File

@ -0,0 +1,45 @@
/*
* 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.OAuth2UserAuthenticationToken;
import org.springframework.security.oauth2.oidc.core.user.OidcUser;
import java.util.Collection;
/**
* A {@link OAuth2UserAuthenticationToken} that represents an
* <i>OpenID Connect 1.0 User</i> {@link Authentication}.
*
* <p>
* This {@link Authentication} associates an {@link OidcUser} principal to a
* {@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);
}
}