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.config.annotation.web.configurers.AbstractHttpConfigurer;
|
||||||
import org.springframework.security.oauth2.client.authentication.AuthorizationCodeAuthenticationProvider;
|
import org.springframework.security.oauth2.client.authentication.AuthorizationCodeAuthenticationProvider;
|
||||||
import org.springframework.security.oauth2.client.authentication.AuthorizationCodeAuthenticationToken;
|
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.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.JwtDecoderRegistry;
|
||||||
import org.springframework.security.oauth2.client.authentication.jwt.NimbusJwtDecoderRegistry;
|
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.registration.ClientRegistrationRepository;
|
||||||
import org.springframework.security.oauth2.client.token.SecurityTokenRepository;
|
import org.springframework.security.oauth2.client.token.SecurityTokenRepository;
|
||||||
import org.springframework.security.oauth2.client.web.AuthorizationCodeAuthenticationFilter;
|
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.AuthorizationRequestRepository;
|
||||||
import org.springframework.security.oauth2.client.web.AuthorizationRequestUriBuilder;
|
import org.springframework.security.oauth2.client.web.AuthorizationRequestUriBuilder;
|
||||||
import org.springframework.security.oauth2.core.AccessToken;
|
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.security.web.authentication.session.SessionAuthenticationStrategy;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A security configurer for the Authorization Code Grant type.
|
* A security configurer for the Authorization Code Grant type.
|
||||||
*
|
*
|
||||||
|
@ -60,7 +54,6 @@ public class AuthorizationCodeGrantConfigurer<B extends HttpSecurityBuilder<B>>
|
||||||
// ***** Authorization Response members
|
// ***** Authorization Response members
|
||||||
private AuthorizationCodeAuthenticationFilter authorizationResponseFilter;
|
private AuthorizationCodeAuthenticationFilter authorizationResponseFilter;
|
||||||
private String authorizationResponseBaseUri;
|
private String authorizationResponseBaseUri;
|
||||||
private AuthorizationGrantAuthenticator<AuthorizationCodeAuthenticationToken> authorizationCodeAuthenticator;
|
|
||||||
private AuthorizationGrantTokenExchanger<AuthorizationCodeAuthenticationToken> authorizationCodeTokenExchanger;
|
private AuthorizationGrantTokenExchanger<AuthorizationCodeAuthenticationToken> authorizationCodeTokenExchanger;
|
||||||
private SecurityTokenRepository<AccessToken> accessTokenRepository;
|
private SecurityTokenRepository<AccessToken> accessTokenRepository;
|
||||||
private JwtDecoderRegistry jwtDecoderRegistry;
|
private JwtDecoderRegistry jwtDecoderRegistry;
|
||||||
|
@ -89,14 +82,6 @@ public class AuthorizationCodeGrantConfigurer<B extends HttpSecurityBuilder<B>>
|
||||||
return this;
|
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(
|
public AuthorizationCodeGrantConfigurer<B> authorizationCodeTokenExchanger(
|
||||||
AuthorizationGrantTokenExchanger<AuthorizationCodeAuthenticationToken> authorizationCodeTokenExchanger) {
|
AuthorizationGrantTokenExchanger<AuthorizationCodeAuthenticationToken> authorizationCodeTokenExchanger) {
|
||||||
|
|
||||||
|
@ -125,12 +110,20 @@ public class AuthorizationCodeGrantConfigurer<B extends HttpSecurityBuilder<B>>
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final void init(B http) throws Exception {
|
public final void init(B http) throws Exception {
|
||||||
AuthorizationCodeAuthenticationProvider authorizationCodeAuthenticationProvider =
|
AuthorizationCodeAuthenticationProvider oauth2AuthorizationCodeAuthenticationProvider =
|
||||||
new AuthorizationCodeAuthenticationProvider(this.getAuthorizationCodeAuthenticator());
|
new AuthorizationCodeAuthenticationProvider(this.getAuthorizationCodeTokenExchanger());
|
||||||
if (this.accessTokenRepository != null) {
|
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.authorizationRequestFilter = new AuthorizationRequestRedirectFilter(
|
||||||
this.getAuthorizationRequestBaseUri(), this.getClientRegistrationRepository());
|
this.getAuthorizationRequestBaseUri(), this.getClientRegistrationRepository());
|
||||||
|
@ -180,17 +173,6 @@ public class AuthorizationCodeGrantConfigurer<B extends HttpSecurityBuilder<B>>
|
||||||
return this.authorizationRequestRepository;
|
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() {
|
private AuthorizationGrantTokenExchanger<AuthorizationCodeAuthenticationToken> getAuthorizationCodeTokenExchanger() {
|
||||||
if (this.authorizationCodeTokenExchanger == null) {
|
if (this.authorizationCodeTokenExchanger == null) {
|
||||||
this.authorizationCodeTokenExchanger = new NimbusAuthorizationCodeTokenExchanger();
|
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.config.annotation.web.configurers.AbstractAuthenticationFilterConfigurer;
|
||||||
import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;
|
import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;
|
||||||
import org.springframework.security.oauth2.client.authentication.AuthorizationCodeAuthenticationToken;
|
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.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.CustomUserTypesOAuth2UserService;
|
||||||
import org.springframework.security.oauth2.client.authentication.userinfo.DefaultOAuth2UserService;
|
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.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.authentication.userinfo.OAuth2UserService;
|
||||||
import org.springframework.security.oauth2.client.registration.ClientRegistration;
|
import org.springframework.security.oauth2.client.registration.ClientRegistration;
|
||||||
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
|
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
|
||||||
|
@ -126,14 +125,6 @@ public final class OAuth2LoginConfigurer<B extends HttpSecurityBuilder<B>> exten
|
||||||
private TokenEndpointConfig() {
|
private TokenEndpointConfig() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public TokenEndpointConfig authorizationCodeAuthenticator(
|
|
||||||
AuthorizationGrantAuthenticator<AuthorizationCodeAuthenticationToken> authorizationCodeAuthenticator) {
|
|
||||||
|
|
||||||
Assert.notNull(authorizationCodeAuthenticator, "authorizationCodeAuthenticator cannot be null");
|
|
||||||
authorizationCodeGrantConfigurer.authorizationCodeAuthenticator(authorizationCodeAuthenticator);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public TokenEndpointConfig authorizationCodeTokenExchanger(
|
public TokenEndpointConfig authorizationCodeTokenExchanger(
|
||||||
AuthorizationGrantTokenExchanger<AuthorizationCodeAuthenticationToken> 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.OAuth2Error;
|
||||||
import org.springframework.security.oauth2.core.endpoint.AuthorizationRequest;
|
import org.springframework.security.oauth2.core.endpoint.AuthorizationRequest;
|
||||||
import org.springframework.security.oauth2.core.endpoint.AuthorizationResponse;
|
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;
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An implementation of an {@link AuthenticationProvider} that is responsible for authenticating
|
* An implementation of an {@link AuthenticationProvider}
|
||||||
* an <i>authorization code</i> credential with the authorization server's <i>Token Endpoint</i>
|
* for the <i>OAuth 2.0 Authorization Code Grant Flow</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).
|
|
||||||
*
|
*
|
||||||
* <p>
|
* This {@link AuthenticationProvider} is responsible for authenticating
|
||||||
* The {@link AuthorizationCodeAuthenticationProvider} uses an {@link AuthorizationGrantAuthenticator}
|
* an <i>authorization code</i> credential with the authorization server's <i>Token Endpoint</i>
|
||||||
* to authenticate the <i>authorization code</i> credential and ultimately
|
* and if valid, exchanging it for an <i>access token</i> credential.
|
||||||
* return an <i>"Authorized Client"</i> as an {@link OAuth2ClientAuthenticationToken}.
|
|
||||||
*
|
*
|
||||||
* @author Joe Grandja
|
* @author Joe Grandja
|
||||||
* @since 5.0
|
* @since 5.0
|
||||||
* @see AuthorizationCodeAuthenticationToken
|
* @see AuthorizationCodeAuthenticationToken
|
||||||
* @see OAuth2ClientAuthenticationToken
|
* @see OAuth2ClientAuthenticationToken
|
||||||
* @see OidcClientAuthenticationToken
|
|
||||||
* @see AuthorizationGrantAuthenticator
|
|
||||||
* @see SecurityTokenRepository
|
* @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="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.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="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 {
|
public class AuthorizationCodeAuthenticationProvider implements AuthenticationProvider {
|
||||||
private static final String INVALID_STATE_PARAMETER_ERROR_CODE = "invalid_state_parameter";
|
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 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();
|
private SecurityTokenRepository<AccessToken> accessTokenRepository = new InMemoryAccessTokenRepository();
|
||||||
|
|
||||||
public AuthorizationCodeAuthenticationProvider(
|
public AuthorizationCodeAuthenticationProvider(
|
||||||
AuthorizationGrantAuthenticator<AuthorizationCodeAuthenticationToken> authorizationCodeAuthenticator) {
|
AuthorizationGrantTokenExchanger<AuthorizationCodeAuthenticationToken> authorizationCodeTokenExchanger) {
|
||||||
|
|
||||||
Assert.notNull(authorizationCodeAuthenticator, "authorizationCodeAuthenticator cannot be null");
|
Assert.notNull(authorizationCodeTokenExchanger, "authorizationCodeTokenExchanger cannot be null");
|
||||||
this.authorizationCodeAuthenticator = authorizationCodeAuthenticator;
|
this.authorizationCodeTokenExchanger = authorizationCodeTokenExchanger;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -69,6 +62,16 @@ public class AuthorizationCodeAuthenticationProvider implements AuthenticationPr
|
||||||
AuthorizationCodeAuthenticationToken authorizationCodeAuthentication =
|
AuthorizationCodeAuthenticationToken authorizationCodeAuthentication =
|
||||||
(AuthorizationCodeAuthenticationToken) authentication;
|
(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();
|
AuthorizationRequest authorizationRequest = authorizationCodeAuthentication.getAuthorizationRequest();
|
||||||
AuthorizationResponse authorizationResponse = authorizationCodeAuthentication.getAuthorizationResponse();
|
AuthorizationResponse authorizationResponse = authorizationCodeAuthentication.getAuthorizationResponse();
|
||||||
|
|
||||||
|
@ -87,8 +90,16 @@ public class AuthorizationCodeAuthenticationProvider implements AuthenticationPr
|
||||||
throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());
|
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 =
|
OAuth2ClientAuthenticationToken clientAuthentication =
|
||||||
this.authorizationCodeAuthenticator.authenticate(authorizationCodeAuthentication);
|
new OAuth2ClientAuthenticationToken(authorizationCodeAuthentication.getClientRegistration(), accessToken);
|
||||||
|
clientAuthentication.setDetails(authorizationCodeAuthentication.getDetails());
|
||||||
|
|
||||||
this.accessTokenRepository.saveSecurityToken(
|
this.accessTokenRepository.saveSecurityToken(
|
||||||
clientAuthentication.getAccessToken(),
|
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;
|
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.Jwt;
|
||||||
import org.springframework.security.jwt.JwtDecoder;
|
import org.springframework.security.jwt.JwtDecoder;
|
||||||
import org.springframework.security.oauth2.client.authentication.AuthorizationCodeAuthenticationToken;
|
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.AuthorizationGrantTokenExchanger;
|
||||||
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationException;
|
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.authentication.jwt.JwtDecoderRegistry;
|
||||||
import org.springframework.security.oauth2.client.registration.ClientRegistration;
|
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.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.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.OidcScope;
|
||||||
|
@ -32,22 +38,30 @@ import org.springframework.security.oauth2.oidc.core.endpoint.OidcParameter;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An implementation of an {@link AuthorizationGrantAuthenticator} that
|
* An implementation of an {@link AuthenticationProvider}
|
||||||
* <i>"authenticates"</i> an <i>authorization code grant</i> credential
|
* for the <i>OpenID Connect Core 1.0 Authorization Code Grant Flow</i>.
|
||||||
* against an OpenID Connect 1.0 Provider's <i>Token Endpoint</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
|
* @author Joe Grandja
|
||||||
* @since 5.0
|
* @since 5.0
|
||||||
* @see AuthorizationGrantAuthenticator
|
|
||||||
* @see AuthorizationCodeAuthenticationToken
|
* @see AuthorizationCodeAuthenticationToken
|
||||||
* @see AuthorizationGrantTokenExchanger
|
* @see OidcClientAuthenticationToken
|
||||||
* @see JwtDecoderRegistry
|
* @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 AuthorizationGrantTokenExchanger<AuthorizationCodeAuthenticationToken> authorizationCodeTokenExchanger;
|
||||||
private final JwtDecoderRegistry jwtDecoderRegistry;
|
private final JwtDecoderRegistry jwtDecoderRegistry;
|
||||||
|
private SecurityTokenRepository<AccessToken> accessTokenRepository = new InMemoryAccessTokenRepository();
|
||||||
|
|
||||||
public OidcAuthorizationCodeAuthenticator(
|
public OidcAuthorizationCodeAuthenticationProvider(
|
||||||
AuthorizationGrantTokenExchanger<AuthorizationCodeAuthenticationToken> authorizationCodeTokenExchanger,
|
AuthorizationGrantTokenExchanger<AuthorizationCodeAuthenticationToken> authorizationCodeTokenExchanger,
|
||||||
JwtDecoderRegistry jwtDecoderRegistry) {
|
JwtDecoderRegistry jwtDecoderRegistry) {
|
||||||
|
|
||||||
|
@ -58,18 +72,36 @@ public class OidcAuthorizationCodeAuthenticator implements AuthorizationGrantAut
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public OAuth2ClientAuthenticationToken authenticate(
|
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
|
||||||
AuthorizationCodeAuthenticationToken authorizationCodeAuthentication) throws OAuth2AuthenticationException {
|
AuthorizationCodeAuthenticationToken authorizationCodeAuthentication =
|
||||||
|
(AuthorizationCodeAuthenticationToken) authentication;
|
||||||
|
|
||||||
// Section 3.1.2.1 Authentication Request - http://openid.net/specs/openid-connect-core-1_0.html#AuthRequest
|
// Section 3.1.2.1 Authentication Request - http://openid.net/specs/openid-connect-core-1_0.html#AuthRequest
|
||||||
// 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(OidcScope.OPENID)) {
|
if (!authorizationCodeAuthentication.getAuthorizationRequest().getScope().contains(OidcScope.OPENID)) {
|
||||||
|
// Let the standard OAuth 2.0 Authorization Code AuthenticationProvider handle this
|
||||||
return null;
|
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 =
|
TokenResponse tokenResponse =
|
||||||
this.authorizationCodeTokenExchanger.exchange(authorizationCodeAuthentication);
|
this.authorizationCodeTokenExchanger.exchange(authorizationCodeAuthentication);
|
||||||
|
@ -78,6 +110,8 @@ public class OidcAuthorizationCodeAuthenticator implements AuthorizationGrantAut
|
||||||
tokenResponse.getTokenValue(), tokenResponse.getIssuedAt(),
|
tokenResponse.getTokenValue(), tokenResponse.getIssuedAt(),
|
||||||
tokenResponse.getExpiresAt(), tokenResponse.getScope());
|
tokenResponse.getExpiresAt(), tokenResponse.getScope());
|
||||||
|
|
||||||
|
ClientRegistration clientRegistration = authorizationCodeAuthentication.getClientRegistration();
|
||||||
|
|
||||||
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());
|
||||||
|
@ -95,6 +129,20 @@ public class OidcAuthorizationCodeAuthenticator implements AuthorizationGrantAut
|
||||||
new OidcClientAuthenticationToken(clientRegistration, accessToken, idToken);
|
new OidcClientAuthenticationToken(clientRegistration, accessToken, idToken);
|
||||||
clientAuthentication.setDetails(authorizationCodeAuthentication.getDetails());
|
clientAuthentication.setDetails(authorizationCodeAuthentication.getDetails());
|
||||||
|
|
||||||
|
this.accessTokenRepository.saveSecurityToken(
|
||||||
|
clientAuthentication.getAccessToken(),
|
||||||
|
clientAuthentication.getClientRegistration());
|
||||||
|
|
||||||
return clientAuthentication;
|
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