Remove AuthorizationGrantAuthenticator

This commit is contained in:
Joe Grandja 2017-10-16 13:43:11 -04:00
parent 3c824dc44b
commit a7d054c9f3
7 changed files with 105 additions and 228 deletions

View File

@ -21,13 +21,10 @@ import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.oauth2.client.authentication.AuthorizationCodeAuthenticationProvider;
import org.springframework.security.oauth2.client.authentication.AuthorizationCodeAuthenticationToken;
import org.springframework.security.oauth2.client.authentication.AuthorizationCodeAuthenticator;
import org.springframework.security.oauth2.client.authentication.AuthorizationGrantAuthenticator;
import org.springframework.security.oauth2.client.authentication.AuthorizationGrantTokenExchanger;
import org.springframework.security.oauth2.client.authentication.DelegatingAuthorizationGrantAuthenticator;
import org.springframework.security.oauth2.client.authentication.NimbusAuthorizationCodeTokenExchanger;
import org.springframework.security.oauth2.client.authentication.jwt.JwtDecoderRegistry;
import org.springframework.security.oauth2.client.authentication.jwt.NimbusJwtDecoderRegistry;
import org.springframework.security.oauth2.client.authentication.NimbusAuthorizationCodeTokenExchanger;
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
import org.springframework.security.oauth2.client.token.SecurityTokenRepository;
import org.springframework.security.oauth2.client.web.AuthorizationCodeAuthenticationFilter;
@ -35,13 +32,10 @@ import org.springframework.security.oauth2.client.web.AuthorizationRequestRedire
import org.springframework.security.oauth2.client.web.AuthorizationRequestRepository;
import org.springframework.security.oauth2.client.web.AuthorizationRequestUriBuilder;
import org.springframework.security.oauth2.core.AccessToken;
import org.springframework.security.oauth2.oidc.client.authentication.OidcAuthorizationCodeAuthenticator;
import org.springframework.security.oauth2.oidc.client.authentication.OidcAuthorizationCodeAuthenticationProvider;
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;
import org.springframework.util.Assert;
import java.util.ArrayList;
import java.util.List;
/**
* A security configurer for the Authorization Code Grant type.
*
@ -60,7 +54,6 @@ public class AuthorizationCodeGrantConfigurer<B extends HttpSecurityBuilder<B>>
// ***** Authorization Response members
private AuthorizationCodeAuthenticationFilter authorizationResponseFilter;
private String authorizationResponseBaseUri;
private AuthorizationGrantAuthenticator<AuthorizationCodeAuthenticationToken> authorizationCodeAuthenticator;
private AuthorizationGrantTokenExchanger<AuthorizationCodeAuthenticationToken> authorizationCodeTokenExchanger;
private SecurityTokenRepository<AccessToken> accessTokenRepository;
private JwtDecoderRegistry jwtDecoderRegistry;
@ -89,14 +82,6 @@ public class AuthorizationCodeGrantConfigurer<B extends HttpSecurityBuilder<B>>
return this;
}
public AuthorizationCodeGrantConfigurer<B> authorizationCodeAuthenticator(
AuthorizationGrantAuthenticator<AuthorizationCodeAuthenticationToken> authorizationCodeAuthenticator) {
Assert.notNull(authorizationCodeAuthenticator, "authorizationCodeAuthenticator cannot be null");
this.authorizationCodeAuthenticator = authorizationCodeAuthenticator;
return this;
}
public AuthorizationCodeGrantConfigurer<B> authorizationCodeTokenExchanger(
AuthorizationGrantTokenExchanger<AuthorizationCodeAuthenticationToken> authorizationCodeTokenExchanger) {
@ -125,12 +110,20 @@ public class AuthorizationCodeGrantConfigurer<B extends HttpSecurityBuilder<B>>
@Override
public final void init(B http) throws Exception {
AuthorizationCodeAuthenticationProvider authorizationCodeAuthenticationProvider =
new AuthorizationCodeAuthenticationProvider(this.getAuthorizationCodeAuthenticator());
AuthorizationCodeAuthenticationProvider oauth2AuthorizationCodeAuthenticationProvider =
new AuthorizationCodeAuthenticationProvider(this.getAuthorizationCodeTokenExchanger());
if (this.accessTokenRepository != null) {
authorizationCodeAuthenticationProvider.setAccessTokenRepository(this.accessTokenRepository);
oauth2AuthorizationCodeAuthenticationProvider.setAccessTokenRepository(this.accessTokenRepository);
}
http.authenticationProvider(this.postProcess(authorizationCodeAuthenticationProvider));
http.authenticationProvider(this.postProcess(oauth2AuthorizationCodeAuthenticationProvider));
OidcAuthorizationCodeAuthenticationProvider oidcAuthorizationCodeAuthenticationProvider =
new OidcAuthorizationCodeAuthenticationProvider(
this.getAuthorizationCodeTokenExchanger(), this.getJwtDecoderRegistry());
if (this.accessTokenRepository != null) {
oidcAuthorizationCodeAuthenticationProvider.setAccessTokenRepository(this.accessTokenRepository);
}
http.authenticationProvider(this.postProcess(oidcAuthorizationCodeAuthenticationProvider));
this.authorizationRequestFilter = new AuthorizationRequestRedirectFilter(
this.getAuthorizationRequestBaseUri(), this.getClientRegistrationRepository());
@ -180,17 +173,6 @@ public class AuthorizationCodeGrantConfigurer<B extends HttpSecurityBuilder<B>>
return this.authorizationRequestRepository;
}
private AuthorizationGrantAuthenticator<AuthorizationCodeAuthenticationToken> getAuthorizationCodeAuthenticator() {
if (this.authorizationCodeAuthenticator == null) {
List<AuthorizationGrantAuthenticator<AuthorizationCodeAuthenticationToken>> authenticators = new ArrayList<>();
authenticators.add(new AuthorizationCodeAuthenticator(this.getAuthorizationCodeTokenExchanger()));
authenticators.add(new OidcAuthorizationCodeAuthenticator(
this.getAuthorizationCodeTokenExchanger(), this.getJwtDecoderRegistry()));
this.authorizationCodeAuthenticator = new DelegatingAuthorizationGrantAuthenticator<>(authenticators);;
}
return this.authorizationCodeAuthenticator;
}
private AuthorizationGrantTokenExchanger<AuthorizationCodeAuthenticationToken> getAuthorizationCodeTokenExchanger() {
if (this.authorizationCodeTokenExchanger == null) {
this.authorizationCodeTokenExchanger = new NimbusAuthorizationCodeTokenExchanger();

View File

@ -21,12 +21,11 @@ import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
import org.springframework.security.config.annotation.web.configurers.AbstractAuthenticationFilterConfigurer;
import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;
import org.springframework.security.oauth2.client.authentication.AuthorizationCodeAuthenticationToken;
import org.springframework.security.oauth2.client.authentication.AuthorizationGrantAuthenticator;
import org.springframework.security.oauth2.client.authentication.AuthorizationGrantTokenExchanger;
import org.springframework.security.oauth2.client.authentication.userinfo.OAuth2UserAuthenticationProvider;
import org.springframework.security.oauth2.client.authentication.userinfo.CustomUserTypesOAuth2UserService;
import org.springframework.security.oauth2.client.authentication.userinfo.DefaultOAuth2UserService;
import org.springframework.security.oauth2.client.authentication.userinfo.DelegatingOAuth2UserService;
import org.springframework.security.oauth2.client.authentication.userinfo.OAuth2UserAuthenticationProvider;
import org.springframework.security.oauth2.client.authentication.userinfo.OAuth2UserService;
import org.springframework.security.oauth2.client.registration.ClientRegistration;
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
@ -126,14 +125,6 @@ public final class OAuth2LoginConfigurer<B extends HttpSecurityBuilder<B>> exten
private TokenEndpointConfig() {
}
public TokenEndpointConfig authorizationCodeAuthenticator(
AuthorizationGrantAuthenticator<AuthorizationCodeAuthenticationToken> authorizationCodeAuthenticator) {
Assert.notNull(authorizationCodeAuthenticator, "authorizationCodeAuthenticator cannot be null");
authorizationCodeGrantConfigurer.authorizationCodeAuthenticator(authorizationCodeAuthenticator);
return this;
}
public TokenEndpointConfig authorizationCodeTokenExchanger(
AuthorizationGrantTokenExchanger<AuthorizationCodeAuthenticationToken> authorizationCodeTokenExchanger) {

View File

@ -24,44 +24,37 @@ import org.springframework.security.oauth2.core.AccessToken;
import org.springframework.security.oauth2.core.OAuth2Error;
import org.springframework.security.oauth2.core.endpoint.AuthorizationRequest;
import org.springframework.security.oauth2.core.endpoint.AuthorizationResponse;
import org.springframework.security.oauth2.oidc.client.authentication.OidcClientAuthenticationToken;
import org.springframework.security.oauth2.core.endpoint.TokenResponse;
import org.springframework.util.Assert;
/**
* An implementation of an {@link AuthenticationProvider} that 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 and optionally an
* <i>id token</i> credential (for OpenID Connect Authorization Code Flow).
* An implementation of an {@link AuthenticationProvider}
* for the <i>OAuth 2.0 Authorization Code Grant Flow</i>.
*
* <p>
* The {@link AuthorizationCodeAuthenticationProvider} uses an {@link AuthorizationGrantAuthenticator}
* to authenticate the <i>authorization code</i> credential and ultimately
* return an <i>&quot;Authorized Client&quot;</i> as an {@link OAuth2ClientAuthenticationToken}.
* 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.
*
* @author Joe Grandja
* @since 5.0
* @see AuthorizationCodeAuthenticationToken
* @see OAuth2ClientAuthenticationToken
* @see OidcClientAuthenticationToken
* @see AuthorizationGrantAuthenticator
* @see SecurityTokenRepository
* @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>
* @see <a target="_blank" href="https://tools.ietf.org/html/rfc6749#section-4.1.4">Section 4.1.4 Access Token Response</a>
* @see <a target="_blank" href="http://openid.net/specs/openid-connect-core-1_0.html#TokenResponse">Section 3.1.3.3 OpenID Connect Token Response</a>
*/
public class AuthorizationCodeAuthenticationProvider implements AuthenticationProvider {
private static final String INVALID_STATE_PARAMETER_ERROR_CODE = "invalid_state_parameter";
private static final String INVALID_REDIRECT_URI_PARAMETER_ERROR_CODE = "invalid_redirect_uri_parameter";
private final AuthorizationGrantAuthenticator<AuthorizationCodeAuthenticationToken> authorizationCodeAuthenticator;
private final AuthorizationGrantTokenExchanger<AuthorizationCodeAuthenticationToken> authorizationCodeTokenExchanger;
private SecurityTokenRepository<AccessToken> accessTokenRepository = new InMemoryAccessTokenRepository();
public AuthorizationCodeAuthenticationProvider(
AuthorizationGrantAuthenticator<AuthorizationCodeAuthenticationToken> authorizationCodeAuthenticator) {
AuthorizationGrantTokenExchanger<AuthorizationCodeAuthenticationToken> authorizationCodeTokenExchanger) {
Assert.notNull(authorizationCodeAuthenticator, "authorizationCodeAuthenticator cannot be null");
this.authorizationCodeAuthenticator = authorizationCodeAuthenticator;
Assert.notNull(authorizationCodeTokenExchanger, "authorizationCodeTokenExchanger cannot be null");
this.authorizationCodeTokenExchanger = authorizationCodeTokenExchanger;
}
@Override
@ -69,6 +62,16 @@ public class AuthorizationCodeAuthenticationProvider implements AuthenticationPr
AuthorizationCodeAuthenticationToken authorizationCodeAuthentication =
(AuthorizationCodeAuthenticationToken) authentication;
// Section 3.1.2.1 Authentication Request - http://openid.net/specs/openid-connect-core-1_0.html#AuthRequest
// scope
// REQUIRED. OpenID Connect requests MUST contain the "openid" scope value.
// If the openid scope value is not present, the behavior is entirely unspecified.
if (authorizationCodeAuthentication.getAuthorizationRequest().getScope().contains("openid")) {
// The OpenID Connect implementation of Authorization Code AuthenticationProvider
// should handle OpenID Connect Authentication Requests so don't handle and return null
return null;
}
AuthorizationRequest authorizationRequest = authorizationCodeAuthentication.getAuthorizationRequest();
AuthorizationResponse authorizationResponse = authorizationCodeAuthentication.getAuthorizationResponse();
@ -87,8 +90,16 @@ public class AuthorizationCodeAuthenticationProvider implements AuthenticationPr
throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());
}
TokenResponse tokenResponse =
this.authorizationCodeTokenExchanger.exchange(authorizationCodeAuthentication);
AccessToken accessToken = new AccessToken(tokenResponse.getTokenType(),
tokenResponse.getTokenValue(), tokenResponse.getIssuedAt(),
tokenResponse.getExpiresAt(), tokenResponse.getScope());
OAuth2ClientAuthenticationToken clientAuthentication =
this.authorizationCodeAuthenticator.authenticate(authorizationCodeAuthentication);
new OAuth2ClientAuthenticationToken(authorizationCodeAuthentication.getClientRegistration(), accessToken);
clientAuthentication.setDetails(authorizationCodeAuthentication.getDetails());
this.accessTokenRepository.saveSecurityToken(
clientAuthentication.getAccessToken(),

View File

@ -1,67 +0,0 @@
/*
* Copyright 2012-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.oauth2.client.authentication;
import org.springframework.security.oauth2.core.AccessToken;
import org.springframework.security.oauth2.core.endpoint.TokenResponse;
import org.springframework.util.Assert;
/**
* An implementation of an {@link AuthorizationGrantAuthenticator} that
* <i>&quot;authenticates&quot;</i> an <i>authorization code grant</i> credential
* against an OAuth 2.0 Provider's <i>Token Endpoint</i>.
*
* @author Joe Grandja
* @since 5.0
* @see AuthorizationCodeAuthenticationToken
* @see AuthorizationGrantTokenExchanger
*/
public class AuthorizationCodeAuthenticator implements AuthorizationGrantAuthenticator<AuthorizationCodeAuthenticationToken> {
private final AuthorizationGrantTokenExchanger<AuthorizationCodeAuthenticationToken> authorizationCodeTokenExchanger;
public AuthorizationCodeAuthenticator(AuthorizationGrantTokenExchanger<AuthorizationCodeAuthenticationToken> authorizationCodeTokenExchanger) {
Assert.notNull(authorizationCodeTokenExchanger, "authorizationCodeTokenExchanger cannot be null");
this.authorizationCodeTokenExchanger = authorizationCodeTokenExchanger;
}
@Override
public OAuth2ClientAuthenticationToken authenticate(
AuthorizationCodeAuthenticationToken authorizationCodeAuthentication) throws OAuth2AuthenticationException {
// Section 3.1.2.1 Authentication Request - http://openid.net/specs/openid-connect-core-1_0.html#AuthRequest
// scope
// REQUIRED. OpenID Connect requests MUST contain the "openid" scope value.
// If the openid scope value is not present, the behavior is entirely unspecified.
if (authorizationCodeAuthentication.getAuthorizationRequest().getScope().contains("openid")) {
// The OpenID Connect implementation of AuthorizationGrantAuthenticator
// should handle OpenID Connect Authentication Requests
return null;
}
TokenResponse tokenResponse =
this.authorizationCodeTokenExchanger.exchange(authorizationCodeAuthentication);
AccessToken accessToken = new AccessToken(tokenResponse.getTokenType(),
tokenResponse.getTokenValue(), tokenResponse.getIssuedAt(),
tokenResponse.getExpiresAt(), tokenResponse.getScope());
OAuth2ClientAuthenticationToken clientAuthentication =
new OAuth2ClientAuthenticationToken(authorizationCodeAuthentication.getClientRegistration(), accessToken);
clientAuthentication.setDetails(authorizationCodeAuthentication.getDetails());
return clientAuthentication;
}
}

View File

@ -1,29 +0,0 @@
/*
* Copyright 2012-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.oauth2.client.authentication;
/**
* A strategy used for <i>&quot;authenticating&quot;</i> an <i>authorization grant</i> credential
* with the authorization server's <i>Token Endpoint</i>.
*
* @author Joe Grandja
* @since 5.0
*/
public interface AuthorizationGrantAuthenticator<T extends AuthorizationGrantAuthenticationToken> {
OAuth2ClientAuthenticationToken authenticate(T authorizationGrantAuthentication) throws OAuth2AuthenticationException;
}

View File

@ -1,59 +0,0 @@
/*
* Copyright 2012-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.oauth2.client.authentication;
import org.springframework.core.ResolvableType;
import org.springframework.util.Assert;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
/**
* An implementation of an {@link AuthorizationGrantAuthenticator} that
* simply delegates to one of the {@link AuthorizationGrantAuthenticator}'s that it composes.
*
* @author Joe Grandja
* @since 5.0
*/
public class DelegatingAuthorizationGrantAuthenticator<T extends AuthorizationGrantAuthenticationToken> implements AuthorizationGrantAuthenticator<T> {
private final Map<Class<? extends AuthorizationGrantAuthenticationToken>, List<AuthorizationGrantAuthenticator<T>>> authenticators = new HashMap<>();
public DelegatingAuthorizationGrantAuthenticator(List<AuthorizationGrantAuthenticator<T>> authenticators) {
Assert.notEmpty(authenticators, "authenticators cannot be empty");
authenticators.forEach(authenticator -> {
Class<? extends AuthorizationGrantAuthenticationToken> authenticationType =
ResolvableType.forInstance(authenticator).as(AuthorizationGrantAuthenticator.class)
.resolveGeneric(0).asSubclass(AuthorizationGrantAuthenticationToken.class);
this.authenticators
.computeIfAbsent(authenticationType, k -> new LinkedList<>())
.add(authenticator);
});
}
@Override
public OAuth2ClientAuthenticationToken authenticate(T authorizationGrantAuthentication) throws OAuth2AuthenticationException {
return this.authenticators.getOrDefault(authorizationGrantAuthentication.getClass(), Collections.emptyList())
.stream()
.map(authenticator -> authenticator.authenticate(authorizationGrantAuthentication))
.filter(Objects::nonNull)
.findFirst()
.orElse(null);
}
}

View File

@ -15,16 +15,22 @@
*/
package org.springframework.security.oauth2.oidc.client.authentication;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.jwt.Jwt;
import org.springframework.security.jwt.JwtDecoder;
import org.springframework.security.oauth2.client.authentication.AuthorizationCodeAuthenticationToken;
import org.springframework.security.oauth2.client.authentication.AuthorizationGrantAuthenticator;
import org.springframework.security.oauth2.client.authentication.AuthorizationGrantTokenExchanger;
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationException;
import org.springframework.security.oauth2.client.authentication.OAuth2ClientAuthenticationToken;
import org.springframework.security.oauth2.client.authentication.jwt.JwtDecoderRegistry;
import org.springframework.security.oauth2.client.registration.ClientRegistration;
import org.springframework.security.oauth2.client.token.InMemoryAccessTokenRepository;
import org.springframework.security.oauth2.client.token.SecurityTokenRepository;
import org.springframework.security.oauth2.core.AccessToken;
import org.springframework.security.oauth2.core.OAuth2Error;
import org.springframework.security.oauth2.core.endpoint.AuthorizationRequest;
import org.springframework.security.oauth2.core.endpoint.AuthorizationResponse;
import org.springframework.security.oauth2.core.endpoint.TokenResponse;
import org.springframework.security.oauth2.oidc.core.IdToken;
import org.springframework.security.oauth2.oidc.core.OidcScope;
@ -32,22 +38,30 @@ import org.springframework.security.oauth2.oidc.core.endpoint.OidcParameter;
import org.springframework.util.Assert;
/**
* An implementation of an {@link AuthorizationGrantAuthenticator} that
* <i>&quot;authenticates&quot;</i> an <i>authorization code grant</i> credential
* against an OpenID Connect 1.0 Provider's <i>Token Endpoint</i>.
* An implementation of an {@link AuthenticationProvider}
* for the <i>OpenID Connect Core 1.0 Authorization Code Grant Flow</i>.
*
* 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.
*
* @author Joe Grandja
* @since 5.0
* @see AuthorizationGrantAuthenticator
* @see AuthorizationCodeAuthenticationToken
* @see AuthorizationGrantTokenExchanger
* @see JwtDecoderRegistry
* @see OidcClientAuthenticationToken
* @see SecurityTokenRepository
* @see <a target="_blank" href="http://openid.net/specs/openid-connect-core-1_0.html#CodeFlowAuth">Section 3.1 Authorization Code Grant Flow</a>
* @see <a target="_blank" href="http://openid.net/specs/openid-connect-core-1_0.html#TokenRequest">Section 3.1.3.1 Token Request</a>
* @see <a target="_blank" href="http://openid.net/specs/openid-connect-core-1_0.html#TokenResponse">Section 3.1.3.3 Token Response</a>
*/
public class OidcAuthorizationCodeAuthenticator implements AuthorizationGrantAuthenticator<AuthorizationCodeAuthenticationToken> {
public class OidcAuthorizationCodeAuthenticationProvider implements AuthenticationProvider {
private static final String INVALID_STATE_PARAMETER_ERROR_CODE = "invalid_state_parameter";
private static final String INVALID_REDIRECT_URI_PARAMETER_ERROR_CODE = "invalid_redirect_uri_parameter";
private final AuthorizationGrantTokenExchanger<AuthorizationCodeAuthenticationToken> authorizationCodeTokenExchanger;
private final JwtDecoderRegistry jwtDecoderRegistry;
private SecurityTokenRepository<AccessToken> accessTokenRepository = new InMemoryAccessTokenRepository();
public OidcAuthorizationCodeAuthenticator(
public OidcAuthorizationCodeAuthenticationProvider(
AuthorizationGrantTokenExchanger<AuthorizationCodeAuthenticationToken> authorizationCodeTokenExchanger,
JwtDecoderRegistry jwtDecoderRegistry) {
@ -58,18 +72,36 @@ public class OidcAuthorizationCodeAuthenticator implements AuthorizationGrantAut
}
@Override
public OAuth2ClientAuthenticationToken authenticate(
AuthorizationCodeAuthenticationToken authorizationCodeAuthentication) throws OAuth2AuthenticationException {
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
AuthorizationCodeAuthenticationToken authorizationCodeAuthentication =
(AuthorizationCodeAuthenticationToken) authentication;
// Section 3.1.2.1 Authentication Request - http://openid.net/specs/openid-connect-core-1_0.html#AuthRequest
// scope
// REQUIRED. OpenID Connect requests MUST contain the "openid" scope value.
// If the openid scope value is not present, the behavior is entirely unspecified.
if (!authorizationCodeAuthentication.getAuthorizationRequest().getScope().contains(OidcScope.OPENID)) {
// Let the standard OAuth 2.0 Authorization Code AuthenticationProvider handle this
return null;
}
ClientRegistration clientRegistration = authorizationCodeAuthentication.getClientRegistration();
AuthorizationRequest authorizationRequest = authorizationCodeAuthentication.getAuthorizationRequest();
AuthorizationResponse authorizationResponse = authorizationCodeAuthentication.getAuthorizationResponse();
if (authorizationResponse.statusError()) {
throw new OAuth2AuthenticationException(
authorizationResponse.getError(), authorizationResponse.getError().toString());
}
if (!authorizationResponse.getState().equals(authorizationRequest.getState())) {
OAuth2Error oauth2Error = new OAuth2Error(INVALID_STATE_PARAMETER_ERROR_CODE);
throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());
}
if (!authorizationResponse.getRedirectUri().equals(authorizationRequest.getRedirectUri())) {
OAuth2Error oauth2Error = new OAuth2Error(INVALID_REDIRECT_URI_PARAMETER_ERROR_CODE);
throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());
}
TokenResponse tokenResponse =
this.authorizationCodeTokenExchanger.exchange(authorizationCodeAuthentication);
@ -78,6 +110,8 @@ public class OidcAuthorizationCodeAuthenticator implements AuthorizationGrantAut
tokenResponse.getTokenValue(), tokenResponse.getIssuedAt(),
tokenResponse.getExpiresAt(), tokenResponse.getScope());
ClientRegistration clientRegistration = authorizationCodeAuthentication.getClientRegistration();
if (!tokenResponse.getAdditionalParameters().containsKey(OidcParameter.ID_TOKEN)) {
throw new IllegalArgumentException(
"Missing (required) ID Token in Token Response for Client Registration: " + clientRegistration.getRegistrationId());
@ -95,6 +129,20 @@ public class OidcAuthorizationCodeAuthenticator implements AuthorizationGrantAut
new OidcClientAuthenticationToken(clientRegistration, accessToken, idToken);
clientAuthentication.setDetails(authorizationCodeAuthentication.getDetails());
this.accessTokenRepository.saveSecurityToken(
clientAuthentication.getAccessToken(),
clientAuthentication.getClientRegistration());
return clientAuthentication;
}
public final void setAccessTokenRepository(SecurityTokenRepository<AccessToken> accessTokenRepository) {
Assert.notNull(accessTokenRepository, "accessTokenRepository cannot be null");
this.accessTokenRepository = accessTokenRepository;
}
@Override
public boolean supports(Class<?> authentication) {
return AuthorizationCodeAuthenticationToken.class.isAssignableFrom(authentication);
}
}