Polish oauth2-client

This commit is contained in:
Joe Grandja 2017-10-12 10:38:24 -04:00
parent d4d7199a6d
commit c441f99567
34 changed files with 191 additions and 276 deletions

View File

@ -991,7 +991,7 @@ public final class HttpSecurity extends
* .and() * .and()
* .oauth2Login() * .oauth2Login()
* .clients(this.clientRegistrationRepository()) * .clients(this.clientRegistrationRepository())
* .authorizationRequestBuilder(this.authorizationRequestBuilder()) * .authorizationRequestUriBuilder(this.authorizationRequestUriBuilder())
* .authorizationCodeTokenExchanger(this.authorizationCodeTokenExchanger()) * .authorizationCodeTokenExchanger(this.authorizationCodeTokenExchanger())
* .userInfoEndpoint() * .userInfoEndpoint()
* .userInfoService(this.userInfoService()) * .userInfoService(this.userInfoService())
@ -1008,7 +1008,7 @@ public final class HttpSecurity extends
* } * }
* *
* @Bean * @Bean
* public AuthorizationRequestUriBuilder authorizationRequestBuilder() { * public AuthorizationRequestUriBuilder authorizationRequestUriBuilder() {
* // Custom URI builder for the "Authorization Request" * // Custom URI builder for the "Authorization Request"
* return new AuthorizationRequestUriBuilderImpl(); * return new AuthorizationRequestUriBuilderImpl();
* } * }

View File

@ -54,7 +54,7 @@ public class AuthorizationCodeGrantConfigurer<B extends HttpSecurityBuilder<B>>
// ***** Authorization Request members // ***** Authorization Request members
private AuthorizationRequestRedirectFilter authorizationRequestFilter; private AuthorizationRequestRedirectFilter authorizationRequestFilter;
private String authorizationRequestBaseUri; private String authorizationRequestBaseUri;
private AuthorizationRequestUriBuilder authorizationRequestBuilder; private AuthorizationRequestUriBuilder authorizationRequestUriBuilder;
private AuthorizationRequestRepository authorizationRequestRepository; private AuthorizationRequestRepository authorizationRequestRepository;
// ***** Authorization Response members // ***** Authorization Response members
@ -71,9 +71,9 @@ public class AuthorizationCodeGrantConfigurer<B extends HttpSecurityBuilder<B>>
return this; return this;
} }
public AuthorizationCodeGrantConfigurer<B> authorizationRequestBuilder(AuthorizationRequestUriBuilder authorizationRequestBuilder) { public AuthorizationCodeGrantConfigurer<B> authorizationRequestUriBuilder(AuthorizationRequestUriBuilder authorizationRequestUriBuilder) {
Assert.notNull(authorizationRequestBuilder, "authorizationRequestBuilder cannot be null"); Assert.notNull(authorizationRequestUriBuilder, "authorizationRequestUriBuilder cannot be null");
this.authorizationRequestBuilder = authorizationRequestBuilder; this.authorizationRequestUriBuilder = authorizationRequestUriBuilder;
return this; return this;
} }
@ -134,8 +134,8 @@ public class AuthorizationCodeGrantConfigurer<B extends HttpSecurityBuilder<B>>
this.authorizationRequestFilter = new AuthorizationRequestRedirectFilter( this.authorizationRequestFilter = new AuthorizationRequestRedirectFilter(
this.getAuthorizationRequestBaseUri(), this.getClientRegistrationRepository()); this.getAuthorizationRequestBaseUri(), this.getClientRegistrationRepository());
if (this.authorizationRequestBuilder != null) { if (this.authorizationRequestUriBuilder != null) {
this.authorizationRequestFilter.setAuthorizationUriBuilder(this.authorizationRequestBuilder); this.authorizationRequestFilter.setAuthorizationRequestUriBuilder(this.authorizationRequestUriBuilder);
} }
if (this.authorizationRequestRepository != null) { if (this.authorizationRequestRepository != null) {
this.authorizationRequestFilter.setAuthorizationRequestRepository(this.authorizationRequestRepository); this.authorizationRequestFilter.setAuthorizationRequestRepository(this.authorizationRequestRepository);

View File

@ -33,7 +33,7 @@ public final class ImplicitGrantConfigurer<B extends HttpSecurityBuilder<B>> ext
AbstractHttpConfigurer<ImplicitGrantConfigurer<B>, B> { AbstractHttpConfigurer<ImplicitGrantConfigurer<B>, B> {
private String authorizationRequestBaseUri; private String authorizationRequestBaseUri;
private AuthorizationRequestUriBuilder authorizationRequestBuilder; private AuthorizationRequestUriBuilder authorizationRequestUriBuilder;
public ImplicitGrantConfigurer<B> authorizationRequestBaseUri(String authorizationRequestBaseUri) { public ImplicitGrantConfigurer<B> authorizationRequestBaseUri(String authorizationRequestBaseUri) {
Assert.hasText(authorizationRequestBaseUri, "authorizationRequestBaseUri cannot be empty"); Assert.hasText(authorizationRequestBaseUri, "authorizationRequestBaseUri cannot be empty");
@ -41,9 +41,9 @@ public final class ImplicitGrantConfigurer<B extends HttpSecurityBuilder<B>> ext
return this; return this;
} }
public ImplicitGrantConfigurer<B> authorizationRequestBuilder(AuthorizationRequestUriBuilder authorizationRequestBuilder) { public ImplicitGrantConfigurer<B> authorizationRequestUriBuilder(AuthorizationRequestUriBuilder authorizationRequestUriBuilder) {
Assert.notNull(authorizationRequestBuilder, "authorizationRequestBuilder cannot be null"); Assert.notNull(authorizationRequestUriBuilder, "authorizationRequestUriBuilder cannot be null");
this.authorizationRequestBuilder = authorizationRequestBuilder; this.authorizationRequestUriBuilder = authorizationRequestUriBuilder;
return this; return this;
} }
@ -57,8 +57,8 @@ public final class ImplicitGrantConfigurer<B extends HttpSecurityBuilder<B>> ext
public void configure(B http) throws Exception { public void configure(B http) throws Exception {
AuthorizationRequestRedirectFilter authorizationRequestFilter = new AuthorizationRequestRedirectFilter( AuthorizationRequestRedirectFilter authorizationRequestFilter = new AuthorizationRequestRedirectFilter(
this.getAuthorizationRequestBaseUri(), this.getClientRegistrationRepository()); this.getAuthorizationRequestBaseUri(), this.getClientRegistrationRepository());
if (this.authorizationRequestBuilder != null) { if (this.authorizationRequestUriBuilder != null) {
authorizationRequestFilter.setAuthorizationUriBuilder(this.authorizationRequestBuilder); authorizationRequestFilter.setAuthorizationRequestUriBuilder(this.authorizationRequestUriBuilder);
} }
http.addFilter(this.postProcess(authorizationRequestFilter)); http.addFilter(this.postProcess(authorizationRequestFilter));
} }

View File

@ -100,9 +100,9 @@ public final class OAuth2LoginConfigurer<B extends HttpSecurityBuilder<B>> exten
return this; return this;
} }
public AuthorizationEndpointConfig authorizationRequestBuilder(AuthorizationRequestUriBuilder authorizationRequestBuilder) { public AuthorizationEndpointConfig authorizationRequestUriBuilder(AuthorizationRequestUriBuilder authorizationRequestUriBuilder) {
Assert.notNull(authorizationRequestBuilder, "authorizationRequestBuilder cannot be null"); Assert.notNull(authorizationRequestUriBuilder, "authorizationRequestUriBuilder cannot be null");
authorizationCodeGrantConfigurer.authorizationRequestBuilder(authorizationRequestBuilder); authorizationCodeGrantConfigurer.authorizationRequestUriBuilder(authorizationRequestUriBuilder);
return this; return this;
} }

View File

@ -87,14 +87,14 @@ public class AuthorizationCodeAuthenticationProvider implements AuthenticationPr
throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString()); throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());
} }
OAuth2ClientAuthenticationToken oauth2ClientAuthentication = OAuth2ClientAuthenticationToken clientAuthentication =
this.authorizationCodeAuthenticator.authenticate(authorizationCodeAuthentication); this.authorizationCodeAuthenticator.authenticate(authorizationCodeAuthentication);
this.accessTokenRepository.saveSecurityToken( this.accessTokenRepository.saveSecurityToken(
oauth2ClientAuthentication.getAccessToken(), clientAuthentication.getAccessToken(),
oauth2ClientAuthentication.getClientRegistration()); clientAuthentication.getClientRegistration());
return oauth2ClientAuthentication; return clientAuthentication;
} }
public final void setAccessTokenRepository(SecurityTokenRepository<AccessToken> accessTokenRepository) { public final void setAccessTokenRepository(SecurityTokenRepository<AccessToken> accessTokenRepository) {

View File

@ -27,6 +27,8 @@ import org.springframework.util.Assert;
* *
* @author Joe Grandja * @author Joe Grandja
* @since 5.0 * @since 5.0
* @see AuthorizationCodeAuthenticationToken
* @see AuthorizationGrantTokenExchanger
*/ */
public class AuthorizationCodeAuthenticator implements AuthorizationGrantAuthenticator<AuthorizationCodeAuthenticationToken> { public class AuthorizationCodeAuthenticator implements AuthorizationGrantAuthenticator<AuthorizationCodeAuthenticationToken> {
private final AuthorizationGrantTokenExchanger<AuthorizationCodeAuthenticationToken> authorizationCodeTokenExchanger; private final AuthorizationGrantTokenExchanger<AuthorizationCodeAuthenticationToken> authorizationCodeTokenExchanger;
@ -46,7 +48,7 @@ public class AuthorizationCodeAuthenticator implements AuthorizationGrantAuthent
// If the openid scope value is not present, the behavior is entirely unspecified. // If the openid scope value is not present, the behavior is entirely unspecified.
if (authorizationCodeAuthentication.getAuthorizationRequest().getScope().contains("openid")) { if (authorizationCodeAuthentication.getAuthorizationRequest().getScope().contains("openid")) {
// The OpenID Connect implementation of AuthorizationGrantAuthenticator // The OpenID Connect implementation of AuthorizationGrantAuthenticator
// must handle OpenID Connect Authentication Requests // should handle OpenID Connect Authentication Requests
return null; return null;
} }
@ -57,10 +59,10 @@ public class AuthorizationCodeAuthenticator implements AuthorizationGrantAuthent
tokenResponse.getTokenValue(), tokenResponse.getIssuedAt(), tokenResponse.getTokenValue(), tokenResponse.getIssuedAt(),
tokenResponse.getExpiresAt(), tokenResponse.getScope()); tokenResponse.getExpiresAt(), tokenResponse.getScope());
OAuth2ClientAuthenticationToken oauth2ClientAuthentication = OAuth2ClientAuthenticationToken clientAuthentication =
new OAuth2ClientAuthenticationToken(authorizationCodeAuthentication.getClientRegistration(), accessToken); new OAuth2ClientAuthenticationToken(authorizationCodeAuthentication.getClientRegistration(), accessToken);
oauth2ClientAuthentication.setDetails(authorizationCodeAuthentication.getDetails()); clientAuthentication.setDetails(authorizationCodeAuthentication.getDetails());
return oauth2ClientAuthentication; return clientAuthentication;
} }
} }

View File

@ -39,28 +39,28 @@ import org.springframework.util.Assert;
* @since 5.0 * @since 5.0
*/ */
public class OAuth2AuthenticationException extends AuthenticationException { public class OAuth2AuthenticationException extends AuthenticationException {
private OAuth2Error errorObject; private OAuth2Error error;
public OAuth2AuthenticationException(OAuth2Error errorObject, Throwable cause) { public OAuth2AuthenticationException(OAuth2Error error, Throwable cause) {
this(errorObject, cause.getMessage(), cause); this(error, cause.getMessage(), cause);
} }
public OAuth2AuthenticationException(OAuth2Error errorObject, String message) { public OAuth2AuthenticationException(OAuth2Error error, String message) {
super(message); super(message);
this.setErrorObject(errorObject); this.setError(error);
} }
public OAuth2AuthenticationException(OAuth2Error errorObject, String message, Throwable cause) { public OAuth2AuthenticationException(OAuth2Error error, String message, Throwable cause) {
super(message, cause); super(message, cause);
this.setErrorObject(errorObject); this.setError(error);
} }
public OAuth2Error getErrorObject() { public OAuth2Error getError() {
return errorObject; return this.error;
} }
private void setErrorObject(OAuth2Error errorObject) { private void setError(OAuth2Error error) {
Assert.notNull(errorObject, "OAuth2 Error object cannot be null"); Assert.notNull(error, "error cannot be null");
this.errorObject = errorObject; this.error = error;
} }
} }

View File

@ -18,12 +18,12 @@ package org.springframework.security.oauth2.client.authentication;
import org.springframework.security.authentication.AbstractAuthenticationToken; import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.core.Authentication; import org.springframework.security.core.Authentication;
import org.springframework.security.core.SpringSecurityCoreVersion; 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.client.registration.ClientRegistration;
import org.springframework.security.oauth2.core.AccessToken; import org.springframework.security.oauth2.core.AccessToken;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
import java.util.Collections;
import java.util.Set; import java.util.Set;
/** /**
@ -48,7 +48,7 @@ public class OAuth2ClientAuthenticationToken extends AbstractAuthenticationToken
private final AccessToken accessToken; private final AccessToken accessToken;
public OAuth2ClientAuthenticationToken(ClientRegistration clientRegistration, AccessToken accessToken) { public OAuth2ClientAuthenticationToken(ClientRegistration clientRegistration, AccessToken accessToken) {
super(AuthorityUtils.NO_AUTHORITIES); super(Collections.emptyList());
Assert.notNull(clientRegistration, "clientRegistration cannot be null"); Assert.notNull(clientRegistration, "clientRegistration cannot be null");
Assert.notNull(accessToken, "accessToken cannot be null"); Assert.notNull(accessToken, "accessToken cannot be null");
this.clientRegistration = clientRegistration; this.clientRegistration = clientRegistration;
@ -63,7 +63,7 @@ public class OAuth2ClientAuthenticationToken extends AbstractAuthenticationToken
@Override @Override
public Object getCredentials() { public Object getCredentials() {
return this.getAccessToken(); return ""; // No need to expose this.getClientRegistration().getClientSecret()
} }
public ClientRegistration getClientRegistration() { public ClientRegistration getClientRegistration() {
@ -74,13 +74,13 @@ public class OAuth2ClientAuthenticationToken extends AbstractAuthenticationToken
return this.accessToken; return this.accessToken;
} }
public Set<String> getAuthorizedScope() { public final Set<String> getAuthorizedScope() {
// As per spec, in section 5.1 Successful Access Token Response // As per spec, in section 5.1 Successful Access Token Response
// https://tools.ietf.org/html/rfc6749#section-5.1 // https://tools.ietf.org/html/rfc6749#section-5.1
// If AccessToken.scope is empty, then default to the scope // If AccessToken.scope is empty, then default to the scope
// originally requested by the client in the Authorization Request // originally requested by the client in the Authorization Request
return (!CollectionUtils.isEmpty(this.getAccessToken().getScope()) ? return (CollectionUtils.isEmpty(this.getAccessToken().getScope()) ?
this.getAccessToken().getScope() : this.getClientRegistration().getScope() :
this.getClientRegistration().getScope()); this.getAccessToken().getScope());
} }
} }

View File

@ -19,11 +19,11 @@ import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.core.Authentication; import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.SpringSecurityCoreVersion; import org.springframework.security.core.SpringSecurityCoreVersion;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.oauth2.core.user.OAuth2User; import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import java.util.Collection; import java.util.Collection;
import java.util.Collections;
/** /**
* An implementation of an {@link AbstractAuthenticationToken} * An implementation of an {@link AbstractAuthenticationToken}
@ -44,7 +44,7 @@ public class OAuth2UserAuthenticationToken extends AbstractAuthenticationToken {
private final OAuth2ClientAuthenticationToken clientAuthentication; private final OAuth2ClientAuthenticationToken clientAuthentication;
public OAuth2UserAuthenticationToken(OAuth2ClientAuthenticationToken clientAuthentication) { public OAuth2UserAuthenticationToken(OAuth2ClientAuthenticationToken clientAuthentication) {
this(null, AuthorityUtils.NO_AUTHORITIES, clientAuthentication); this(null, Collections.emptyList(), clientAuthentication);
} }
public OAuth2UserAuthenticationToken(OAuth2User principal, Collection<? extends GrantedAuthority> authorities, public OAuth2UserAuthenticationToken(OAuth2User principal, Collection<? extends GrantedAuthority> authorities,

View File

@ -19,7 +19,7 @@ import org.springframework.security.jwt.JwtDecoder;
import org.springframework.security.oauth2.client.registration.ClientRegistration; import org.springframework.security.oauth2.client.registration.ClientRegistration;
/** /**
* A registry for {@link JwtDecoder}'s that are associated to a {@link ClientRegistration}. * A registry of {@link JwtDecoder}'s that are associated to a {@link ClientRegistration}.
* *
* @author Joe Grandja * @author Joe Grandja
* @since 5.0 * @since 5.0

View File

@ -26,8 +26,8 @@ import java.util.HashMap;
import java.util.Map; import java.util.Map;
/** /**
* A {@link JwtDecoderRegistry} that uses the <b>Nimbus JOSE + JWT SDK</b> * A {@link JwtDecoderRegistry} that creates/manages instances of
* to create/manage instances of {@link NimbusJwtDecoderJwkSupport} internally. * {@link NimbusJwtDecoderJwkSupport}, which uses the <b>Nimbus JOSE + JWT SDK</b> internally.
* *
* @author Joe Grandja * @author Joe Grandja
* @since 5.0 * @since 5.0

View File

@ -186,19 +186,19 @@ public class ClientRegistration {
} }
public static class Builder { public static class Builder {
protected String registrationId; private String registrationId;
protected String clientId; private String clientId;
protected String clientSecret; private String clientSecret;
protected ClientAuthenticationMethod clientAuthenticationMethod = ClientAuthenticationMethod.BASIC; private ClientAuthenticationMethod clientAuthenticationMethod = ClientAuthenticationMethod.BASIC;
protected AuthorizationGrantType authorizationGrantType; private AuthorizationGrantType authorizationGrantType;
protected String redirectUri; private String redirectUri;
protected Set<String> scope; private Set<String> scope;
protected String authorizationUri; private String authorizationUri;
protected String tokenUri; private String tokenUri;
protected String userInfoUri; private String userInfoUri;
protected String userNameAttributeName; private String userNameAttributeName;
protected String jwkSetUri; private String jwkSetUri;
protected String clientName; private String clientName;
public Builder(String registrationId) { public Builder(String registrationId) {
this.registrationId = registrationId; this.registrationId = registrationId;
@ -212,7 +212,7 @@ public class ClientRegistration {
this.authorizationGrantType(clientRegistrationProperties.getAuthorizationGrantType()); this.authorizationGrantType(clientRegistrationProperties.getAuthorizationGrantType());
this.redirectUri(clientRegistrationProperties.getRedirectUri()); this.redirectUri(clientRegistrationProperties.getRedirectUri());
if (!CollectionUtils.isEmpty(clientRegistrationProperties.getScope())) { if (!CollectionUtils.isEmpty(clientRegistrationProperties.getScope())) {
this.scope(clientRegistrationProperties.getScope().stream().toArray(String[]::new)); this.scope(clientRegistrationProperties.getScope().toArray(new String[0]));
} }
this.authorizationUri(clientRegistrationProperties.getAuthorizationUri()); this.authorizationUri(clientRegistrationProperties.getAuthorizationUri());
this.tokenUri(clientRegistrationProperties.getTokenUri()); this.tokenUri(clientRegistrationProperties.getTokenUri());
@ -230,7 +230,7 @@ public class ClientRegistration {
this.authorizationGrantType(clientRegistration.getAuthorizationGrantType()); this.authorizationGrantType(clientRegistration.getAuthorizationGrantType());
this.redirectUri(clientRegistration.getRedirectUri()); this.redirectUri(clientRegistration.getRedirectUri());
if (!CollectionUtils.isEmpty(clientRegistration.getScope())) { if (!CollectionUtils.isEmpty(clientRegistration.getScope())) {
this.scope(clientRegistration.getScope().stream().toArray(String[]::new)); this.scope(clientRegistration.getScope().toArray(new String[0]));
} }
this.authorizationUri(clientRegistration.getProviderDetails().getAuthorizationUri()); this.authorizationUri(clientRegistration.getProviderDetails().getAuthorizationUri());
this.tokenUri(clientRegistration.getProviderDetails().getTokenUri()); this.tokenUri(clientRegistration.getProviderDetails().getTokenUri());

View File

@ -28,21 +28,21 @@ import java.util.Map;
* *
* @author Joe Grandja * @author Joe Grandja
* @since 5.0 * @since 5.0
* @see ClientRegistrationRepository
* @see ClientRegistration * @see ClientRegistration
*/ */
public final class InMemoryClientRegistrationRepository implements ClientRegistrationRepository, Iterable<ClientRegistration> { public final class InMemoryClientRegistrationRepository implements ClientRegistrationRepository, Iterable<ClientRegistration> {
private final ClientRegistrationIdentifierStrategy<String> identifierStrategy = new RegistrationIdIdentifierStrategy();
private final Map<String, ClientRegistration> registrations; private final Map<String, ClientRegistration> registrations;
public InMemoryClientRegistrationRepository(List<ClientRegistration> registrations) { public InMemoryClientRegistrationRepository(List<ClientRegistration> registrations) {
Assert.notEmpty(registrations, "registrations cannot be empty"); Assert.notEmpty(registrations, "registrations cannot be empty");
Map<String, ClientRegistration> registrationsMap = new HashMap<>(); Map<String, ClientRegistration> registrationsMap = new HashMap<>();
registrations.forEach(registration -> { registrations.forEach(registration -> {
String identifier = this.identifierStrategy.getIdentifier(registration); if (registrationsMap.containsKey(registration.getRegistrationId())) {
if (registrationsMap.containsKey(identifier)) { throw new IllegalArgumentException("ClientRegistration must be unique. Found duplicate registrationId: " +
throw new IllegalArgumentException("ClientRegistration must be unique. Found duplicate identifier: " + identifier); registration.getRegistrationId());
} }
registrationsMap.put(identifier, registration); registrationsMap.put(registration.getRegistrationId(), registration);
}); });
this.registrations = Collections.unmodifiableMap(registrationsMap); this.registrations = Collections.unmodifiableMap(registrationsMap);
} }

View File

@ -1,35 +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.registration;
import org.springframework.util.Assert;
/**
* A {@link ClientRegistrationIdentifierStrategy} that identifies a {@link ClientRegistration}
* using the {@link ClientRegistration#getRegistrationId()}.
*
* @author Joe Grandja
* @since 5.0
* @see ClientRegistration
*/
public class RegistrationIdIdentifierStrategy implements ClientRegistrationIdentifierStrategy<String> {
@Override
public String getIdentifier(ClientRegistration clientRegistration) {
Assert.notNull(clientRegistration, "clientRegistration cannot be null");
return clientRegistration.getRegistrationId();
}
}

View File

@ -20,8 +20,7 @@ import org.springframework.security.oauth2.core.SecurityToken;
/** /**
* Implementations of this interface are responsible for the persistence * Implementations of this interface are responsible for the persistence
* and association of an OAuth 2.0 / OpenID Connect 1.0 * and association of a {@link SecurityToken} to a {@link ClientRegistration Client}.
* {@link SecurityToken} to a {@link ClientRegistration Client}.
* *
* @author Joe Grandja * @author Joe Grandja
* @since 5.0 * @since 5.0

View File

@ -58,7 +58,7 @@ public class CustomUserTypesOAuth2UserService implements OAuth2UserService {
public OAuth2User loadUser(OAuth2ClientAuthenticationToken clientAuthentication) throws OAuth2AuthenticationException { public OAuth2User loadUser(OAuth2ClientAuthenticationToken clientAuthentication) throws OAuth2AuthenticationException {
URI userInfoUri = URI.create(clientAuthentication.getClientRegistration().getProviderDetails().getUserInfoEndpoint().getUri()); URI userInfoUri = URI.create(clientAuthentication.getClientRegistration().getProviderDetails().getUserInfoEndpoint().getUri());
Class<? extends OAuth2User> customUserType; Class<? extends OAuth2User> customUserType;
if ((customUserType = this.getCustomUserTypes().get(userInfoUri)) == null) { if ((customUserType = this.customUserTypes.get(userInfoUri)) == null) {
return null; return null;
} }
@ -82,14 +82,6 @@ public class CustomUserTypesOAuth2UserService implements OAuth2UserService {
return customUser; return customUser;
} }
protected Map<URI, Class<? extends OAuth2User>> getCustomUserTypes() {
return this.customUserTypes;
}
protected UserInfoRetriever getUserInfoRetriever() {
return this.userInfoRetriever;
}
public final void setUserInfoRetriever(UserInfoRetriever userInfoRetriever) { public final void setUserInfoRetriever(UserInfoRetriever userInfoRetriever) {
Assert.notNull(userInfoRetriever, "userInfoRetriever cannot be null"); Assert.notNull(userInfoRetriever, "userInfoRetriever cannot be null");
this.userInfoRetriever = userInfoRetriever; this.userInfoRetriever = userInfoRetriever;

View File

@ -53,9 +53,6 @@ import java.util.Set;
public class DefaultOAuth2UserService implements OAuth2UserService { public class DefaultOAuth2UserService implements OAuth2UserService {
private UserInfoRetriever userInfoRetriever = new NimbusUserInfoRetriever(); private UserInfoRetriever userInfoRetriever = new NimbusUserInfoRetriever();
public DefaultOAuth2UserService() {
}
@Override @Override
public OAuth2User loadUser(OAuth2ClientAuthenticationToken clientAuthentication) throws OAuth2AuthenticationException { public OAuth2User loadUser(OAuth2ClientAuthenticationToken clientAuthentication) throws OAuth2AuthenticationException {
if (OidcClientAuthenticationToken.class.isAssignableFrom(clientAuthentication.getClass())) { if (OidcClientAuthenticationToken.class.isAssignableFrom(clientAuthentication.getClass())) {
@ -69,7 +66,7 @@ public class DefaultOAuth2UserService implements OAuth2UserService {
clientAuthentication.getClientRegistration().getRegistrationId()); clientAuthentication.getClientRegistration().getRegistrationId());
} }
Map<String, Object> userAttributes = this.getUserInfoRetriever().retrieve(clientAuthentication); Map<String, Object> userAttributes = this.userInfoRetriever.retrieve(clientAuthentication);
GrantedAuthority authority = new OAuth2UserAuthority(userAttributes); GrantedAuthority authority = new OAuth2UserAuthority(userAttributes);
Set<GrantedAuthority> authorities = new HashSet<>(); Set<GrantedAuthority> authorities = new HashSet<>();
authorities.add(authority); authorities.add(authority);
@ -77,10 +74,6 @@ public class DefaultOAuth2UserService implements OAuth2UserService {
return new DefaultOAuth2User(authorities, userAttributes, userNameAttributeName); return new DefaultOAuth2User(authorities, userAttributes, userNameAttributeName);
} }
protected UserInfoRetriever getUserInfoRetriever() {
return this.userInfoRetriever;
}
public final void setUserInfoRetriever(UserInfoRetriever userInfoRetriever) { public final void setUserInfoRetriever(UserInfoRetriever userInfoRetriever) {
Assert.notNull(userInfoRetriever, "userInfoRetriever cannot be null"); Assert.notNull(userInfoRetriever, "userInfoRetriever cannot be null");
this.userInfoRetriever = userInfoRetriever; this.userInfoRetriever = userInfoRetriever;

View File

@ -37,17 +37,17 @@ import java.util.Objects;
* @see OAuth2User * @see OAuth2User
*/ */
public class DelegatingOAuth2UserService implements OAuth2UserService { public class DelegatingOAuth2UserService implements OAuth2UserService {
private final List<OAuth2UserService> oauth2UserServices; private final List<OAuth2UserService> userServices;
public DelegatingOAuth2UserService(List<OAuth2UserService> oauth2UserServices) { public DelegatingOAuth2UserService(List<OAuth2UserService> userServices) {
Assert.notEmpty(oauth2UserServices, "oauth2UserServices cannot be empty"); Assert.notEmpty(userServices, "userServices cannot be empty");
this.oauth2UserServices = oauth2UserServices; this.userServices = userServices;
} }
@Override @Override
public OAuth2User loadUser(OAuth2ClientAuthenticationToken clientAuthentication) throws OAuth2AuthenticationException { public OAuth2User loadUser(OAuth2ClientAuthenticationToken clientAuthentication) throws OAuth2AuthenticationException {
OAuth2User oauth2User = this.oauth2UserServices.stream() OAuth2User oauth2User = this.userServices.stream()
.map(oauth2UserService -> oauth2UserService.loadUser(clientAuthentication)) .map(userService -> userService.loadUser(clientAuthentication))
.filter(Objects::nonNull) .filter(Objects::nonNull)
.findFirst() .findFirst()
.orElse(null); .orElse(null);

View File

@ -1,74 +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.user.nimbus;
import com.nimbusds.oauth2.sdk.http.HTTPResponse;
import org.springframework.http.HttpHeaders;
import org.springframework.http.client.AbstractClientHttpResponse;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.util.Assert;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
/**
* An implementation of a {@link ClientHttpResponse} which is used by {@link NimbusUserInfoRetriever}.
*
* <p>
* <b>NOTE:</b> This class is intended for internal use only.
*
* @author Joe Grandja
* @since 5.0
*/
final class NimbusClientHttpResponse extends AbstractClientHttpResponse {
private final HTTPResponse httpResponse;
private final HttpHeaders headers;
NimbusClientHttpResponse(HTTPResponse httpResponse) {
Assert.notNull(httpResponse, "httpResponse cannot be null");
this.httpResponse = httpResponse;
this.headers = new HttpHeaders();
this.headers.setAll(httpResponse.getHeaders());
}
@Override
public int getRawStatusCode() throws IOException {
return this.httpResponse.getStatusCode();
}
@Override
public String getStatusText() throws IOException {
return String.valueOf(this.getRawStatusCode());
}
@Override
public void close() {
}
@Override
public InputStream getBody() throws IOException {
InputStream inputStream = new ByteArrayInputStream(
this.httpResponse.getContent().getBytes(Charset.forName("UTF-8")));
return inputStream;
}
@Override
public HttpHeaders getHeaders() {
return this.headers;
}
}

View File

@ -22,6 +22,8 @@ import com.nimbusds.oauth2.sdk.http.HTTPResponse;
import com.nimbusds.oauth2.sdk.token.BearerAccessToken; import com.nimbusds.oauth2.sdk.token.BearerAccessToken;
import com.nimbusds.openid.connect.sdk.UserInfoErrorResponse; import com.nimbusds.openid.connect.sdk.UserInfoErrorResponse;
import com.nimbusds.openid.connect.sdk.UserInfoRequest; import com.nimbusds.openid.connect.sdk.UserInfoRequest;
import org.springframework.http.HttpHeaders;
import org.springframework.http.client.AbstractClientHttpResponse;
import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.security.authentication.AuthenticationServiceException; import org.springframework.security.authentication.AuthenticationServiceException;
@ -29,9 +31,13 @@ import org.springframework.security.oauth2.client.authentication.OAuth2Authentic
import org.springframework.security.oauth2.client.authentication.OAuth2ClientAuthenticationToken; import org.springframework.security.oauth2.client.authentication.OAuth2ClientAuthenticationToken;
import org.springframework.security.oauth2.client.user.UserInfoRetriever; import org.springframework.security.oauth2.client.user.UserInfoRetriever;
import org.springframework.security.oauth2.core.OAuth2Error; import org.springframework.security.oauth2.core.OAuth2Error;
import org.springframework.util.Assert;
import java.io.ByteArrayInputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.net.URI; import java.net.URI;
import java.nio.charset.Charset;
import java.util.Map; import java.util.Map;
/** /**
@ -100,4 +106,42 @@ public class NimbusUserInfoRetriever implements UserInfoRetriever {
throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString(), ex); throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString(), ex);
} }
} }
private static class NimbusClientHttpResponse extends AbstractClientHttpResponse {
private final HTTPResponse httpResponse;
private final HttpHeaders headers;
private NimbusClientHttpResponse(HTTPResponse httpResponse) {
Assert.notNull(httpResponse, "httpResponse cannot be null");
this.httpResponse = httpResponse;
this.headers = new HttpHeaders();
this.headers.setAll(httpResponse.getHeaders());
}
@Override
public int getRawStatusCode() throws IOException {
return this.httpResponse.getStatusCode();
}
@Override
public String getStatusText() throws IOException {
return String.valueOf(this.getRawStatusCode());
}
@Override
public void close() {
}
@Override
public InputStream getBody() throws IOException {
InputStream inputStream = new ByteArrayInputStream(
this.httpResponse.getContent().getBytes(Charset.forName("UTF-8")));
return inputStream;
}
@Override
public HttpHeaders getHeaders() {
return this.headers;
}
}
} }

View File

@ -65,9 +65,10 @@ import java.io.IOException;
* @see AbstractAuthenticationProcessingFilter * @see AbstractAuthenticationProcessingFilter
* @see AuthorizationCodeAuthenticationToken * @see AuthorizationCodeAuthenticationToken
* @see AuthorizationCodeAuthenticationProvider * @see AuthorizationCodeAuthenticationProvider
* @see AuthorizationRequestRedirectFilter * @see AuthorizationResponse
* @see AuthorizationRequest * @see AuthorizationRequest
* @see AuthorizationRequestRepository * @see AuthorizationRequestRepository
* @see AuthorizationRequestRedirectFilter
* @see ClientRegistrationRepository * @see ClientRegistrationRepository
* @see <a target="_blank" href="https://tools.ietf.org/html/rfc6749#section-4.1">Section 4.1 Authorization Code Grant</a> * @see <a target="_blank" href="https://tools.ietf.org/html/rfc6749#section-4.1">Section 4.1 Authorization Code Grant</a>
* @see <a target="_blank" href="https://tools.ietf.org/html/rfc6749#section-4.1.2">Section 4.1.2 Authorization Response</a> * @see <a target="_blank" href="https://tools.ietf.org/html/rfc6749#section-4.1.2">Section 4.1.2 Authorization Response</a>
@ -125,11 +126,11 @@ public class AuthorizationCodeAuthenticationFilter extends AbstractAuthenticatio
clientRegistration, authorizationRequest, authorizationResponse); clientRegistration, authorizationRequest, authorizationResponse);
authorizationCodeAuthentication.setDetails(this.authenticationDetailsSource.buildDetails(request)); authorizationCodeAuthentication.setDetails(this.authenticationDetailsSource.buildDetails(request));
OAuth2ClientAuthenticationToken oauth2ClientAuthentication = OAuth2ClientAuthenticationToken clientAuthentication =
(OAuth2ClientAuthenticationToken)this.getAuthenticationManager().authenticate(authorizationCodeAuthentication); (OAuth2ClientAuthenticationToken)this.getAuthenticationManager().authenticate(authorizationCodeAuthentication);
return this.getAuthenticationManager().authenticate( return this.getAuthenticationManager().authenticate(
new OAuth2UserAuthenticationToken(oauth2ClientAuthentication)); new OAuth2UserAuthenticationToken(clientAuthentication));
} }
public final RequestMatcher getAuthorizationResponseMatcher() { public final RequestMatcher getAuthorizationResponseMatcher() {
@ -192,12 +193,12 @@ public class AuthorizationCodeAuthenticationFilter extends AbstractAuthenticatio
.state(state) .state(state)
.build(); .build();
} else { } else {
String description = request.getParameter(OAuth2Parameter.ERROR_DESCRIPTION); String errorDescription = request.getParameter(OAuth2Parameter.ERROR_DESCRIPTION);
String uri = request.getParameter(OAuth2Parameter.ERROR_URI); String errorUri = request.getParameter(OAuth2Parameter.ERROR_URI);
return AuthorizationResponse.error(errorCode) return AuthorizationResponse.error(errorCode)
.redirectUri(redirectUri) .redirectUri(redirectUri)
.errorDescription(description) .errorDescription(errorDescription)
.errorUri(uri) .errorUri(errorUri)
.state(state) .state(state)
.build(); .build();
} }

View File

@ -64,10 +64,10 @@ import java.util.Map;
*/ */
public class AuthorizationRequestRedirectFilter extends OncePerRequestFilter { public class AuthorizationRequestRedirectFilter extends OncePerRequestFilter {
public static final String DEFAULT_AUTHORIZATION_REQUEST_BASE_URI = "/oauth2/authorization"; public static final String DEFAULT_AUTHORIZATION_REQUEST_BASE_URI = "/oauth2/authorization";
public static final String REGISTRATION_ID_URI_VARIABLE_NAME = "registrationId"; private static final String REGISTRATION_ID_URI_VARIABLE_NAME = "registrationId";
private final AntPathRequestMatcher authorizationRequestMatcher; private final AntPathRequestMatcher authorizationRequestMatcher;
private final ClientRegistrationRepository clientRegistrationRepository; private final ClientRegistrationRepository clientRegistrationRepository;
private AuthorizationRequestUriBuilder authorizationUriBuilder = new DefaultAuthorizationRequestUriBuilder(); private AuthorizationRequestUriBuilder authorizationRequestUriBuilder = new DefaultAuthorizationRequestUriBuilder();
private final RedirectStrategy authorizationRedirectStrategy = new DefaultRedirectStrategy(); private final RedirectStrategy authorizationRedirectStrategy = new DefaultRedirectStrategy();
private final StringKeyGenerator stateGenerator = new DefaultStateGenerator(); private final StringKeyGenerator stateGenerator = new DefaultStateGenerator();
private AuthorizationRequestRepository authorizationRequestRepository = new HttpSessionAuthorizationRequestRepository(); private AuthorizationRequestRepository authorizationRequestRepository = new HttpSessionAuthorizationRequestRepository();
@ -86,9 +86,9 @@ public class AuthorizationRequestRedirectFilter extends OncePerRequestFilter {
this.clientRegistrationRepository = clientRegistrationRepository; this.clientRegistrationRepository = clientRegistrationRepository;
} }
public final void setAuthorizationUriBuilder(AuthorizationRequestUriBuilder authorizationUriBuilder) { public final void setAuthorizationRequestUriBuilder(AuthorizationRequestUriBuilder authorizationRequestUriBuilder) {
Assert.notNull(authorizationUriBuilder, "authorizationUriBuilder cannot be null"); Assert.notNull(authorizationRequestUriBuilder, "authorizationRequestUriBuilder cannot be null");
this.authorizationUriBuilder = authorizationUriBuilder; this.authorizationRequestUriBuilder = authorizationRequestUriBuilder;
} }
public final void setAuthorizationRequestRepository(AuthorizationRequestRepository authorizationRequestRepository) { public final void setAuthorizationRequestRepository(AuthorizationRequestRepository authorizationRequestRepository) {
@ -112,18 +112,18 @@ public class AuthorizationRequestRedirectFilter extends OncePerRequestFilter {
filterChain.doFilter(request, response); filterChain.doFilter(request, response);
} }
protected boolean shouldRequestAuthorization(HttpServletRequest request, HttpServletResponse response) { private boolean shouldRequestAuthorization(HttpServletRequest request, HttpServletResponse response) {
return this.authorizationRequestMatcher.matches(request); return this.authorizationRequestMatcher.matches(request);
} }
protected void sendRedirectForAuthorization(HttpServletRequest request, HttpServletResponse response) private void sendRedirectForAuthorization(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException { throws IOException, ServletException {
String registrationId = this.authorizationRequestMatcher String registrationId = this.authorizationRequestMatcher
.extractUriTemplateVariables(request).get(REGISTRATION_ID_URI_VARIABLE_NAME); .extractUriTemplateVariables(request).get(REGISTRATION_ID_URI_VARIABLE_NAME);
ClientRegistration clientRegistration = this.clientRegistrationRepository.findByRegistrationId(registrationId); ClientRegistration clientRegistration = this.clientRegistrationRepository.findByRegistrationId(registrationId);
if (clientRegistration == null) { if (clientRegistration == null) {
throw new IllegalArgumentException("Invalid Client Identifier (Registration Id): " + registrationId); throw new IllegalArgumentException("Invalid Client Registration with Id: " + registrationId);
} }
String redirectUriStr = this.expandRedirectUri(request, clientRegistration); String redirectUriStr = this.expandRedirectUri(request, clientRegistration);
@ -153,11 +153,11 @@ public class AuthorizationRequestRedirectFilter extends OncePerRequestFilter {
this.authorizationRequestRepository.saveAuthorizationRequest(authorizationRequest, request, response); this.authorizationRequestRepository.saveAuthorizationRequest(authorizationRequest, request, response);
} }
URI redirectUri = this.authorizationUriBuilder.build(authorizationRequest); URI redirectUri = this.authorizationRequestUriBuilder.build(authorizationRequest);
this.authorizationRedirectStrategy.sendRedirect(request, response, redirectUri.toString()); this.authorizationRedirectStrategy.sendRedirect(request, response, redirectUri.toString());
} }
protected void unsuccessfulRedirectForAuthorization(HttpServletRequest request, HttpServletResponse response, private void unsuccessfulRedirectForAuthorization(HttpServletRequest request, HttpServletResponse response,
Exception failed) throws IOException, ServletException { Exception failed) throws IOException, ServletException {
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {

View File

@ -27,8 +27,8 @@ import javax.servlet.http.HttpServletResponse;
* <p> * <p>
* Used by the {@link AuthorizationRequestRedirectFilter} for persisting the <i>Authorization Request</i> * Used by the {@link AuthorizationRequestRedirectFilter} for persisting the <i>Authorization Request</i>
* before it initiates the authorization code grant flow. * before it initiates the authorization code grant flow.
* As well, used by the {@link AuthorizationCodeAuthenticationFilter} when resolving * As well, used by the {@link AuthorizationCodeAuthenticationFilter} for resolving
* the associated <i>Authorization Request</i> during the handling of the <i>Authorization Response</i>. * the associated <i>Authorization Request</i> when handling the <i>Authorization Response</i>.
* *
* @author Joe Grandja * @author Joe Grandja
* @since 5.0 * @since 5.0
@ -42,6 +42,6 @@ public interface AuthorizationRequestRepository {
void saveAuthorizationRequest(AuthorizationRequest authorizationRequest, HttpServletRequest request, void saveAuthorizationRequest(AuthorizationRequest authorizationRequest, HttpServletRequest request,
HttpServletResponse response); HttpServletResponse response);
AuthorizationRequest removeAuthorizationRequest(HttpServletRequest request); void removeAuthorizationRequest(HttpServletRequest request);
} }

View File

@ -24,7 +24,7 @@ import java.util.stream.Collectors;
/** /**
* The default implementation of an {@link AuthorizationRequestUriBuilder}, * The default implementation of an {@link AuthorizationRequestUriBuilder},
* which internally uses an {@link UriComponentsBuilder} to construct the <i>OAuth 2.0 Authorization Request</i>. * which internally uses a {@link UriComponentsBuilder} to construct the <i>OAuth 2.0 Authorization Request</i>.
* *
* @author Joe Grandja * @author Joe Grandja
* @since 5.0 * @since 5.0

View File

@ -15,16 +15,16 @@
*/ */
package org.springframework.security.oauth2.client.web; package org.springframework.security.oauth2.client.web;
import java.util.Base64;
import org.springframework.security.crypto.keygen.BytesKeyGenerator; import org.springframework.security.crypto.keygen.BytesKeyGenerator;
import org.springframework.security.crypto.keygen.KeyGenerators; import org.springframework.security.crypto.keygen.KeyGenerators;
import org.springframework.security.crypto.keygen.StringKeyGenerator; import org.springframework.security.crypto.keygen.StringKeyGenerator;
import org.springframework.security.oauth2.core.endpoint.OAuth2Parameter;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import java.util.Base64;
/** /**
* The default implementation for generating the * The default implementation for generating the {@link OAuth2Parameter#STATE} parameter
* {@link org.springframework.security.oauth2.core.endpoint.OAuth2Parameter#STATE} parameter
* used in the <i>Authorization Request</i> and correlated in the <i>Authorization Response</i> (or <i>Error Response</i>). * used in the <i>Authorization Request</i> and correlated in the <i>Authorization Response</i> (or <i>Error Response</i>).
* *
* <p> * <p>
@ -36,16 +36,16 @@ import org.springframework.util.Assert;
* @since 5.0 * @since 5.0
*/ */
public class DefaultStateGenerator implements StringKeyGenerator { public class DefaultStateGenerator implements StringKeyGenerator {
private static final int DEFAULT_BYTE_LENGTH = 32; private static final int DEFAULT_KEY_LENGTH = 32;
private final BytesKeyGenerator keyGenerator; private final BytesKeyGenerator keyGenerator;
public DefaultStateGenerator() { public DefaultStateGenerator() {
this(DEFAULT_BYTE_LENGTH); this(DEFAULT_KEY_LENGTH);
} }
public DefaultStateGenerator(int byteLength) { public DefaultStateGenerator(int keyLength) {
Assert.isTrue(byteLength > 0, "byteLength must be greater than 0"); Assert.isTrue(keyLength >= DEFAULT_KEY_LENGTH, "keyLength must be greater than " + DEFAULT_KEY_LENGTH);
this.keyGenerator = KeyGenerators.secureRandom(byteLength); this.keyGenerator = KeyGenerators.secureRandom(keyLength);
} }
@Override @Override

View File

@ -32,16 +32,15 @@ import javax.servlet.http.HttpSession;
public final class HttpSessionAuthorizationRequestRepository implements AuthorizationRequestRepository { public final class HttpSessionAuthorizationRequestRepository implements AuthorizationRequestRepository {
private static final String DEFAULT_AUTHORIZATION_REQUEST_ATTR_NAME = private static final String DEFAULT_AUTHORIZATION_REQUEST_ATTR_NAME =
HttpSessionAuthorizationRequestRepository.class.getName() + ".AUTHORIZATION_REQUEST"; HttpSessionAuthorizationRequestRepository.class.getName() + ".AUTHORIZATION_REQUEST";
private String sessionAttributeName = DEFAULT_AUTHORIZATION_REQUEST_ATTR_NAME; private final String sessionAttributeName = DEFAULT_AUTHORIZATION_REQUEST_ATTR_NAME;
@Override @Override
public AuthorizationRequest loadAuthorizationRequest(HttpServletRequest request) { public AuthorizationRequest loadAuthorizationRequest(HttpServletRequest request) {
AuthorizationRequest authorizationRequest = null;
HttpSession session = request.getSession(false); HttpSession session = request.getSession(false);
if (session != null) { if (session != null) {
authorizationRequest = (AuthorizationRequest) session.getAttribute(this.sessionAttributeName); return (AuthorizationRequest) session.getAttribute(this.sessionAttributeName);
} }
return authorizationRequest; return null;
} }
@Override @Override
@ -55,11 +54,7 @@ public final class HttpSessionAuthorizationRequestRepository implements Authoriz
} }
@Override @Override
public AuthorizationRequest removeAuthorizationRequest(HttpServletRequest request) { public void removeAuthorizationRequest(HttpServletRequest request) {
AuthorizationRequest authorizationRequest = this.loadAuthorizationRequest(request); request.getSession().removeAttribute(this.sessionAttributeName);
if (authorizationRequest != null) {
request.getSession().removeAttribute(this.sessionAttributeName);
}
return authorizationRequest;
} }
} }

View File

@ -33,9 +33,9 @@ import com.nimbusds.oauth2.sdk.id.ClientID;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.security.authentication.AuthenticationServiceException; import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.oauth2.client.authentication.AuthorizationCodeAuthenticationToken; import org.springframework.security.oauth2.client.authentication.AuthorizationCodeAuthenticationToken;
import org.springframework.security.oauth2.client.web.AuthorizationGrantTokenExchanger;
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationException; import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationException;
import org.springframework.security.oauth2.client.registration.ClientRegistration; import org.springframework.security.oauth2.client.registration.ClientRegistration;
import org.springframework.security.oauth2.client.web.AuthorizationGrantTokenExchanger;
import org.springframework.security.oauth2.core.AccessToken; import org.springframework.security.oauth2.core.AccessToken;
import org.springframework.security.oauth2.core.ClientAuthenticationMethod; import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
import org.springframework.security.oauth2.core.OAuth2Error; import org.springframework.security.oauth2.core.OAuth2Error;
@ -45,10 +45,10 @@ import org.springframework.util.CollectionUtils;
import java.io.IOException; import java.io.IOException;
import java.net.URI; import java.net.URI;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet; import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors;
/** /**
* An implementation of an {@link AuthorizationGrantTokenExchanger} that <i>&quot;exchanges&quot;</i> * An implementation of an {@link AuthorizationGrantTokenExchanger} that <i>&quot;exchanges&quot;</i>
@ -60,6 +60,7 @@ import java.util.stream.Collectors;
* *
* @author Joe Grandja * @author Joe Grandja
* @since 5.0 * @since 5.0
* @see AuthorizationGrantTokenExchanger
* @see AuthorizationCodeAuthenticationToken * @see AuthorizationCodeAuthenticationToken
* @see TokenResponse * @see TokenResponse
* @see <a target="_blank" href="https://connect2id.com/products/nimbus-oauth-openid-connect-sdk">Nimbus OAuth 2.0 SDK</a> * @see <a target="_blank" href="https://connect2id.com/products/nimbus-oauth-openid-connect-sdk">Nimbus OAuth 2.0 SDK</a>
@ -70,17 +71,17 @@ public class NimbusAuthorizationCodeTokenExchanger implements AuthorizationGrant
private static final String INVALID_TOKEN_RESPONSE_ERROR_CODE = "invalid_token_response"; private static final String INVALID_TOKEN_RESPONSE_ERROR_CODE = "invalid_token_response";
@Override @Override
public TokenResponse exchange(AuthorizationCodeAuthenticationToken authorizationCodeAuthenticationToken) public TokenResponse exchange(AuthorizationCodeAuthenticationToken authorizationCodeAuthentication)
throws OAuth2AuthenticationException { throws OAuth2AuthenticationException {
ClientRegistration clientRegistration = authorizationCodeAuthenticationToken.getClientRegistration(); ClientRegistration clientRegistration = authorizationCodeAuthentication.getClientRegistration();
// Build the authorization code grant request for the token endpoint // Build the authorization code grant request for the token endpoint
AuthorizationCode authorizationCode = new AuthorizationCode( AuthorizationCode authorizationCode = new AuthorizationCode(
authorizationCodeAuthenticationToken.getAuthorizationResponse().getCode()); authorizationCodeAuthentication.getAuthorizationResponse().getCode());
URI redirectUri = this.toURI(clientRegistration.getRedirectUri()); URI redirectUri = toURI(clientRegistration.getRedirectUri());
AuthorizationGrant authorizationCodeGrant = new AuthorizationCodeGrant(authorizationCode, redirectUri); AuthorizationGrant authorizationCodeGrant = new AuthorizationCodeGrant(authorizationCode, redirectUri);
URI tokenUri = this.toURI(clientRegistration.getProviderDetails().getTokenUri()); URI tokenUri = toURI(clientRegistration.getProviderDetails().getTokenUri());
// Set the credentials to authenticate the client at the token endpoint // Set the credentials to authenticate the client at the token endpoint
ClientID clientId = new ClientID(clientRegistration.getClientId()); ClientID clientId = new ClientID(clientRegistration.getClientId());
@ -102,11 +103,8 @@ public class NimbusAuthorizationCodeTokenExchanger implements AuthorizationGrant
httpRequest.setReadTimeout(30000); httpRequest.setReadTimeout(30000);
tokenResponse = com.nimbusds.oauth2.sdk.TokenResponse.parse(httpRequest.send()); tokenResponse = com.nimbusds.oauth2.sdk.TokenResponse.parse(httpRequest.send());
} catch (ParseException pe) { } catch (ParseException pe) {
// This error occurs if the Access Token Response is not well-formed,
// for example, a required attribute is missing
throw new OAuth2AuthenticationException(new OAuth2Error(INVALID_TOKEN_RESPONSE_ERROR_CODE), pe); throw new OAuth2AuthenticationException(new OAuth2Error(INVALID_TOKEN_RESPONSE_ERROR_CODE), pe);
} catch (IOException ioe) { } catch (IOException ioe) {
// This error occurs when there is a network-related issue
throw new AuthenticationServiceException("An error occurred while sending the Access Token Request: " + throw new AuthenticationServiceException("An error occurred while sending the Access Token Request: " +
ioe.getMessage(), ioe); ioe.getMessage(), ioe);
} }
@ -129,10 +127,9 @@ public class NimbusAuthorizationCodeTokenExchanger implements AuthorizationGrant
long expiresIn = accessTokenResponse.getTokens().getAccessToken().getLifetime(); long expiresIn = accessTokenResponse.getTokens().getAccessToken().getLifetime();
Set<String> scope = Collections.emptySet(); Set<String> scope = Collections.emptySet();
if (!CollectionUtils.isEmpty(accessTokenResponse.getTokens().getAccessToken().getScope())) { if (!CollectionUtils.isEmpty(accessTokenResponse.getTokens().getAccessToken().getScope())) {
scope = new HashSet<>(accessTokenResponse.getTokens().getAccessToken().getScope().toStringList()); scope = new LinkedHashSet<>(accessTokenResponse.getTokens().getAccessToken().getScope().toStringList());
} }
Map<String, Object> additionalParameters = accessTokenResponse.getCustomParameters().entrySet().stream() Map<String, Object> additionalParameters = new LinkedHashMap<>(accessTokenResponse.getCustomParameters());
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
return TokenResponse.withToken(accessToken) return TokenResponse.withToken(accessToken)
.tokenType(accessTokenType) .tokenType(accessTokenType)
@ -142,7 +139,7 @@ public class NimbusAuthorizationCodeTokenExchanger implements AuthorizationGrant
.build(); .build();
} }
private URI toURI(String uriStr) { private static URI toURI(String uriStr) {
try { try {
return new URI(uriStr); return new URI(uriStr);
} catch (Exception ex) { } catch (Exception ex) {

View File

@ -27,6 +27,7 @@ import org.springframework.security.oauth2.client.web.AuthorizationGrantTokenExc
import org.springframework.security.oauth2.core.AccessToken; import org.springframework.security.oauth2.core.AccessToken;
import org.springframework.security.oauth2.core.endpoint.TokenResponse; import org.springframework.security.oauth2.core.endpoint.TokenResponse;
import org.springframework.security.oauth2.oidc.core.IdToken; 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.endpoint.OidcParameter;
import org.springframework.util.Assert; import org.springframework.util.Assert;
@ -37,6 +38,10 @@ import org.springframework.util.Assert;
* *
* @author Joe Grandja * @author Joe Grandja
* @since 5.0 * @since 5.0
* @see AuthorizationGrantAuthenticator
* @see AuthorizationCodeAuthenticationToken
* @see AuthorizationGrantTokenExchanger
* @see JwtDecoderRegistry
*/ */
public class OidcAuthorizationCodeAuthenticator implements AuthorizationGrantAuthenticator<AuthorizationCodeAuthenticationToken> { public class OidcAuthorizationCodeAuthenticator implements AuthorizationGrantAuthenticator<AuthorizationCodeAuthenticationToken> {
private final AuthorizationGrantTokenExchanger<AuthorizationCodeAuthenticationToken> authorizationCodeTokenExchanger; private final AuthorizationGrantTokenExchanger<AuthorizationCodeAuthenticationToken> authorizationCodeTokenExchanger;
@ -60,7 +65,7 @@ public class OidcAuthorizationCodeAuthenticator implements AuthorizationGrantAut
// scope // scope
// REQUIRED. OpenID Connect requests MUST contain the "openid" scope value. // REQUIRED. OpenID Connect requests MUST contain the "openid" scope value.
// If the openid scope value is not present, the behavior is entirely unspecified. // If the openid scope value is not present, the behavior is entirely unspecified.
if (!authorizationCodeAuthentication.getAuthorizationRequest().getScope().contains("openid")) { if (!authorizationCodeAuthentication.getAuthorizationRequest().getScope().contains(OidcScope.OPENID)) {
return null; return null;
} }
@ -75,21 +80,21 @@ public class OidcAuthorizationCodeAuthenticator implements AuthorizationGrantAut
if (!tokenResponse.getAdditionalParameters().containsKey(OidcParameter.ID_TOKEN)) { if (!tokenResponse.getAdditionalParameters().containsKey(OidcParameter.ID_TOKEN)) {
throw new IllegalArgumentException( throw new IllegalArgumentException(
"Missing (required) ID Token in Token Response for Client Registration: '" + clientRegistration.getRegistrationId() + "'"); "Missing (required) ID Token in Token Response for Client Registration: " + clientRegistration.getRegistrationId());
} }
JwtDecoder jwtDecoder = this.jwtDecoderRegistry.getJwtDecoder(clientRegistration); JwtDecoder jwtDecoder = this.jwtDecoderRegistry.getJwtDecoder(clientRegistration);
if (jwtDecoder == null) { if (jwtDecoder == null) {
throw new IllegalArgumentException("Unable to find a registered JwtDecoder for Client Registration: '" + clientRegistration.getRegistrationId() + throw new IllegalArgumentException("Failed to find a registered JwtDecoder for Client Registration: '" + clientRegistration.getRegistrationId() +
"'. Check to ensure you have configured the JwkSet URI."); "'. Check to ensure you have configured the JwkSet URI.");
} }
Jwt jwt = jwtDecoder.decode((String)tokenResponse.getAdditionalParameters().get(OidcParameter.ID_TOKEN)); Jwt jwt = jwtDecoder.decode((String)tokenResponse.getAdditionalParameters().get(OidcParameter.ID_TOKEN));
IdToken idToken = new IdToken(jwt.getTokenValue(), jwt.getIssuedAt(), jwt.getExpiresAt(), jwt.getClaims()); IdToken idToken = new IdToken(jwt.getTokenValue(), jwt.getIssuedAt(), jwt.getExpiresAt(), jwt.getClaims());
OidcClientAuthenticationToken oidcClientAuthentication = OidcClientAuthenticationToken clientAuthentication =
new OidcClientAuthenticationToken(clientRegistration, accessToken, idToken); new OidcClientAuthenticationToken(clientRegistration, accessToken, idToken);
oidcClientAuthentication.setDetails(authorizationCodeAuthentication.getDetails()); clientAuthentication.setDetails(authorizationCodeAuthentication.getDetails());
return oidcClientAuthentication; return clientAuthentication;
} }
} }

View File

@ -24,7 +24,7 @@ import org.springframework.util.Assert;
/** /**
* A {@link OAuth2ClientAuthenticationToken} that represents an * A {@link OAuth2ClientAuthenticationToken} that represents an
* <i>OpenID Connect 1.0 Client</i> {@link Authentication} (also known as <i>Relying Party</i>). * <i>OpenID Connect 1.0 Client</i> {@link Authentication}.
* *
* <p> * <p>
* A client is considered <i>&quot;authenticated&quot;</i>, * A client is considered <i>&quot;authenticated&quot;</i>,

View File

@ -17,12 +17,12 @@ package org.springframework.security.oauth2.oidc.client.authentication;
import org.springframework.security.core.Authentication; import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.oauth2.client.authentication.OAuth2ClientAuthenticationToken; import org.springframework.security.oauth2.client.authentication.OAuth2ClientAuthenticationToken;
import org.springframework.security.oauth2.client.authentication.OAuth2UserAuthenticationToken; import org.springframework.security.oauth2.client.authentication.OAuth2UserAuthenticationToken;
import org.springframework.security.oauth2.oidc.core.user.OidcUser; import org.springframework.security.oauth2.oidc.core.user.OidcUser;
import java.util.Collection; import java.util.Collection;
import java.util.Collections;
/** /**
* A {@link OAuth2UserAuthenticationToken} that represents an * A {@link OAuth2UserAuthenticationToken} that represents an
@ -41,7 +41,7 @@ import java.util.Collection;
public class OidcUserAuthenticationToken extends OAuth2UserAuthenticationToken { public class OidcUserAuthenticationToken extends OAuth2UserAuthenticationToken {
public OidcUserAuthenticationToken(OidcClientAuthenticationToken clientAuthentication) { public OidcUserAuthenticationToken(OidcClientAuthenticationToken clientAuthentication) {
this(null, AuthorityUtils.NO_AUTHORITIES, clientAuthentication); this(null, Collections.emptyList(), clientAuthentication);
} }
public OidcUserAuthenticationToken(OidcUser principal, Collection<? extends GrantedAuthority> authorities, public OidcUserAuthenticationToken(OidcUser principal, Collection<? extends GrantedAuthority> authorities,

View File

@ -65,7 +65,7 @@ public class OidcUserService implements OAuth2UserService {
UserInfo userInfo = null; UserInfo userInfo = null;
if (this.shouldRetrieveUserInfo(oidcClientAuthentication)) { if (this.shouldRetrieveUserInfo(oidcClientAuthentication)) {
Map<String, Object> userAttributes = this.getUserInfoRetriever().retrieve(oidcClientAuthentication); Map<String, Object> userAttributes = this.userInfoRetriever.retrieve(oidcClientAuthentication);
userInfo = new UserInfo(userAttributes); userInfo = new UserInfo(userAttributes);
} }
@ -76,10 +76,6 @@ public class OidcUserService implements OAuth2UserService {
return new DefaultOidcUser(authorities, oidcClientAuthentication.getIdToken(), userInfo); return new DefaultOidcUser(authorities, oidcClientAuthentication.getIdToken(), userInfo);
} }
protected UserInfoRetriever getUserInfoRetriever() {
return this.userInfoRetriever;
}
public final void setUserInfoRetriever(UserInfoRetriever userInfoRetriever) { public final void setUserInfoRetriever(UserInfoRetriever userInfoRetriever) {
Assert.notNull(userInfoRetriever, "userInfoRetriever cannot be null"); Assert.notNull(userInfoRetriever, "userInfoRetriever cannot be null");
this.userInfoRetriever = userInfoRetriever; this.userInfoRetriever = userInfoRetriever;

View File

@ -165,8 +165,8 @@ public class AuthorizationCodeAuthenticationFilterTests {
Assertions.assertThat(authenticationExceptionArgCaptor.getValue()).isInstanceOf(OAuth2AuthenticationException.class); Assertions.assertThat(authenticationExceptionArgCaptor.getValue()).isInstanceOf(OAuth2AuthenticationException.class);
OAuth2AuthenticationException oauth2AuthenticationException = OAuth2AuthenticationException oauth2AuthenticationException =
(OAuth2AuthenticationException)authenticationExceptionArgCaptor.getValue(); (OAuth2AuthenticationException)authenticationExceptionArgCaptor.getValue();
Assertions.assertThat(oauth2AuthenticationException.getErrorObject()).isNotNull(); Assertions.assertThat(oauth2AuthenticationException.getError()).isNotNull();
Assertions.assertThat(oauth2AuthenticationException.getErrorObject().getErrorCode()).isEqualTo(errorCode); Assertions.assertThat(oauth2AuthenticationException.getError().getErrorCode()).isEqualTo(errorCode);
} }
private AuthorizationCodeAuthenticationFilter setupFilter(ClientRegistration... clientRegistrations) throws Exception { private AuthorizationCodeAuthenticationFilter setupFilter(ClientRegistration... clientRegistrations) throws Exception {

View File

@ -128,7 +128,7 @@ public class AuthorizationRequestRedirectFilterTests {
ClientRegistrationRepository clientRegistrationRepository = TestUtil.clientRegistrationRepository(clientRegistrations); ClientRegistrationRepository clientRegistrationRepository = TestUtil.clientRegistrationRepository(clientRegistrations);
AuthorizationRequestRedirectFilter filter = new AuthorizationRequestRedirectFilter(clientRegistrationRepository); AuthorizationRequestRedirectFilter filter = new AuthorizationRequestRedirectFilter(clientRegistrationRepository);
filter.setAuthorizationUriBuilder(authorizationUriBuilder); filter.setAuthorizationRequestUriBuilder(authorizationUriBuilder);
return filter; return filter;
} }

View File

@ -102,7 +102,7 @@ public class DefaultOAuth2UserTests {
@Test @Test
public void constructorWhenNameAttributeKeyIsInvalidThenThrowsException() { public void constructorWhenNameAttributeKeyIsInvalidThenThrowsException() {
this.thrown.expect(IllegalArgumentException.class); this.thrown.expect(IllegalArgumentException.class);
this.thrown.expectMessage("Invalid nameAttributeKey: invalid"); this.thrown.expectMessage("Missing attribute 'invalid' in attributes");
new DefaultOAuth2User(TEST_AUTHORITIES, TEST_ATTRIBUTES, "invalid"); new DefaultOAuth2User(TEST_AUTHORITIES, TEST_ATTRIBUTES, "invalid");
} }