Improve configurability for AuthenticationConverter and AuthenticationProvider

Closes gh-417
This commit is contained in:
Joe Grandja
2022-09-01 17:02:14 -04:00
parent 07d69cbfb4
commit 2cc603c7e7
15 changed files with 709 additions and 197 deletions

View File

@@ -17,6 +17,7 @@ package org.springframework.security.oauth2.server.authorization.config.annotati
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import javax.servlet.http.HttpServletRequest;
@@ -32,6 +33,8 @@ import org.springframework.security.oauth2.server.authorization.authentication.O
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeRequestAuthenticationToken;
import org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;
import org.springframework.security.oauth2.server.authorization.web.OAuth2AuthorizationEndpointFilter;
import org.springframework.security.oauth2.server.authorization.web.authentication.DelegatingAuthenticationConverter;
import org.springframework.security.oauth2.server.authorization.web.authentication.OAuth2AuthorizationCodeRequestAuthenticationConverter;
import org.springframework.security.web.authentication.AuthenticationConverter;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
@@ -52,8 +55,10 @@ import org.springframework.util.StringUtils;
*/
public final class OAuth2AuthorizationEndpointConfigurer extends AbstractOAuth2Configurer {
private RequestMatcher requestMatcher;
private AuthenticationConverter authorizationRequestConverter;
private final List<AuthenticationConverter> authorizationRequestConverters = new ArrayList<>();
private Consumer<List<AuthenticationConverter>> authorizationRequestConvertersConsumer = (authorizationRequestConverters) -> {};
private final List<AuthenticationProvider> authenticationProviders = new ArrayList<>();
private Consumer<List<AuthenticationProvider>> authenticationProvidersConsumer = (authenticationProviders) -> {};
private AuthenticationSuccessHandler authorizationResponseHandler;
private AuthenticationFailureHandler errorResponseHandler;
private String consentPage;
@@ -66,14 +71,31 @@ public final class OAuth2AuthorizationEndpointConfigurer extends AbstractOAuth2C
}
/**
* Sets the {@link AuthenticationConverter} used when attempting to extract an Authorization Request (or Consent) from {@link HttpServletRequest}
* Adds an {@link AuthenticationConverter} used when attempting to extract an Authorization Request (or Consent) from {@link HttpServletRequest}
* to an instance of {@link OAuth2AuthorizationCodeRequestAuthenticationToken} used for authenticating the request.
*
* @param authorizationRequestConverter the {@link AuthenticationConverter} used when attempting to extract an Authorization Request (or Consent) from {@link HttpServletRequest}
* @param authorizationRequestConverter an {@link AuthenticationConverter} used when attempting to extract an Authorization Request (or Consent) from {@link HttpServletRequest}
* @return the {@link OAuth2AuthorizationEndpointConfigurer} for further configuration
*/
public OAuth2AuthorizationEndpointConfigurer authorizationRequestConverter(AuthenticationConverter authorizationRequestConverter) {
this.authorizationRequestConverter = authorizationRequestConverter;
Assert.notNull(authorizationRequestConverter, "authorizationRequestConverter cannot be null");
this.authorizationRequestConverters.add(authorizationRequestConverter);
return this;
}
/**
* Sets the {@code Consumer} providing access to the {@code List} of default
* and (optionally) added {@link #authorizationRequestConverter(AuthenticationConverter) AuthenticationConverter}'s
* allowing the ability to add, remove, or customize a specific {@link AuthenticationConverter}.
*
* @param authorizationRequestConvertersConsumer the {@code Consumer} providing access to the {@code List} of default and (optionally) added {@link AuthenticationConverter}'s
* @return the {@link OAuth2AuthorizationEndpointConfigurer} for further configuration
* @since 0.4.0
*/
public OAuth2AuthorizationEndpointConfigurer authorizationRequestConverters(
Consumer<List<AuthenticationConverter>> authorizationRequestConvertersConsumer) {
Assert.notNull(authorizationRequestConvertersConsumer, "authorizationRequestConvertersConsumer cannot be null");
this.authorizationRequestConvertersConsumer = authorizationRequestConvertersConsumer;
return this;
}
@@ -89,6 +111,22 @@ public final class OAuth2AuthorizationEndpointConfigurer extends AbstractOAuth2C
return this;
}
/**
* Sets the {@code Consumer} providing access to the {@code List} of default
* and (optionally) added {@link #authenticationProvider(AuthenticationProvider) AuthenticationProvider}'s
* allowing the ability to add, remove, or customize a specific {@link AuthenticationProvider}.
*
* @param authenticationProvidersConsumer the {@code Consumer} providing access to the {@code List} of default and (optionally) added {@link AuthenticationProvider}'s
* @return the {@link OAuth2AuthorizationEndpointConfigurer} for further configuration
* @since 0.4.0
*/
public OAuth2AuthorizationEndpointConfigurer authenticationProviders(
Consumer<List<AuthenticationProvider>> authenticationProvidersConsumer) {
Assert.notNull(authenticationProvidersConsumer, "authenticationProvidersConsumer cannot be null");
this.authenticationProvidersConsumer = authenticationProvidersConsumer;
return this;
}
/**
* Sets the {@link AuthenticationSuccessHandler} used for handling an {@link OAuth2AuthorizationCodeRequestAuthenticationToken}
* and returning the {@link OAuth2AuthorizationResponse Authorization Response}.
@@ -158,10 +196,11 @@ public final class OAuth2AuthorizationEndpointConfigurer extends AbstractOAuth2C
authorizationServerSettings.getAuthorizationEndpoint(),
HttpMethod.POST.name()));
List<AuthenticationProvider> authenticationProviders =
!this.authenticationProviders.isEmpty() ?
this.authenticationProviders :
createDefaultAuthenticationProviders(httpSecurity);
List<AuthenticationProvider> authenticationProviders = createDefaultAuthenticationProviders(httpSecurity);
if (!this.authenticationProviders.isEmpty()) {
authenticationProviders.addAll(0, this.authenticationProviders);
}
this.authenticationProvidersConsumer.accept(authenticationProviders);
authenticationProviders.forEach(authenticationProvider ->
httpSecurity.authenticationProvider(postProcess(authenticationProvider)));
}
@@ -175,9 +214,13 @@ public final class OAuth2AuthorizationEndpointConfigurer extends AbstractOAuth2C
new OAuth2AuthorizationEndpointFilter(
authenticationManager,
authorizationServerSettings.getAuthorizationEndpoint());
if (this.authorizationRequestConverter != null) {
authorizationEndpointFilter.setAuthenticationConverter(this.authorizationRequestConverter);
List<AuthenticationConverter> authenticationConverters = createDefaultAuthenticationConverters();
if (!this.authorizationRequestConverters.isEmpty()) {
authenticationConverters.addAll(0, this.authorizationRequestConverters);
}
this.authorizationRequestConvertersConsumer.accept(authenticationConverters);
authorizationEndpointFilter.setAuthenticationConverter(
new DelegatingAuthenticationConverter(authenticationConverters));
if (this.authorizationResponseHandler != null) {
authorizationEndpointFilter.setAuthenticationSuccessHandler(this.authorizationResponseHandler);
}
@@ -195,7 +238,15 @@ public final class OAuth2AuthorizationEndpointConfigurer extends AbstractOAuth2C
return this.requestMatcher;
}
private List<AuthenticationProvider> createDefaultAuthenticationProviders(HttpSecurity httpSecurity) {
private static List<AuthenticationConverter> createDefaultAuthenticationConverters() {
List<AuthenticationConverter> authenticationConverters = new ArrayList<>();
authenticationConverters.add(new OAuth2AuthorizationCodeRequestAuthenticationConverter());
return authenticationConverters;
}
private static List<AuthenticationProvider> createDefaultAuthenticationProviders(HttpSecurity httpSecurity) {
List<AuthenticationProvider> authenticationProviders = new ArrayList<>();
OAuth2AuthorizationCodeRequestAuthenticationProvider authorizationCodeRequestAuthenticationProvider =

View File

@@ -17,6 +17,7 @@ package org.springframework.security.oauth2.server.authorization.config.annotati
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import javax.servlet.http.HttpServletRequest;
@@ -36,6 +37,11 @@ import org.springframework.security.oauth2.server.authorization.authentication.P
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;
import org.springframework.security.oauth2.server.authorization.web.OAuth2ClientAuthenticationFilter;
import org.springframework.security.oauth2.server.authorization.web.authentication.ClientSecretBasicAuthenticationConverter;
import org.springframework.security.oauth2.server.authorization.web.authentication.ClientSecretPostAuthenticationConverter;
import org.springframework.security.oauth2.server.authorization.web.authentication.DelegatingAuthenticationConverter;
import org.springframework.security.oauth2.server.authorization.web.authentication.JwtClientAssertionAuthenticationConverter;
import org.springframework.security.oauth2.server.authorization.web.authentication.PublicClientAuthenticationConverter;
import org.springframework.security.web.authentication.AuthenticationConverter;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
@@ -55,8 +61,10 @@ import org.springframework.util.Assert;
*/
public final class OAuth2ClientAuthenticationConfigurer extends AbstractOAuth2Configurer {
private RequestMatcher requestMatcher;
private AuthenticationConverter authenticationConverter;
private final List<AuthenticationConverter> authenticationConverters = new ArrayList<>();
private Consumer<List<AuthenticationConverter>> authenticationConvertersConsumer = (authenticationConverters) -> {};
private final List<AuthenticationProvider> authenticationProviders = new ArrayList<>();
private Consumer<List<AuthenticationProvider>> authenticationProvidersConsumer = (authenticationProviders) -> {};
private AuthenticationSuccessHandler authenticationSuccessHandler;
private AuthenticationFailureHandler errorResponseHandler;
@@ -68,14 +76,31 @@ public final class OAuth2ClientAuthenticationConfigurer extends AbstractOAuth2Co
}
/**
* Sets the {@link AuthenticationConverter} used when attempting to extract client credentials from {@link HttpServletRequest}
* Adds an {@link AuthenticationConverter} used when attempting to extract client credentials from {@link HttpServletRequest}
* to an instance of {@link OAuth2ClientAuthenticationToken} used for authenticating the client.
*
* @param authenticationConverter the {@link AuthenticationConverter} used when attempting to extract client credentials from {@link HttpServletRequest}
* @param authenticationConverter an {@link AuthenticationConverter} used when attempting to extract client credentials from {@link HttpServletRequest}
* @return the {@link OAuth2ClientAuthenticationConfigurer} for further configuration
*/
public OAuth2ClientAuthenticationConfigurer authenticationConverter(AuthenticationConverter authenticationConverter) {
this.authenticationConverter = authenticationConverter;
Assert.notNull(authenticationConverter, "authenticationConverter cannot be null");
this.authenticationConverters.add(authenticationConverter);
return this;
}
/**
* Sets the {@code Consumer} providing access to the {@code List} of default
* and (optionally) added {@link #authenticationConverter(AuthenticationConverter) AuthenticationConverter}'s
* allowing the ability to add, remove, or customize a specific {@link AuthenticationConverter}.
*
* @param authenticationConvertersConsumer the {@code Consumer} providing access to the {@code List} of default and (optionally) added {@link AuthenticationConverter}'s
* @return the {@link OAuth2ClientAuthenticationConfigurer} for further configuration
* @since 0.4.0
*/
public OAuth2ClientAuthenticationConfigurer authenticationConverters(
Consumer<List<AuthenticationConverter>> authenticationConvertersConsumer) {
Assert.notNull(authenticationConvertersConsumer, "authenticationConvertersConsumer cannot be null");
this.authenticationConvertersConsumer = authenticationConvertersConsumer;
return this;
}
@@ -91,6 +116,22 @@ public final class OAuth2ClientAuthenticationConfigurer extends AbstractOAuth2Co
return this;
}
/**
* Sets the {@code Consumer} providing access to the {@code List} of default
* and (optionally) added {@link #authenticationProvider(AuthenticationProvider) AuthenticationProvider}'s
* allowing the ability to add, remove, or customize a specific {@link AuthenticationProvider}.
*
* @param authenticationProvidersConsumer the {@code Consumer} providing access to the {@code List} of default and (optionally) added {@link AuthenticationProvider}'s
* @return the {@link OAuth2ClientAuthenticationConfigurer} for further configuration
* @since 0.4.0
*/
public OAuth2ClientAuthenticationConfigurer authenticationProviders(
Consumer<List<AuthenticationProvider>> authenticationProvidersConsumer) {
Assert.notNull(authenticationProvidersConsumer, "authenticationProvidersConsumer cannot be null");
this.authenticationProvidersConsumer = authenticationProvidersConsumer;
return this;
}
/**
* Sets the {@link AuthenticationSuccessHandler} used for handling a successful client authentication
* and associating the {@link OAuth2ClientAuthenticationToken} to the {@link SecurityContext}.
@@ -129,10 +170,11 @@ public final class OAuth2ClientAuthenticationConfigurer extends AbstractOAuth2Co
authorizationServerSettings.getTokenRevocationEndpoint(),
HttpMethod.POST.name()));
List<AuthenticationProvider> authenticationProviders =
!this.authenticationProviders.isEmpty() ?
this.authenticationProviders :
createDefaultAuthenticationProviders(httpSecurity);
List<AuthenticationProvider> authenticationProviders = createDefaultAuthenticationProviders(httpSecurity);
if (!this.authenticationProviders.isEmpty()) {
authenticationProviders.addAll(0, this.authenticationProviders);
}
this.authenticationProvidersConsumer.accept(authenticationProviders);
authenticationProviders.forEach(authenticationProvider ->
httpSecurity.authenticationProvider(postProcess(authenticationProvider)));
}
@@ -142,9 +184,13 @@ public final class OAuth2ClientAuthenticationConfigurer extends AbstractOAuth2Co
AuthenticationManager authenticationManager = httpSecurity.getSharedObject(AuthenticationManager.class);
OAuth2ClientAuthenticationFilter clientAuthenticationFilter = new OAuth2ClientAuthenticationFilter(
authenticationManager, this.requestMatcher);
if (this.authenticationConverter != null) {
clientAuthenticationFilter.setAuthenticationConverter(this.authenticationConverter);
List<AuthenticationConverter> authenticationConverters = createDefaultAuthenticationConverters();
if (!this.authenticationConverters.isEmpty()) {
authenticationConverters.addAll(0, this.authenticationConverters);
}
this.authenticationConvertersConsumer.accept(authenticationConverters);
clientAuthenticationFilter.setAuthenticationConverter(
new DelegatingAuthenticationConverter(authenticationConverters));
if (this.authenticationSuccessHandler != null) {
clientAuthenticationFilter.setAuthenticationSuccessHandler(this.authenticationSuccessHandler);
}
@@ -159,7 +205,18 @@ public final class OAuth2ClientAuthenticationConfigurer extends AbstractOAuth2Co
return this.requestMatcher;
}
private List<AuthenticationProvider> createDefaultAuthenticationProviders(HttpSecurity httpSecurity) {
private static List<AuthenticationConverter> createDefaultAuthenticationConverters() {
List<AuthenticationConverter> authenticationConverters = new ArrayList<>();
authenticationConverters.add(new JwtClientAssertionAuthenticationConverter());
authenticationConverters.add(new ClientSecretBasicAuthenticationConverter());
authenticationConverters.add(new ClientSecretPostAuthenticationConverter());
authenticationConverters.add(new PublicClientAuthenticationConverter());
return authenticationConverters;
}
private static List<AuthenticationProvider> createDefaultAuthenticationProviders(HttpSecurity httpSecurity) {
List<AuthenticationProvider> authenticationProviders = new ArrayList<>();
RegisteredClientRepository registeredClientRepository = OAuth2ConfigurerUtils.getRegisteredClientRepository(httpSecurity);

View File

@@ -16,8 +16,8 @@
package org.springframework.security.oauth2.server.authorization.config.annotation.web.configurers;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.function.Consumer;
import javax.servlet.http.HttpServletRequest;
@@ -39,6 +39,10 @@ import org.springframework.security.oauth2.server.authorization.authentication.O
import org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;
import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenGenerator;
import org.springframework.security.oauth2.server.authorization.web.OAuth2TokenEndpointFilter;
import org.springframework.security.oauth2.server.authorization.web.authentication.DelegatingAuthenticationConverter;
import org.springframework.security.oauth2.server.authorization.web.authentication.OAuth2AuthorizationCodeAuthenticationConverter;
import org.springframework.security.oauth2.server.authorization.web.authentication.OAuth2ClientCredentialsAuthenticationConverter;
import org.springframework.security.oauth2.server.authorization.web.authentication.OAuth2RefreshTokenAuthenticationConverter;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import org.springframework.security.web.authentication.AuthenticationConverter;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
@@ -57,8 +61,10 @@ import org.springframework.util.Assert;
*/
public final class OAuth2TokenEndpointConfigurer extends AbstractOAuth2Configurer {
private RequestMatcher requestMatcher;
private AuthenticationConverter accessTokenRequestConverter;
private final List<AuthenticationProvider> authenticationProviders = new LinkedList<>();
private final List<AuthenticationConverter> accessTokenRequestConverters = new ArrayList<>();
private Consumer<List<AuthenticationConverter>> accessTokenRequestConvertersConsumer = (accessTokenRequestConverters) -> {};
private final List<AuthenticationProvider> authenticationProviders = new ArrayList<>();
private Consumer<List<AuthenticationProvider>> authenticationProvidersConsumer = (authenticationProviders) -> {};
private AuthenticationSuccessHandler accessTokenResponseHandler;
private AuthenticationFailureHandler errorResponseHandler;
@@ -70,14 +76,31 @@ public final class OAuth2TokenEndpointConfigurer extends AbstractOAuth2Configure
}
/**
* Sets the {@link AuthenticationConverter} used when attempting to extract an Access Token Request from {@link HttpServletRequest}
* Adds an {@link AuthenticationConverter} used when attempting to extract an Access Token Request from {@link HttpServletRequest}
* to an instance of {@link OAuth2AuthorizationGrantAuthenticationToken} used for authenticating the authorization grant.
*
* @param accessTokenRequestConverter the {@link AuthenticationConverter} used when attempting to extract an Access Token Request from {@link HttpServletRequest}
* @param accessTokenRequestConverter an {@link AuthenticationConverter} used when attempting to extract an Access Token Request from {@link HttpServletRequest}
* @return the {@link OAuth2TokenEndpointConfigurer} for further configuration
*/
public OAuth2TokenEndpointConfigurer accessTokenRequestConverter(AuthenticationConverter accessTokenRequestConverter) {
this.accessTokenRequestConverter = accessTokenRequestConverter;
Assert.notNull(accessTokenRequestConverter, "accessTokenRequestConverter cannot be null");
this.accessTokenRequestConverters.add(accessTokenRequestConverter);
return this;
}
/**
* Sets the {@code Consumer} providing access to the {@code List} of default
* and (optionally) added {@link #accessTokenRequestConverter(AuthenticationConverter) AuthenticationConverter}'s
* allowing the ability to add, remove, or customize a specific {@link AuthenticationConverter}.
*
* @param accessTokenRequestConvertersConsumer the {@code Consumer} providing access to the {@code List} of default and (optionally) added {@link AuthenticationConverter}'s
* @return the {@link OAuth2TokenEndpointConfigurer} for further configuration
* @since 0.4.0
*/
public OAuth2TokenEndpointConfigurer accessTokenRequestConverters(
Consumer<List<AuthenticationConverter>> accessTokenRequestConvertersConsumer) {
Assert.notNull(accessTokenRequestConvertersConsumer, "accessTokenRequestConvertersConsumer cannot be null");
this.accessTokenRequestConvertersConsumer = accessTokenRequestConvertersConsumer;
return this;
}
@@ -93,6 +116,22 @@ public final class OAuth2TokenEndpointConfigurer extends AbstractOAuth2Configure
return this;
}
/**
* Sets the {@code Consumer} providing access to the {@code List} of default
* and (optionally) added {@link #authenticationProvider(AuthenticationProvider) AuthenticationProvider}'s
* allowing the ability to add, remove, or customize a specific {@link AuthenticationProvider}.
*
* @param authenticationProvidersConsumer the {@code Consumer} providing access to the {@code List} of default and (optionally) added {@link AuthenticationProvider}'s
* @return the {@link OAuth2TokenEndpointConfigurer} for further configuration
* @since 0.4.0
*/
public OAuth2TokenEndpointConfigurer authenticationProviders(
Consumer<List<AuthenticationProvider>> authenticationProvidersConsumer) {
Assert.notNull(authenticationProvidersConsumer, "authenticationProvidersConsumer cannot be null");
this.authenticationProvidersConsumer = authenticationProvidersConsumer;
return this;
}
/**
* Sets the {@link AuthenticationSuccessHandler} used for handling an {@link OAuth2AccessTokenAuthenticationToken}
* and returning the {@link OAuth2AccessTokenResponse Access Token Response}.
@@ -123,10 +162,11 @@ public final class OAuth2TokenEndpointConfigurer extends AbstractOAuth2Configure
this.requestMatcher = new AntPathRequestMatcher(
authorizationServerSettings.getTokenEndpoint(), HttpMethod.POST.name());
List<AuthenticationProvider> authenticationProviders =
!this.authenticationProviders.isEmpty() ?
this.authenticationProviders :
createDefaultAuthenticationProviders(httpSecurity);
List<AuthenticationProvider> authenticationProviders = createDefaultAuthenticationProviders(httpSecurity);
if (!this.authenticationProviders.isEmpty()) {
authenticationProviders.addAll(0, this.authenticationProviders);
}
this.authenticationProvidersConsumer.accept(authenticationProviders);
authenticationProviders.forEach(authenticationProvider ->
httpSecurity.authenticationProvider(postProcess(authenticationProvider)));
}
@@ -140,9 +180,13 @@ public final class OAuth2TokenEndpointConfigurer extends AbstractOAuth2Configure
new OAuth2TokenEndpointFilter(
authenticationManager,
authorizationServerSettings.getTokenEndpoint());
if (this.accessTokenRequestConverter != null) {
tokenEndpointFilter.setAuthenticationConverter(this.accessTokenRequestConverter);
List<AuthenticationConverter> authenticationConverters = createDefaultAuthenticationConverters();
if (!this.accessTokenRequestConverters.isEmpty()) {
authenticationConverters.addAll(0, this.accessTokenRequestConverters);
}
this.accessTokenRequestConvertersConsumer.accept(authenticationConverters);
tokenEndpointFilter.setAuthenticationConverter(
new DelegatingAuthenticationConverter(authenticationConverters));
if (this.accessTokenResponseHandler != null) {
tokenEndpointFilter.setAuthenticationSuccessHandler(this.accessTokenResponseHandler);
}
@@ -157,7 +201,17 @@ public final class OAuth2TokenEndpointConfigurer extends AbstractOAuth2Configure
return this.requestMatcher;
}
private List<AuthenticationProvider> createDefaultAuthenticationProviders(HttpSecurity httpSecurity) {
private static List<AuthenticationConverter> createDefaultAuthenticationConverters() {
List<AuthenticationConverter> authenticationConverters = new ArrayList<>();
authenticationConverters.add(new OAuth2AuthorizationCodeAuthenticationConverter());
authenticationConverters.add(new OAuth2RefreshTokenAuthenticationConverter());
authenticationConverters.add(new OAuth2ClientCredentialsAuthenticationConverter());
return authenticationConverters;
}
private static List<AuthenticationProvider> createDefaultAuthenticationProviders(HttpSecurity httpSecurity) {
List<AuthenticationProvider> authenticationProviders = new ArrayList<>();
OAuth2AuthorizationService authorizationService = OAuth2ConfigurerUtils.getAuthorizationService(httpSecurity);

View File

@@ -16,8 +16,8 @@
package org.springframework.security.oauth2.server.authorization.config.annotation.web.configurers;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.function.Consumer;
import javax.servlet.http.HttpServletRequest;
@@ -33,6 +33,8 @@ import org.springframework.security.oauth2.server.authorization.authentication.O
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2TokenIntrospectionAuthenticationToken;
import org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;
import org.springframework.security.oauth2.server.authorization.web.OAuth2TokenIntrospectionEndpointFilter;
import org.springframework.security.oauth2.server.authorization.web.authentication.DelegatingAuthenticationConverter;
import org.springframework.security.oauth2.server.authorization.web.authentication.OAuth2TokenIntrospectionAuthenticationConverter;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import org.springframework.security.web.authentication.AuthenticationConverter;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
@@ -45,14 +47,17 @@ import org.springframework.util.Assert;
* Configurer for the OAuth 2.0 Token Introspection Endpoint.
*
* @author Gaurav Tiwari
* @author Joe Grandja
* @since 0.2.3
* @see OAuth2AuthorizationServerConfigurer#tokenIntrospectionEndpoint(Customizer)
* @see OAuth2TokenIntrospectionEndpointFilter
*/
public final class OAuth2TokenIntrospectionEndpointConfigurer extends AbstractOAuth2Configurer {
private RequestMatcher requestMatcher;
private AuthenticationConverter introspectionRequestConverter;
private final List<AuthenticationProvider> authenticationProviders = new LinkedList<>();
private final List<AuthenticationConverter> introspectionRequestConverters = new ArrayList<>();
private Consumer<List<AuthenticationConverter>> introspectionRequestConvertersConsumer = (introspectionRequestConverters) -> {};
private final List<AuthenticationProvider> authenticationProviders = new ArrayList<>();
private Consumer<List<AuthenticationProvider>> authenticationProvidersConsumer = (authenticationProviders) -> {};
private AuthenticationSuccessHandler introspectionResponseHandler;
private AuthenticationFailureHandler errorResponseHandler;
@@ -64,14 +69,31 @@ public final class OAuth2TokenIntrospectionEndpointConfigurer extends AbstractOA
}
/**
* Sets the {@link AuthenticationConverter} used when attempting to extract an Introspection Request from {@link HttpServletRequest}
* Adds an {@link AuthenticationConverter} used when attempting to extract an Introspection Request from {@link HttpServletRequest}
* to an instance of {@link OAuth2TokenIntrospectionAuthenticationToken} used for authenticating the request.
*
* @param introspectionRequestConverter the {@link AuthenticationConverter} used when attempting to extract an Introspection Request from {@link HttpServletRequest}
* @param introspectionRequestConverter an {@link AuthenticationConverter} used when attempting to extract an Introspection Request from {@link HttpServletRequest}
* @return the {@link OAuth2TokenIntrospectionEndpointConfigurer} for further configuration
*/
public OAuth2TokenIntrospectionEndpointConfigurer introspectionRequestConverter(AuthenticationConverter introspectionRequestConverter) {
this.introspectionRequestConverter = introspectionRequestConverter;
Assert.notNull(introspectionRequestConverter, "introspectionRequestConverter cannot be null");
this.introspectionRequestConverters.add(introspectionRequestConverter);
return this;
}
/**
* Sets the {@code Consumer} providing access to the {@code List} of default
* and (optionally) added {@link #introspectionRequestConverter(AuthenticationConverter) AuthenticationConverter}'s
* allowing the ability to add, remove, or customize a specific {@link AuthenticationConverter}.
*
* @param introspectionRequestConvertersConsumer the {@code Consumer} providing access to the {@code List} of default and (optionally) added {@link AuthenticationConverter}'s
* @return the {@link OAuth2TokenIntrospectionEndpointConfigurer} for further configuration
* @since 0.4.0
*/
public OAuth2TokenIntrospectionEndpointConfigurer introspectionRequestConverters(
Consumer<List<AuthenticationConverter>> introspectionRequestConvertersConsumer) {
Assert.notNull(introspectionRequestConvertersConsumer, "introspectionRequestConvertersConsumer cannot be null");
this.introspectionRequestConvertersConsumer = introspectionRequestConvertersConsumer;
return this;
}
@@ -87,6 +109,22 @@ public final class OAuth2TokenIntrospectionEndpointConfigurer extends AbstractOA
return this;
}
/**
* Sets the {@code Consumer} providing access to the {@code List} of default
* and (optionally) added {@link #authenticationProvider(AuthenticationProvider) AuthenticationProvider}'s
* allowing the ability to add, remove, or customize a specific {@link AuthenticationProvider}.
*
* @param authenticationProvidersConsumer the {@code Consumer} providing access to the {@code List} of default and (optionally) added {@link AuthenticationProvider}'s
* @return the {@link OAuth2TokenIntrospectionEndpointConfigurer} for further configuration
* @since 0.4.0
*/
public OAuth2TokenIntrospectionEndpointConfigurer authenticationProviders(
Consumer<List<AuthenticationProvider>> authenticationProvidersConsumer) {
Assert.notNull(authenticationProvidersConsumer, "authenticationProvidersConsumer cannot be null");
this.authenticationProvidersConsumer = authenticationProvidersConsumer;
return this;
}
/**
* Sets the {@link AuthenticationSuccessHandler} used for handling an {@link OAuth2TokenIntrospectionAuthenticationToken}.
*
@@ -116,10 +154,11 @@ public final class OAuth2TokenIntrospectionEndpointConfigurer extends AbstractOA
this.requestMatcher = new AntPathRequestMatcher(
authorizationServerSettings.getTokenIntrospectionEndpoint(), HttpMethod.POST.name());
List<AuthenticationProvider> authenticationProviders =
!this.authenticationProviders.isEmpty() ?
this.authenticationProviders :
createDefaultAuthenticationProviders(httpSecurity);
List<AuthenticationProvider> authenticationProviders = createDefaultAuthenticationProviders(httpSecurity);
if (!this.authenticationProviders.isEmpty()) {
authenticationProviders.addAll(0, this.authenticationProviders);
}
this.authenticationProvidersConsumer.accept(authenticationProviders);
authenticationProviders.forEach(authenticationProvider ->
httpSecurity.authenticationProvider(postProcess(authenticationProvider)));
}
@@ -132,9 +171,13 @@ public final class OAuth2TokenIntrospectionEndpointConfigurer extends AbstractOA
OAuth2TokenIntrospectionEndpointFilter introspectionEndpointFilter =
new OAuth2TokenIntrospectionEndpointFilter(
authenticationManager, authorizationServerSettings.getTokenIntrospectionEndpoint());
if (this.introspectionRequestConverter != null) {
introspectionEndpointFilter.setAuthenticationConverter(this.introspectionRequestConverter);
List<AuthenticationConverter> authenticationConverters = createDefaultAuthenticationConverters();
if (!this.introspectionRequestConverters.isEmpty()) {
authenticationConverters.addAll(0, this.introspectionRequestConverters);
}
this.introspectionRequestConvertersConsumer.accept(authenticationConverters);
introspectionEndpointFilter.setAuthenticationConverter(
new DelegatingAuthenticationConverter(authenticationConverters));
if (this.introspectionResponseHandler != null) {
introspectionEndpointFilter.setAuthenticationSuccessHandler(this.introspectionResponseHandler);
}
@@ -149,7 +192,15 @@ public final class OAuth2TokenIntrospectionEndpointConfigurer extends AbstractOA
return this.requestMatcher;
}
private List<AuthenticationProvider> createDefaultAuthenticationProviders(HttpSecurity httpSecurity) {
private static List<AuthenticationConverter> createDefaultAuthenticationConverters() {
List<AuthenticationConverter> authenticationConverters = new ArrayList<>();
authenticationConverters.add(new OAuth2TokenIntrospectionAuthenticationConverter());
return authenticationConverters;
}
private static List<AuthenticationProvider> createDefaultAuthenticationProviders(HttpSecurity httpSecurity) {
List<AuthenticationProvider> authenticationProviders = new ArrayList<>();
OAuth2TokenIntrospectionAuthenticationProvider tokenIntrospectionAuthenticationProvider =

View File

@@ -16,8 +16,8 @@
package org.springframework.security.oauth2.server.authorization.config.annotation.web.configurers;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.function.Consumer;
import javax.servlet.http.HttpServletRequest;
@@ -32,6 +32,8 @@ import org.springframework.security.oauth2.server.authorization.authentication.O
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2TokenRevocationAuthenticationToken;
import org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;
import org.springframework.security.oauth2.server.authorization.web.OAuth2TokenRevocationEndpointFilter;
import org.springframework.security.oauth2.server.authorization.web.authentication.DelegatingAuthenticationConverter;
import org.springframework.security.oauth2.server.authorization.web.authentication.OAuth2TokenRevocationAuthenticationConverter;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import org.springframework.security.web.authentication.AuthenticationConverter;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
@@ -44,14 +46,17 @@ import org.springframework.util.Assert;
* Configurer for the OAuth 2.0 Token Revocation Endpoint.
*
* @author Arfat Chaus
* @author Joe Grandja
* @since 0.2.2
* @see OAuth2AuthorizationServerConfigurer#tokenRevocationEndpoint
* @see OAuth2TokenRevocationEndpointFilter
*/
public final class OAuth2TokenRevocationEndpointConfigurer extends AbstractOAuth2Configurer {
private RequestMatcher requestMatcher;
private AuthenticationConverter revocationRequestConverter;
private final List<AuthenticationProvider> authenticationProviders = new LinkedList<>();
private final List<AuthenticationConverter> revocationRequestConverters = new ArrayList<>();
private Consumer<List<AuthenticationConverter>> revocationRequestConvertersConsumer = (revocationRequestConverters) -> {};
private final List<AuthenticationProvider> authenticationProviders = new ArrayList<>();
private Consumer<List<AuthenticationProvider>> authenticationProvidersConsumer = (authenticationProviders) -> {};
private AuthenticationSuccessHandler revocationResponseHandler;
private AuthenticationFailureHandler errorResponseHandler;
@@ -63,14 +68,31 @@ public final class OAuth2TokenRevocationEndpointConfigurer extends AbstractOAuth
}
/**
* Sets the {@link AuthenticationConverter} used when attempting to extract a Revoke Token Request from {@link HttpServletRequest}
* to an instance of {@link OAuth2TokenRevocationAuthenticationToken} used for authenticating the client.
* Adds an {@link AuthenticationConverter} used when attempting to extract a Revoke Token Request from {@link HttpServletRequest}
* to an instance of {@link OAuth2TokenRevocationAuthenticationToken} used for authenticating the request.
*
* @param revocationRequestConverter the {@link AuthenticationConverter} used when attempting to extract client credentials from {@link HttpServletRequest}
* @param revocationRequestConverter an {@link AuthenticationConverter} used when attempting to extract a Revoke Token Request from {@link HttpServletRequest}
* @return the {@link OAuth2TokenRevocationEndpointConfigurer} for further configuration
*/
public OAuth2TokenRevocationEndpointConfigurer revocationRequestConverter(AuthenticationConverter revocationRequestConverter) {
this.revocationRequestConverter = revocationRequestConverter;
Assert.notNull(revocationRequestConverter, "revocationRequestConverter cannot be null");
this.revocationRequestConverters.add(revocationRequestConverter);
return this;
}
/**
* Sets the {@code Consumer} providing access to the {@code List} of default
* and (optionally) added {@link #revocationRequestConverter(AuthenticationConverter) AuthenticationConverter}'s
* allowing the ability to add, remove, or customize a specific {@link AuthenticationConverter}.
*
* @param revocationRequestConvertersConsumer the {@code Consumer} providing access to the {@code List} of default and (optionally) added {@link AuthenticationConverter}'s
* @return the {@link OAuth2TokenRevocationEndpointConfigurer} for further configuration
* @since 0.4.0
*/
public OAuth2TokenRevocationEndpointConfigurer revocationRequestConverters(
Consumer<List<AuthenticationConverter>> revocationRequestConvertersConsumer) {
Assert.notNull(revocationRequestConvertersConsumer, "revocationRequestConvertersConsumer cannot be null");
this.revocationRequestConvertersConsumer = revocationRequestConvertersConsumer;
return this;
}
@@ -86,6 +108,22 @@ public final class OAuth2TokenRevocationEndpointConfigurer extends AbstractOAuth
return this;
}
/**
* Sets the {@code Consumer} providing access to the {@code List} of default
* and (optionally) added {@link #authenticationProvider(AuthenticationProvider) AuthenticationProvider}'s
* allowing the ability to add, remove, or customize a specific {@link AuthenticationProvider}.
*
* @param authenticationProvidersConsumer the {@code Consumer} providing access to the {@code List} of default and (optionally) added {@link AuthenticationProvider}'s
* @return the {@link OAuth2TokenRevocationEndpointConfigurer} for further configuration
* @since 0.4.0
*/
public OAuth2TokenRevocationEndpointConfigurer authenticationProviders(
Consumer<List<AuthenticationProvider>> authenticationProvidersConsumer) {
Assert.notNull(authenticationProvidersConsumer, "authenticationProvidersConsumer cannot be null");
this.authenticationProvidersConsumer = authenticationProvidersConsumer;
return this;
}
/**
* Sets the {@link AuthenticationSuccessHandler} used for handling an {@link OAuth2TokenRevocationAuthenticationToken}.
*
@@ -115,10 +153,11 @@ public final class OAuth2TokenRevocationEndpointConfigurer extends AbstractOAuth
this.requestMatcher = new AntPathRequestMatcher(
authorizationServerSettings.getTokenRevocationEndpoint(), HttpMethod.POST.name());
List<AuthenticationProvider> authenticationProviders =
!this.authenticationProviders.isEmpty() ?
this.authenticationProviders :
createDefaultAuthenticationProviders(httpSecurity);
List<AuthenticationProvider> authenticationProviders = createDefaultAuthenticationProviders(httpSecurity);
if (!this.authenticationProviders.isEmpty()) {
authenticationProviders.addAll(0, this.authenticationProviders);
}
this.authenticationProvidersConsumer.accept(authenticationProviders);
authenticationProviders.forEach(authenticationProvider ->
httpSecurity.authenticationProvider(postProcess(authenticationProvider)));
}
@@ -131,9 +170,13 @@ public final class OAuth2TokenRevocationEndpointConfigurer extends AbstractOAuth
OAuth2TokenRevocationEndpointFilter revocationEndpointFilter =
new OAuth2TokenRevocationEndpointFilter(
authenticationManager, authorizationServerSettings.getTokenRevocationEndpoint());
if (this.revocationRequestConverter != null) {
revocationEndpointFilter.setAuthenticationConverter(this.revocationRequestConverter);
List<AuthenticationConverter> authenticationConverters = createDefaultAuthenticationConverters();
if (!this.revocationRequestConverters.isEmpty()) {
authenticationConverters.addAll(0, this.revocationRequestConverters);
}
this.revocationRequestConvertersConsumer.accept(authenticationConverters);
revocationEndpointFilter.setAuthenticationConverter(
new DelegatingAuthenticationConverter(authenticationConverters));
if (this.revocationResponseHandler != null) {
revocationEndpointFilter.setAuthenticationSuccessHandler(this.revocationResponseHandler);
}
@@ -148,7 +191,15 @@ public final class OAuth2TokenRevocationEndpointConfigurer extends AbstractOAuth
return this.requestMatcher;
}
private List<AuthenticationProvider> createDefaultAuthenticationProviders(HttpSecurity httpSecurity) {
private static List<AuthenticationConverter> createDefaultAuthenticationConverters() {
List<AuthenticationConverter> authenticationConverters = new ArrayList<>();
authenticationConverters.add(new OAuth2TokenRevocationAuthenticationConverter());
return authenticationConverters;
}
private static List<AuthenticationProvider> createDefaultAuthenticationProviders(HttpSecurity httpSecurity) {
List<AuthenticationProvider> authenticationProviders = new ArrayList<>();
OAuth2TokenRevocationAuthenticationProvider tokenRevocationAuthenticationProvider =

View File

@@ -16,8 +16,6 @@
package org.springframework.security.oauth2.server.authorization.web;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
@@ -34,21 +32,18 @@ import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.core.OAuth2Error;
import org.springframework.security.oauth2.core.OAuth2ErrorCodes;
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
import org.springframework.security.oauth2.core.http.converter.OAuth2ErrorHttpMessageConverter;
import org.springframework.security.oauth2.server.authorization.OAuth2TokenIntrospection;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2TokenIntrospectionAuthenticationProvider;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2TokenIntrospectionAuthenticationToken;
import org.springframework.security.oauth2.server.authorization.http.converter.OAuth2TokenIntrospectionHttpMessageConverter;
import org.springframework.security.oauth2.server.authorization.web.authentication.OAuth2TokenIntrospectionAuthenticationConverter;
import org.springframework.security.web.authentication.AuthenticationConverter;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.util.Assert;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;
/**
@@ -70,8 +65,7 @@ public final class OAuth2TokenIntrospectionEndpointFilter extends OncePerRequest
private final AuthenticationManager authenticationManager;
private final RequestMatcher tokenIntrospectionEndpointMatcher;
private AuthenticationConverter authenticationConverter =
new DefaultTokenIntrospectionAuthenticationConverter();
private AuthenticationConverter authenticationConverter;
private final HttpMessageConverter<OAuth2TokenIntrospection> tokenIntrospectionHttpResponseConverter =
new OAuth2TokenIntrospectionHttpMessageConverter();
private final HttpMessageConverter<OAuth2Error> errorHttpResponseConverter = new OAuth2ErrorHttpMessageConverter();
@@ -100,6 +94,7 @@ public final class OAuth2TokenIntrospectionEndpointFilter extends OncePerRequest
this.authenticationManager = authenticationManager;
this.tokenIntrospectionEndpointMatcher = new AntPathRequestMatcher(
tokenIntrospectionEndpointUri, HttpMethod.POST.name());
this.authenticationConverter = new OAuth2TokenIntrospectionAuthenticationConverter();
}
@Override
@@ -175,47 +170,4 @@ public final class OAuth2TokenIntrospectionEndpointFilter extends OncePerRequest
this.errorHttpResponseConverter.write(error, null, httpResponse);
}
private static void throwError(String errorCode, String parameterName) {
OAuth2Error error = new OAuth2Error(errorCode, "OAuth 2.0 Token Introspection Parameter: " + parameterName,
"https://datatracker.ietf.org/doc/html/rfc7662#section-2.1");
throw new OAuth2AuthenticationException(error);
}
private static class DefaultTokenIntrospectionAuthenticationConverter
implements AuthenticationConverter {
@Override
public Authentication convert(HttpServletRequest request) {
Authentication clientPrincipal = SecurityContextHolder.getContext().getAuthentication();
MultiValueMap<String, String> parameters = OAuth2EndpointUtils.getParameters(request);
// token (REQUIRED)
String token = parameters.getFirst(OAuth2ParameterNames.TOKEN);
if (!StringUtils.hasText(token) ||
parameters.get(OAuth2ParameterNames.TOKEN).size() != 1) {
throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.TOKEN);
}
// token_type_hint (OPTIONAL)
String tokenTypeHint = parameters.getFirst(OAuth2ParameterNames.TOKEN_TYPE_HINT);
if (StringUtils.hasText(tokenTypeHint) &&
parameters.get(OAuth2ParameterNames.TOKEN_TYPE_HINT).size() != 1) {
throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.TOKEN_TYPE_HINT);
}
Map<String, Object> additionalParameters = new HashMap<>();
parameters.forEach((key, value) -> {
if (!key.equals(OAuth2ParameterNames.TOKEN) &&
!key.equals(OAuth2ParameterNames.TOKEN_TYPE_HINT)) {
additionalParameters.put(key, value.get(0));
}
});
return new OAuth2TokenIntrospectionAuthenticationToken(
token, clientPrincipal, tokenTypeHint, additionalParameters);
}
}
}

View File

@@ -32,19 +32,16 @@ import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.core.OAuth2Error;
import org.springframework.security.oauth2.core.OAuth2ErrorCodes;
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
import org.springframework.security.oauth2.core.http.converter.OAuth2ErrorHttpMessageConverter;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2TokenRevocationAuthenticationProvider;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2TokenRevocationAuthenticationToken;
import org.springframework.security.oauth2.server.authorization.web.authentication.OAuth2TokenRevocationAuthenticationConverter;
import org.springframework.security.web.authentication.AuthenticationConverter;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.util.Assert;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;
/**
@@ -66,8 +63,7 @@ public final class OAuth2TokenRevocationEndpointFilter extends OncePerRequestFil
private final AuthenticationManager authenticationManager;
private final RequestMatcher tokenRevocationEndpointMatcher;
private AuthenticationConverter authenticationConverter =
new DefaultTokenRevocationAuthenticationConverter();
private AuthenticationConverter authenticationConverter;
private final HttpMessageConverter<OAuth2Error> errorHttpResponseConverter =
new OAuth2ErrorHttpMessageConverter();
private AuthenticationSuccessHandler authenticationSuccessHandler = this::sendRevocationSuccessResponse;
@@ -95,6 +91,7 @@ public final class OAuth2TokenRevocationEndpointFilter extends OncePerRequestFil
this.authenticationManager = authenticationManager;
this.tokenRevocationEndpointMatcher = new AntPathRequestMatcher(
tokenRevocationEndpointUri, HttpMethod.POST.name());
this.authenticationConverter = new OAuth2TokenRevocationAuthenticationConverter();
}
@Override
@@ -119,9 +116,9 @@ public final class OAuth2TokenRevocationEndpointFilter extends OncePerRequestFil
/**
* Sets the {@link AuthenticationConverter} used when attempting to extract a Revoke Token Request from {@link HttpServletRequest}
* to an instance of {@link OAuth2TokenRevocationAuthenticationToken} used for authenticating the client.
* to an instance of {@link OAuth2TokenRevocationAuthenticationToken} used for authenticating the request.
*
* @param authenticationConverter the {@link AuthenticationConverter} used when attempting to extract client credentials from {@link HttpServletRequest}
* @param authenticationConverter the {@link AuthenticationConverter} used when attempting to extract a Revoke Token Request from {@link HttpServletRequest}
* @since 0.2.2
*/
public void setAuthenticationConverter(AuthenticationConverter authenticationConverter) {
@@ -164,36 +161,4 @@ public final class OAuth2TokenRevocationEndpointFilter extends OncePerRequestFil
this.errorHttpResponseConverter.write(error, null, httpResponse);
}
private static void throwError(String errorCode, String parameterName) {
OAuth2Error error = new OAuth2Error(errorCode, "OAuth 2.0 Token Revocation Parameter: " + parameterName,
"https://datatracker.ietf.org/doc/html/rfc7009#section-2.1");
throw new OAuth2AuthenticationException(error);
}
private static class DefaultTokenRevocationAuthenticationConverter
implements AuthenticationConverter {
@Override
public Authentication convert(HttpServletRequest request) {
Authentication clientPrincipal = SecurityContextHolder.getContext().getAuthentication();
MultiValueMap<String, String> parameters = OAuth2EndpointUtils.getParameters(request);
// token (REQUIRED)
String token = parameters.getFirst(OAuth2ParameterNames.TOKEN);
if (!StringUtils.hasText(token) ||
parameters.get(OAuth2ParameterNames.TOKEN).size() != 1) {
throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.TOKEN);
}
// token_type_hint (OPTIONAL)
String tokenTypeHint = parameters.getFirst(OAuth2ParameterNames.TOKEN_TYPE_HINT);
if (StringUtils.hasText(tokenTypeHint) &&
parameters.get(OAuth2ParameterNames.TOKEN_TYPE_HINT).size() != 1) {
throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.TOKEN_TYPE_HINT);
}
return new OAuth2TokenRevocationAuthenticationToken(token, clientPrincipal, tokenTypeHint);
}
}
}

View File

@@ -0,0 +1,86 @@
/*
* Copyright 2020-2022 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
*
* https://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.server.authorization.web.authentication;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.core.OAuth2Error;
import org.springframework.security.oauth2.core.OAuth2ErrorCodes;
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2TokenIntrospectionAuthenticationToken;
import org.springframework.security.oauth2.server.authorization.web.OAuth2TokenIntrospectionEndpointFilter;
import org.springframework.security.web.authentication.AuthenticationConverter;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;
/**
* Attempts to extract an Introspection Request from {@link HttpServletRequest}
* and then converts it to an {@link OAuth2TokenIntrospectionAuthenticationToken} used for authenticating the request.
*
* @author Gerardo Roza
* @author Joe Grandja
* @since 0.4.0
* @see AuthenticationConverter
* @see OAuth2TokenIntrospectionAuthenticationToken
* @see OAuth2TokenIntrospectionEndpointFilter
*/
public final class OAuth2TokenIntrospectionAuthenticationConverter implements AuthenticationConverter {
@Override
public Authentication convert(HttpServletRequest request) {
Authentication clientPrincipal = SecurityContextHolder.getContext().getAuthentication();
MultiValueMap<String, String> parameters = OAuth2EndpointUtils.getParameters(request);
// token (REQUIRED)
String token = parameters.getFirst(OAuth2ParameterNames.TOKEN);
if (!StringUtils.hasText(token) ||
parameters.get(OAuth2ParameterNames.TOKEN).size() != 1) {
throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.TOKEN);
}
// token_type_hint (OPTIONAL)
String tokenTypeHint = parameters.getFirst(OAuth2ParameterNames.TOKEN_TYPE_HINT);
if (StringUtils.hasText(tokenTypeHint) &&
parameters.get(OAuth2ParameterNames.TOKEN_TYPE_HINT).size() != 1) {
throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.TOKEN_TYPE_HINT);
}
Map<String, Object> additionalParameters = new HashMap<>();
parameters.forEach((key, value) -> {
if (!key.equals(OAuth2ParameterNames.TOKEN) &&
!key.equals(OAuth2ParameterNames.TOKEN_TYPE_HINT)) {
additionalParameters.put(key, value.get(0));
}
});
return new OAuth2TokenIntrospectionAuthenticationToken(
token, clientPrincipal, tokenTypeHint, additionalParameters);
}
private static void throwError(String errorCode, String parameterName) {
OAuth2Error error = new OAuth2Error(errorCode, "OAuth 2.0 Token Introspection Parameter: " + parameterName,
"https://datatracker.ietf.org/doc/html/rfc7662#section-2.1");
throw new OAuth2AuthenticationException(error);
}
}

View File

@@ -0,0 +1,74 @@
/*
* Copyright 2020-2022 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
*
* https://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.server.authorization.web.authentication;
import javax.servlet.http.HttpServletRequest;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.core.OAuth2Error;
import org.springframework.security.oauth2.core.OAuth2ErrorCodes;
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2TokenRevocationAuthenticationToken;
import org.springframework.security.oauth2.server.authorization.web.OAuth2TokenRevocationEndpointFilter;
import org.springframework.security.web.authentication.AuthenticationConverter;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;
/**
* Attempts to extract a Revoke Token Request from {@link HttpServletRequest}
* and then converts it to an {@link OAuth2TokenRevocationAuthenticationToken} used for authenticating the request.
*
* @author Vivek Babu
* @author Joe Grandja
* @since 0.4.0
* @see AuthenticationConverter
* @see OAuth2TokenRevocationAuthenticationToken
* @see OAuth2TokenRevocationEndpointFilter
*/
public final class OAuth2TokenRevocationAuthenticationConverter implements AuthenticationConverter {
@Override
public Authentication convert(HttpServletRequest request) {
Authentication clientPrincipal = SecurityContextHolder.getContext().getAuthentication();
MultiValueMap<String, String> parameters = OAuth2EndpointUtils.getParameters(request);
// token (REQUIRED)
String token = parameters.getFirst(OAuth2ParameterNames.TOKEN);
if (!StringUtils.hasText(token) ||
parameters.get(OAuth2ParameterNames.TOKEN).size() != 1) {
throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.TOKEN);
}
// token_type_hint (OPTIONAL)
String tokenTypeHint = parameters.getFirst(OAuth2ParameterNames.TOKEN_TYPE_HINT);
if (StringUtils.hasText(tokenTypeHint) &&
parameters.get(OAuth2ParameterNames.TOKEN_TYPE_HINT).size() != 1) {
throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.TOKEN_TYPE_HINT);
}
return new OAuth2TokenRevocationAuthenticationToken(token, clientPrincipal, tokenTypeHint);
}
private static void throwError(String errorCode, String parameterName) {
OAuth2Error error = new OAuth2Error(errorCode, "OAuth 2.0 Token Revocation Parameter: " + parameterName,
"https://datatracker.ietf.org/doc/html/rfc7009#section-2.1");
throw new OAuth2AuthenticationException(error);
}
}