2
0
Эх сурвалжийг харах

Remove AuthorizationGrantAuthenticator

Joe Grandja 7 жил өмнө
parent
commit
a7d054c9f3

+ 14 - 32
config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/AuthorizationCodeGrantConfigurer.java

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

+ 1 - 10
config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OAuth2LoginConfigurer.java

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

+ 29 - 18
oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/authentication/AuthorizationCodeAuthenticationProvider.java

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

+ 0 - 67
oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/authentication/AuthorizationCodeAuthenticator.java

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

+ 0 - 29
oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/authentication/AuthorizationGrantAuthenticator.java

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

+ 0 - 59
oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/authentication/DelegatingAuthorizationGrantAuthenticator.java

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

+ 61 - 13
oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/oidc/client/authentication/OidcAuthorizationCodeAuthenticator.java → oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/oidc/client/authentication/OidcAuthorizationCodeAuthenticationProvider.java

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