Remove AuthorizationGrantAuthenticator
This commit is contained in:
parent
3c824dc44b
commit
a7d054c9f3
|
@ -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();
|
||||
|
|
|
@ -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) {
|
||||
|
||||
|
|
|
@ -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>"Authorized Client"</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(),
|
||||
|
|
|
@ -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>"authenticates"</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;
|
||||
}
|
||||
}
|
|
@ -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>"authenticating"</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;
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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>"authenticates"</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);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue