Browse Source

Add support for multiple issuers per host using the path component

Closes gh-1342
pull/1429/head
Joe Grandja 2 years ago
parent
commit
168077be24
  1. 85
      oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/AuthorizationServerContextFilter.java
  2. 15
      oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2AuthorizationEndpointConfigurer.java
  3. 8
      oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2AuthorizationServerConfigurer.java
  4. 4
      oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2AuthorizationServerMetadataEndpointConfigurer.java
  5. 12
      oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2ClientAuthenticationConfigurer.java
  6. 10
      oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2ConfigurerUtils.java
  7. 8
      oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2DeviceAuthorizationEndpointConfigurer.java
  8. 15
      oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2DeviceVerificationEndpointConfigurer.java
  9. 8
      oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2TokenEndpointConfigurer.java
  10. 8
      oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2TokenIntrospectionEndpointConfigurer.java
  11. 8
      oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2TokenRevocationEndpointConfigurer.java
  12. 8
      oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OidcClientRegistrationEndpointConfigurer.java
  13. 8
      oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OidcLogoutEndpointConfigurer.java
  14. 4
      oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OidcProviderConfigurationEndpointConfigurer.java
  15. 8
      oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OidcUserInfoEndpointConfigurer.java
  16. 19
      oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/context/AuthorizationServerContext.java
  17. 4
      oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/web/OidcProviderConfigurationEndpointFilter.java
  18. 4
      oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/OAuth2AuthorizationServerMetadataEndpointFilter.java
  19. 9
      oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/OAuth2DeviceAuthorizationEndpointFilter.java
  20. 133
      oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/AuthorizationServerContextFilterTests.java
  21. 10
      oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/JwkSetTests.java
  22. 42
      oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2AuthorizationCodeGrantTests.java
  23. 36
      oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2AuthorizationServerMetadataTests.java
  24. 26
      oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2ClientCredentialsGrantTests.java
  25. 14
      oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2DeviceCodeGrantTests.java
  26. 51
      oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2TokenIntrospectionTests.java
  27. 31
      oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2TokenRevocationTests.java
  28. 18
      oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OidcClientRegistrationTests.java
  29. 81
      oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OidcProviderConfigurationTests.java
  30. 10
      oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OidcTests.java
  31. 24
      oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OidcUserInfoTests.java
  32. 6
      oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/OAuth2DeviceAuthorizationEndpointFilterTests.java
  33. 6
      samples/demo-authorizationserver/src/main/java/sample/config/AuthorizationServerConfig.java

85
oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/AuthorizationServerContextFilter.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2020-2022 the original author or authors.
* Copyright 2020-2024 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.
@ -16,7 +16,10 @@ @@ -16,7 +16,10 @@
package org.springframework.security.oauth2.server.authorization.config.annotation.web.configurers;
import java.io.IOException;
import java.util.function.Supplier;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
@ -28,6 +31,7 @@ import org.springframework.security.oauth2.server.authorization.context.Authoriz @@ -28,6 +31,7 @@ import org.springframework.security.oauth2.server.authorization.context.Authoriz
import org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;
import org.springframework.security.web.util.UrlUtils;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.util.UriComponentsBuilder;
@ -42,10 +46,12 @@ import org.springframework.web.util.UriComponentsBuilder; @@ -42,10 +46,12 @@ import org.springframework.web.util.UriComponentsBuilder;
*/
final class AuthorizationServerContextFilter extends OncePerRequestFilter {
private final AuthorizationServerSettings authorizationServerSettings;
private final IssuerResolver issuerResolver;
AuthorizationServerContextFilter(AuthorizationServerSettings authorizationServerSettings) {
Assert.notNull(authorizationServerSettings, "authorizationServerSettings cannot be null");
this.authorizationServerSettings = authorizationServerSettings;
this.issuerResolver = new IssuerResolver(authorizationServerSettings);
}
@Override
@ -53,10 +59,9 @@ final class AuthorizationServerContextFilter extends OncePerRequestFilter { @@ -53,10 +59,9 @@ final class AuthorizationServerContextFilter extends OncePerRequestFilter {
throws ServletException, IOException {
try {
String issuer = this.issuerResolver.resolve(request);
AuthorizationServerContext authorizationServerContext =
new DefaultAuthorizationServerContext(
() -> resolveIssuer(this.authorizationServerSettings, request),
this.authorizationServerSettings);
new DefaultAuthorizationServerContext(issuer, this.authorizationServerSettings);
AuthorizationServerContextHolder.setContext(authorizationServerContext);
filterChain.doFilter(request, response);
} finally {
@ -64,35 +69,69 @@ final class AuthorizationServerContextFilter extends OncePerRequestFilter { @@ -64,35 +69,69 @@ final class AuthorizationServerContextFilter extends OncePerRequestFilter {
}
}
private static String resolveIssuer(AuthorizationServerSettings authorizationServerSettings, HttpServletRequest request) {
return authorizationServerSettings.getIssuer() != null ?
authorizationServerSettings.getIssuer() :
getContextPath(request);
}
private static final class IssuerResolver {
private final String issuer;
private final Set<String> endpointUris;
private IssuerResolver(AuthorizationServerSettings authorizationServerSettings) {
if (authorizationServerSettings.getIssuer() != null) {
this.issuer = authorizationServerSettings.getIssuer();
this.endpointUris = Collections.emptySet();
} else {
this.issuer = null;
this.endpointUris = new HashSet<>();
this.endpointUris.add("/.well-known/oauth-authorization-server");
this.endpointUris.add("/.well-known/openid-configuration");
for (Map.Entry<String, Object> setting : authorizationServerSettings.getSettings().entrySet()) {
if (setting.getKey().endsWith("-endpoint")) {
this.endpointUris.add((String) setting.getValue());
}
}
}
}
private String resolve(HttpServletRequest request) {
if (this.issuer != null) {
return this.issuer;
}
// Resolve Issuer Identifier dynamically from request
String path = request.getRequestURI();
if (!StringUtils.hasText(path)) {
path = "";
} else {
for (String endpointUri : this.endpointUris) {
if (path.contains(endpointUri)) {
path = path.replace(endpointUri, "");
break;
}
}
}
// @formatter:off
return UriComponentsBuilder.fromHttpUrl(UrlUtils.buildFullRequestUrl(request))
.replacePath(path)
.replaceQuery(null)
.fragment(null)
.build()
.toUriString();
// @formatter:on
}
private static String getContextPath(HttpServletRequest request) {
// @formatter:off
return UriComponentsBuilder.fromHttpUrl(UrlUtils.buildFullRequestUrl(request))
.replacePath(request.getContextPath())
.replaceQuery(null)
.fragment(null)
.build()
.toUriString();
// @formatter:on
}
private static final class DefaultAuthorizationServerContext implements AuthorizationServerContext {
private final Supplier<String> issuerSupplier;
private final String issuer;
private final AuthorizationServerSettings authorizationServerSettings;
private DefaultAuthorizationServerContext(Supplier<String> issuerSupplier, AuthorizationServerSettings authorizationServerSettings) {
this.issuerSupplier = issuerSupplier;
private DefaultAuthorizationServerContext(String issuer, AuthorizationServerSettings authorizationServerSettings) {
this.issuer = issuer;
this.authorizationServerSettings = authorizationServerSettings;
}
@Override
public String getIssuer() {
return this.issuerSupplier.get();
return this.issuer;
}
@Override

15
oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2AuthorizationEndpointConfigurer.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2020-2023 the original author or authors.
* Copyright 2020-2024 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.
@ -51,6 +51,8 @@ import org.springframework.security.web.util.matcher.RequestMatcher; @@ -51,6 +51,8 @@ import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import static org.springframework.security.oauth2.server.authorization.config.annotation.web.configurers.OAuth2ConfigurerUtils.withMultipleIssuerPattern;
/**
* Configurer for the OAuth 2.0 Authorization Endpoint.
*
@ -209,13 +211,10 @@ public final class OAuth2AuthorizationEndpointConfigurer extends AbstractOAuth2C @@ -209,13 +211,10 @@ public final class OAuth2AuthorizationEndpointConfigurer extends AbstractOAuth2C
@Override
void init(HttpSecurity httpSecurity) {
AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils.getAuthorizationServerSettings(httpSecurity);
String authorizationEndpointUri = withMultipleIssuerPattern(authorizationServerSettings.getAuthorizationEndpoint());
this.requestMatcher = new OrRequestMatcher(
new AntPathRequestMatcher(
authorizationServerSettings.getAuthorizationEndpoint(),
HttpMethod.GET.name()),
new AntPathRequestMatcher(
authorizationServerSettings.getAuthorizationEndpoint(),
HttpMethod.POST.name()));
new AntPathRequestMatcher(authorizationEndpointUri, HttpMethod.GET.name()),
new AntPathRequestMatcher(authorizationEndpointUri, HttpMethod.POST.name()));
List<AuthenticationProvider> authenticationProviders = createDefaultAuthenticationProviders(httpSecurity);
if (!this.authenticationProviders.isEmpty()) {
@ -234,7 +233,7 @@ public final class OAuth2AuthorizationEndpointConfigurer extends AbstractOAuth2C @@ -234,7 +233,7 @@ public final class OAuth2AuthorizationEndpointConfigurer extends AbstractOAuth2C
OAuth2AuthorizationEndpointFilter authorizationEndpointFilter =
new OAuth2AuthorizationEndpointFilter(
authenticationManager,
authorizationServerSettings.getAuthorizationEndpoint());
withMultipleIssuerPattern(authorizationServerSettings.getAuthorizationEndpoint()));
List<AuthenticationConverter> authenticationConverters = createDefaultAuthenticationConverters();
if (!this.authorizationRequestConverters.isEmpty()) {
authenticationConverters.addAll(0, this.authorizationRequestConverters);

8
oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2AuthorizationServerConfigurer.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2020-2022 the original author or authors.
* Copyright 2020-2024 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.
@ -56,6 +56,8 @@ import org.springframework.security.web.util.matcher.OrRequestMatcher; @@ -56,6 +56,8 @@ import org.springframework.security.web.util.matcher.OrRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.util.Assert;
import static org.springframework.security.oauth2.server.authorization.config.annotation.web.configurers.OAuth2ConfigurerUtils.withMultipleIssuerPattern;
/**
* An {@link AbstractHttpConfigurer} for OAuth 2.0 Authorization Server support.
*
@ -314,7 +316,7 @@ public final class OAuth2AuthorizationServerConfigurer @@ -314,7 +316,7 @@ public final class OAuth2AuthorizationServerConfigurer
requestMatchers.add(configurer.getRequestMatcher());
});
requestMatchers.add(new AntPathRequestMatcher(
authorizationServerSettings.getJwkSetEndpoint(), HttpMethod.GET.name()));
withMultipleIssuerPattern(authorizationServerSettings.getJwkSetEndpoint()), HttpMethod.GET.name()));
this.endpointsMatcher = new OrRequestMatcher(requestMatchers);
ExceptionHandlingConfigurer<HttpSecurity> exceptionHandling = httpSecurity.getConfigurer(ExceptionHandlingConfigurer.class);
@ -342,7 +344,7 @@ public final class OAuth2AuthorizationServerConfigurer @@ -342,7 +344,7 @@ public final class OAuth2AuthorizationServerConfigurer
JWKSource<com.nimbusds.jose.proc.SecurityContext> jwkSource = OAuth2ConfigurerUtils.getJwkSource(httpSecurity);
if (jwkSource != null) {
NimbusJwkSetEndpointFilter jwkSetEndpointFilter = new NimbusJwkSetEndpointFilter(
jwkSource, authorizationServerSettings.getJwkSetEndpoint());
jwkSource, withMultipleIssuerPattern(authorizationServerSettings.getJwkSetEndpoint()));
httpSecurity.addFilterBefore(postProcess(jwkSetEndpointFilter), AbstractPreAuthenticatedProcessingFilter.class);
}
}

4
oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2AuthorizationServerMetadataEndpointConfigurer.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2020-2022 the original author or authors.
* Copyright 2020-2024 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.
@ -70,7 +70,7 @@ public final class OAuth2AuthorizationServerMetadataEndpointConfigurer extends A @@ -70,7 +70,7 @@ public final class OAuth2AuthorizationServerMetadataEndpointConfigurer extends A
@Override
void init(HttpSecurity httpSecurity) {
this.requestMatcher = new AntPathRequestMatcher(
"/.well-known/oauth-authorization-server", HttpMethod.GET.name());
"/.well-known/oauth-authorization-server/**", HttpMethod.GET.name());
}
@Override

12
oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2ClientAuthenticationConfigurer.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2020-2023 the original author or authors.
* Copyright 2020-2024 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.
@ -51,6 +51,8 @@ import org.springframework.security.web.util.matcher.OrRequestMatcher; @@ -51,6 +51,8 @@ import org.springframework.security.web.util.matcher.OrRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.util.Assert;
import static org.springframework.security.oauth2.server.authorization.config.annotation.web.configurers.OAuth2ConfigurerUtils.withMultipleIssuerPattern;
/**
* Configurer for OAuth 2.0 Client Authentication.
*
@ -161,16 +163,16 @@ public final class OAuth2ClientAuthenticationConfigurer extends AbstractOAuth2Co @@ -161,16 +163,16 @@ public final class OAuth2ClientAuthenticationConfigurer extends AbstractOAuth2Co
AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils.getAuthorizationServerSettings(httpSecurity);
this.requestMatcher = new OrRequestMatcher(
new AntPathRequestMatcher(
authorizationServerSettings.getTokenEndpoint(),
withMultipleIssuerPattern(authorizationServerSettings.getTokenEndpoint()),
HttpMethod.POST.name()),
new AntPathRequestMatcher(
authorizationServerSettings.getTokenIntrospectionEndpoint(),
withMultipleIssuerPattern(authorizationServerSettings.getTokenIntrospectionEndpoint()),
HttpMethod.POST.name()),
new AntPathRequestMatcher(
authorizationServerSettings.getTokenRevocationEndpoint(),
withMultipleIssuerPattern(authorizationServerSettings.getTokenRevocationEndpoint()),
HttpMethod.POST.name()),
new AntPathRequestMatcher(
authorizationServerSettings.getDeviceAuthorizationEndpoint(),
withMultipleIssuerPattern(authorizationServerSettings.getDeviceAuthorizationEndpoint()),
HttpMethod.POST.name()));
List<AuthenticationProvider> authenticationProviders = createDefaultAuthenticationProviders(httpSecurity);

10
oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2ConfigurerUtils.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2020-2023 the original author or authors.
* Copyright 2020-2024 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.
@ -43,6 +43,7 @@ import org.springframework.security.oauth2.server.authorization.token.OAuth2Refr @@ -43,6 +43,7 @@ import org.springframework.security.oauth2.server.authorization.token.OAuth2Refr
import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenClaimsContext;
import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenCustomizer;
import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenGenerator;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
@ -56,6 +57,13 @@ final class OAuth2ConfigurerUtils { @@ -56,6 +57,13 @@ final class OAuth2ConfigurerUtils {
private OAuth2ConfigurerUtils() {
}
static String withMultipleIssuerPattern(String endpointUri) {
Assert.hasText(endpointUri, "endpointUri cannot be empty");
return endpointUri.startsWith("/") ?
"/**" + endpointUri :
"/**/" + endpointUri;
}
static RegisteredClientRepository getRegisteredClientRepository(HttpSecurity httpSecurity) {
RegisteredClientRepository registeredClientRepository = httpSecurity.getSharedObject(RegisteredClientRepository.class);
if (registeredClientRepository == null) {

8
oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2DeviceAuthorizationEndpointConfigurer.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2020-2023 the original author or authors.
* Copyright 2020-2024 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.
@ -45,6 +45,8 @@ import org.springframework.security.web.util.matcher.RequestMatcher; @@ -45,6 +45,8 @@ import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import static org.springframework.security.oauth2.server.authorization.config.annotation.web.configurers.OAuth2ConfigurerUtils.withMultipleIssuerPattern;
/**
* Configurer for the OAuth 2.0 Device Authorization Endpoint.
*
@ -166,7 +168,7 @@ public final class OAuth2DeviceAuthorizationEndpointConfigurer extends AbstractO @@ -166,7 +168,7 @@ public final class OAuth2DeviceAuthorizationEndpointConfigurer extends AbstractO
AuthorizationServerSettings authorizationServerSettings =
OAuth2ConfigurerUtils.getAuthorizationServerSettings(builder);
this.requestMatcher = new AntPathRequestMatcher(
authorizationServerSettings.getDeviceAuthorizationEndpoint(), HttpMethod.POST.name());
withMultipleIssuerPattern(authorizationServerSettings.getDeviceAuthorizationEndpoint()), HttpMethod.POST.name());
List<AuthenticationProvider> authenticationProviders = createDefaultAuthenticationProviders(builder);
if (!this.authenticationProviders.isEmpty()) {
@ -184,7 +186,7 @@ public final class OAuth2DeviceAuthorizationEndpointConfigurer extends AbstractO @@ -184,7 +186,7 @@ public final class OAuth2DeviceAuthorizationEndpointConfigurer extends AbstractO
OAuth2DeviceAuthorizationEndpointFilter deviceAuthorizationEndpointFilter =
new OAuth2DeviceAuthorizationEndpointFilter(
authenticationManager, authorizationServerSettings.getDeviceAuthorizationEndpoint());
authenticationManager, withMultipleIssuerPattern(authorizationServerSettings.getDeviceAuthorizationEndpoint()));
List<AuthenticationConverter> authenticationConverters = createDefaultAuthenticationConverters();
if (!this.deviceAuthorizationRequestConverters.isEmpty()) {

15
oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2DeviceVerificationEndpointConfigurer.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2020-2023 the original author or authors.
* Copyright 2020-2024 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.
@ -50,6 +50,8 @@ import org.springframework.security.web.util.matcher.RequestMatcher; @@ -50,6 +50,8 @@ import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import static org.springframework.security.oauth2.server.authorization.config.annotation.web.configurers.OAuth2ConfigurerUtils.withMultipleIssuerPattern;
/**
* Configurer for the OAuth 2.0 Device Verification Endpoint.
*
@ -195,13 +197,10 @@ public final class OAuth2DeviceVerificationEndpointConfigurer extends AbstractOA @@ -195,13 +197,10 @@ public final class OAuth2DeviceVerificationEndpointConfigurer extends AbstractOA
public void init(HttpSecurity builder) {
AuthorizationServerSettings authorizationServerSettings =
OAuth2ConfigurerUtils.getAuthorizationServerSettings(builder);
String deviceVerificationEndpointUri = withMultipleIssuerPattern(authorizationServerSettings.getDeviceVerificationEndpoint());
this.requestMatcher = new OrRequestMatcher(
new AntPathRequestMatcher(
authorizationServerSettings.getDeviceVerificationEndpoint(),
HttpMethod.GET.name()),
new AntPathRequestMatcher(
authorizationServerSettings.getDeviceVerificationEndpoint(),
HttpMethod.POST.name()));
new AntPathRequestMatcher(deviceVerificationEndpointUri, HttpMethod.GET.name()),
new AntPathRequestMatcher(deviceVerificationEndpointUri, HttpMethod.POST.name()));
List<AuthenticationProvider> authenticationProviders = createDefaultAuthenticationProviders(builder);
if (!this.authenticationProviders.isEmpty()) {
@ -221,7 +220,7 @@ public final class OAuth2DeviceVerificationEndpointConfigurer extends AbstractOA @@ -221,7 +220,7 @@ public final class OAuth2DeviceVerificationEndpointConfigurer extends AbstractOA
OAuth2DeviceVerificationEndpointFilter deviceVerificationEndpointFilter =
new OAuth2DeviceVerificationEndpointFilter(
authenticationManager,
authorizationServerSettings.getDeviceVerificationEndpoint());
withMultipleIssuerPattern(authorizationServerSettings.getDeviceVerificationEndpoint()));
List<AuthenticationConverter> authenticationConverters = createDefaultAuthenticationConverters();
if (!this.deviceVerificationRequestConverters.isEmpty()) {
authenticationConverters.addAll(0, this.deviceVerificationRequestConverters);

8
oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2TokenEndpointConfigurer.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2020-2023 the original author or authors.
* Copyright 2020-2024 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.
@ -54,6 +54,8 @@ import org.springframework.security.web.util.matcher.AntPathRequestMatcher; @@ -54,6 +54,8 @@ import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.util.Assert;
import static org.springframework.security.oauth2.server.authorization.config.annotation.web.configurers.OAuth2ConfigurerUtils.withMultipleIssuerPattern;
/**
* Configurer for the OAuth 2.0 Token Endpoint.
*
@ -163,7 +165,7 @@ public final class OAuth2TokenEndpointConfigurer extends AbstractOAuth2Configure @@ -163,7 +165,7 @@ public final class OAuth2TokenEndpointConfigurer extends AbstractOAuth2Configure
void init(HttpSecurity httpSecurity) {
AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils.getAuthorizationServerSettings(httpSecurity);
this.requestMatcher = new AntPathRequestMatcher(
authorizationServerSettings.getTokenEndpoint(), HttpMethod.POST.name());
withMultipleIssuerPattern(authorizationServerSettings.getTokenEndpoint()), HttpMethod.POST.name());
List<AuthenticationProvider> authenticationProviders = createDefaultAuthenticationProviders(httpSecurity);
if (!this.authenticationProviders.isEmpty()) {
@ -182,7 +184,7 @@ public final class OAuth2TokenEndpointConfigurer extends AbstractOAuth2Configure @@ -182,7 +184,7 @@ public final class OAuth2TokenEndpointConfigurer extends AbstractOAuth2Configure
OAuth2TokenEndpointFilter tokenEndpointFilter =
new OAuth2TokenEndpointFilter(
authenticationManager,
authorizationServerSettings.getTokenEndpoint());
withMultipleIssuerPattern(authorizationServerSettings.getTokenEndpoint()));
List<AuthenticationConverter> authenticationConverters = createDefaultAuthenticationConverters();
if (!this.accessTokenRequestConverters.isEmpty()) {
authenticationConverters.addAll(0, this.accessTokenRequestConverters);

8
oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2TokenIntrospectionEndpointConfigurer.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2020-2022 the original author or authors.
* Copyright 2020-2024 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.
@ -43,6 +43,8 @@ import org.springframework.security.web.util.matcher.AntPathRequestMatcher; @@ -43,6 +43,8 @@ import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.util.Assert;
import static org.springframework.security.oauth2.server.authorization.config.annotation.web.configurers.OAuth2ConfigurerUtils.withMultipleIssuerPattern;
/**
* Configurer for the OAuth 2.0 Token Introspection Endpoint.
*
@ -152,7 +154,7 @@ public final class OAuth2TokenIntrospectionEndpointConfigurer extends AbstractOA @@ -152,7 +154,7 @@ public final class OAuth2TokenIntrospectionEndpointConfigurer extends AbstractOA
void init(HttpSecurity httpSecurity) {
AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils.getAuthorizationServerSettings(httpSecurity);
this.requestMatcher = new AntPathRequestMatcher(
authorizationServerSettings.getTokenIntrospectionEndpoint(), HttpMethod.POST.name());
withMultipleIssuerPattern(authorizationServerSettings.getTokenIntrospectionEndpoint()), HttpMethod.POST.name());
List<AuthenticationProvider> authenticationProviders = createDefaultAuthenticationProviders(httpSecurity);
if (!this.authenticationProviders.isEmpty()) {
@ -170,7 +172,7 @@ public final class OAuth2TokenIntrospectionEndpointConfigurer extends AbstractOA @@ -170,7 +172,7 @@ public final class OAuth2TokenIntrospectionEndpointConfigurer extends AbstractOA
OAuth2TokenIntrospectionEndpointFilter introspectionEndpointFilter =
new OAuth2TokenIntrospectionEndpointFilter(
authenticationManager, authorizationServerSettings.getTokenIntrospectionEndpoint());
authenticationManager, withMultipleIssuerPattern(authorizationServerSettings.getTokenIntrospectionEndpoint()));
List<AuthenticationConverter> authenticationConverters = createDefaultAuthenticationConverters();
if (!this.introspectionRequestConverters.isEmpty()) {
authenticationConverters.addAll(0, this.introspectionRequestConverters);

8
oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2TokenRevocationEndpointConfigurer.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2020-2022 the original author or authors.
* Copyright 2020-2024 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.
@ -42,6 +42,8 @@ import org.springframework.security.web.util.matcher.AntPathRequestMatcher; @@ -42,6 +42,8 @@ import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.util.Assert;
import static org.springframework.security.oauth2.server.authorization.config.annotation.web.configurers.OAuth2ConfigurerUtils.withMultipleIssuerPattern;
/**
* Configurer for the OAuth 2.0 Token Revocation Endpoint.
*
@ -151,7 +153,7 @@ public final class OAuth2TokenRevocationEndpointConfigurer extends AbstractOAuth @@ -151,7 +153,7 @@ public final class OAuth2TokenRevocationEndpointConfigurer extends AbstractOAuth
void init(HttpSecurity httpSecurity) {
AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils.getAuthorizationServerSettings(httpSecurity);
this.requestMatcher = new AntPathRequestMatcher(
authorizationServerSettings.getTokenRevocationEndpoint(), HttpMethod.POST.name());
withMultipleIssuerPattern(authorizationServerSettings.getTokenRevocationEndpoint()), HttpMethod.POST.name());
List<AuthenticationProvider> authenticationProviders = createDefaultAuthenticationProviders(httpSecurity);
if (!this.authenticationProviders.isEmpty()) {
@ -169,7 +171,7 @@ public final class OAuth2TokenRevocationEndpointConfigurer extends AbstractOAuth @@ -169,7 +171,7 @@ public final class OAuth2TokenRevocationEndpointConfigurer extends AbstractOAuth
OAuth2TokenRevocationEndpointFilter revocationEndpointFilter =
new OAuth2TokenRevocationEndpointFilter(
authenticationManager, authorizationServerSettings.getTokenRevocationEndpoint());
authenticationManager, withMultipleIssuerPattern(authorizationServerSettings.getTokenRevocationEndpoint()));
List<AuthenticationConverter> authenticationConverters = createDefaultAuthenticationConverters();
if (!this.revocationRequestConverters.isEmpty()) {
authenticationConverters.addAll(0, this.revocationRequestConverters);

8
oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OidcClientRegistrationEndpointConfigurer.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2020-2023 the original author or authors.
* Copyright 2020-2024 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.
@ -46,6 +46,8 @@ import org.springframework.security.web.util.matcher.OrRequestMatcher; @@ -46,6 +46,8 @@ import org.springframework.security.web.util.matcher.OrRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.util.Assert;
import static org.springframework.security.oauth2.server.authorization.config.annotation.web.configurers.OAuth2ConfigurerUtils.withMultipleIssuerPattern;
/**
* Configurer for OpenID Connect 1.0 Dynamic Client Registration Endpoint.
*
@ -160,7 +162,7 @@ public final class OidcClientRegistrationEndpointConfigurer extends AbstractOAut @@ -160,7 +162,7 @@ public final class OidcClientRegistrationEndpointConfigurer extends AbstractOAut
@Override
void init(HttpSecurity httpSecurity) {
AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils.getAuthorizationServerSettings(httpSecurity);
String clientRegistrationEndpointUri = authorizationServerSettings.getOidcClientRegistrationEndpoint();
String clientRegistrationEndpointUri = withMultipleIssuerPattern(authorizationServerSettings.getOidcClientRegistrationEndpoint());
this.requestMatcher = new OrRequestMatcher(
new AntPathRequestMatcher(clientRegistrationEndpointUri, HttpMethod.POST.name()),
new AntPathRequestMatcher(clientRegistrationEndpointUri, HttpMethod.GET.name())
@ -183,7 +185,7 @@ public final class OidcClientRegistrationEndpointConfigurer extends AbstractOAut @@ -183,7 +185,7 @@ public final class OidcClientRegistrationEndpointConfigurer extends AbstractOAut
OidcClientRegistrationEndpointFilter oidcClientRegistrationEndpointFilter =
new OidcClientRegistrationEndpointFilter(
authenticationManager,
authorizationServerSettings.getOidcClientRegistrationEndpoint());
withMultipleIssuerPattern(authorizationServerSettings.getOidcClientRegistrationEndpoint()));
List<AuthenticationConverter> authenticationConverters = createDefaultAuthenticationConverters();
if (!this.clientRegistrationRequestConverters.isEmpty()) {
authenticationConverters.addAll(0, this.clientRegistrationRequestConverters);

8
oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OidcLogoutEndpointConfigurer.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2020-2023 the original author or authors.
* Copyright 2020-2024 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.
@ -44,6 +44,8 @@ import org.springframework.security.web.util.matcher.OrRequestMatcher; @@ -44,6 +44,8 @@ import org.springframework.security.web.util.matcher.OrRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.util.Assert;
import static org.springframework.security.oauth2.server.authorization.config.annotation.web.configurers.OAuth2ConfigurerUtils.withMultipleIssuerPattern;
/**
* Configurer for OpenID Connect 1.0 RP-Initiated Logout Endpoint.
*
@ -151,7 +153,7 @@ public final class OidcLogoutEndpointConfigurer extends AbstractOAuth2Configurer @@ -151,7 +153,7 @@ public final class OidcLogoutEndpointConfigurer extends AbstractOAuth2Configurer
@Override
void init(HttpSecurity httpSecurity) {
AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils.getAuthorizationServerSettings(httpSecurity);
String logoutEndpointUri = authorizationServerSettings.getOidcLogoutEndpoint();
String logoutEndpointUri = withMultipleIssuerPattern(authorizationServerSettings.getOidcLogoutEndpoint());
this.requestMatcher = new OrRequestMatcher(
new AntPathRequestMatcher(logoutEndpointUri, HttpMethod.GET.name()),
new AntPathRequestMatcher(logoutEndpointUri, HttpMethod.POST.name())
@ -174,7 +176,7 @@ public final class OidcLogoutEndpointConfigurer extends AbstractOAuth2Configurer @@ -174,7 +176,7 @@ public final class OidcLogoutEndpointConfigurer extends AbstractOAuth2Configurer
OidcLogoutEndpointFilter oidcLogoutEndpointFilter =
new OidcLogoutEndpointFilter(
authenticationManager,
authorizationServerSettings.getOidcLogoutEndpoint());
withMultipleIssuerPattern(authorizationServerSettings.getOidcLogoutEndpoint()));
List<AuthenticationConverter> authenticationConverters = createDefaultAuthenticationConverters();
if (!this.logoutRequestConverters.isEmpty()) {
authenticationConverters.addAll(0, this.logoutRequestConverters);

4
oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OidcProviderConfigurationEndpointConfigurer.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2020-2022 the original author or authors.
* Copyright 2020-2024 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.
@ -70,7 +70,7 @@ public final class OidcProviderConfigurationEndpointConfigurer extends AbstractO @@ -70,7 +70,7 @@ public final class OidcProviderConfigurationEndpointConfigurer extends AbstractO
@Override
void init(HttpSecurity httpSecurity) {
this.requestMatcher = new AntPathRequestMatcher(
"/.well-known/openid-configuration", HttpMethod.GET.name());
"/**/.well-known/openid-configuration", HttpMethod.GET.name());
}
@Override

8
oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OidcUserInfoEndpointConfigurer.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2020-2022 the original author or authors.
* Copyright 2020-2024 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.
@ -49,6 +49,8 @@ import org.springframework.security.web.util.matcher.OrRequestMatcher; @@ -49,6 +49,8 @@ import org.springframework.security.web.util.matcher.OrRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.util.Assert;
import static org.springframework.security.oauth2.server.authorization.config.annotation.web.configurers.OAuth2ConfigurerUtils.withMultipleIssuerPattern;
/**
* Configurer for OpenID Connect 1.0 UserInfo Endpoint.
*
@ -185,7 +187,7 @@ public final class OidcUserInfoEndpointConfigurer extends AbstractOAuth2Configur @@ -185,7 +187,7 @@ public final class OidcUserInfoEndpointConfigurer extends AbstractOAuth2Configur
@Override
void init(HttpSecurity httpSecurity) {
AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils.getAuthorizationServerSettings(httpSecurity);
String userInfoEndpointUri = authorizationServerSettings.getOidcUserInfoEndpoint();
String userInfoEndpointUri = withMultipleIssuerPattern(authorizationServerSettings.getOidcUserInfoEndpoint());
this.requestMatcher = new OrRequestMatcher(
new AntPathRequestMatcher(userInfoEndpointUri, HttpMethod.GET.name()),
new AntPathRequestMatcher(userInfoEndpointUri, HttpMethod.POST.name()));
@ -207,7 +209,7 @@ public final class OidcUserInfoEndpointConfigurer extends AbstractOAuth2Configur @@ -207,7 +209,7 @@ public final class OidcUserInfoEndpointConfigurer extends AbstractOAuth2Configur
OidcUserInfoEndpointFilter oidcUserInfoEndpointFilter =
new OidcUserInfoEndpointFilter(
authenticationManager,
authorizationServerSettings.getOidcUserInfoEndpoint());
withMultipleIssuerPattern(authorizationServerSettings.getOidcUserInfoEndpoint()));
List<AuthenticationConverter> authenticationConverters = createDefaultAuthenticationConverters();
if (!this.userInfoRequestConverters.isEmpty()) {
authenticationConverters.addAll(0, this.userInfoRequestConverters);

19
oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/context/AuthorizationServerContext.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2020-2022 the original author or authors.
* Copyright 2020-2024 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.
@ -28,9 +28,22 @@ import org.springframework.security.oauth2.server.authorization.settings.Authori @@ -28,9 +28,22 @@ import org.springframework.security.oauth2.server.authorization.settings.Authori
public interface AuthorizationServerContext {
/**
* Returns the {@code URL} of the Authorization Server's issuer identifier.
* Returns {@link AuthorizationServerSettings#getIssuer()} if available, otherwise,
* resolves the issuer identifier from the <i>"current"</i> request.
*
* @return the {@code URL} of the Authorization Server's issuer identifier
* <p>
* The issuer identifier may contain a path component to support multiple issuers per host in a multi-tenant hosting configuration.
*
* <p>
* For example:
* <ul>
* <li>{@code https://example.com/issuer1/oauth2/token} &mdash; resolves the issuer to {@code https://example.com/issuer1}</li>
* <li>{@code https://example.com/issuer2/oauth2/token} &mdash; resolves the issuer to {@code https://example.com/issuer2}</li>
* <li>{@code https://example.com/authz/issuer1/oauth2/token} &mdash; resolves the issuer to {@code https://example.com/authz/issuer1}</li>
* <li>{@code https://example.com/authz/issuer2/oauth2/token} &mdash; resolves the issuer to {@code https://example.com/authz/issuer2}</li>
* </ul>
*
* @return {@link AuthorizationServerSettings#getIssuer()} if available, otherwise, resolves the issuer identifier from the <i>"current"</i> request
*/
String getIssuer();

4
oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/web/OidcProviderConfigurationEndpointFilter.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2020-2023 the original author or authors.
* Copyright 2020-2024 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.
@ -57,7 +57,7 @@ public final class OidcProviderConfigurationEndpointFilter extends OncePerReques @@ -57,7 +57,7 @@ public final class OidcProviderConfigurationEndpointFilter extends OncePerReques
/**
* The default endpoint {@code URI} for OpenID Provider Configuration requests.
*/
private static final String DEFAULT_OIDC_PROVIDER_CONFIGURATION_ENDPOINT_URI = "/.well-known/openid-configuration";
private static final String DEFAULT_OIDC_PROVIDER_CONFIGURATION_ENDPOINT_URI = "/**/.well-known/openid-configuration";
private final RequestMatcher requestMatcher = new AntPathRequestMatcher(
DEFAULT_OIDC_PROVIDER_CONFIGURATION_ENDPOINT_URI,

4
oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/OAuth2AuthorizationServerMetadataEndpointFilter.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2020-2023 the original author or authors.
* Copyright 2020-2024 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.
@ -55,7 +55,7 @@ public final class OAuth2AuthorizationServerMetadataEndpointFilter extends OnceP @@ -55,7 +55,7 @@ public final class OAuth2AuthorizationServerMetadataEndpointFilter extends OnceP
/**
* The default endpoint {@code URI} for OAuth 2.0 Authorization Server Metadata requests.
*/
private static final String DEFAULT_OAUTH2_AUTHORIZATION_SERVER_METADATA_ENDPOINT_URI = "/.well-known/oauth-authorization-server";
private static final String DEFAULT_OAUTH2_AUTHORIZATION_SERVER_METADATA_ENDPOINT_URI = "/.well-known/oauth-authorization-server/**";
private final RequestMatcher requestMatcher = new AntPathRequestMatcher(
DEFAULT_OAUTH2_AUTHORIZATION_SERVER_METADATA_ENDPOINT_URI,

9
oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/OAuth2DeviceAuthorizationEndpointFilter.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2020-2023 the original author or authors.
* Copyright 2020-2024 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.
@ -40,13 +40,13 @@ import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames; @@ -40,13 +40,13 @@ import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
import org.springframework.security.oauth2.core.http.converter.OAuth2DeviceAuthorizationResponseHttpMessageConverter;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2DeviceAuthorizationRequestAuthenticationProvider;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2DeviceAuthorizationRequestAuthenticationToken;
import org.springframework.security.oauth2.server.authorization.context.AuthorizationServerContextHolder;
import org.springframework.security.oauth2.server.authorization.web.authentication.OAuth2DeviceAuthorizationRequestAuthenticationConverter;
import org.springframework.security.oauth2.server.authorization.web.authentication.OAuth2ErrorAuthenticationFailureHandler;
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.authentication.WebAuthenticationDetailsSource;
import org.springframework.security.web.util.UrlUtils;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.util.Assert;
@ -199,9 +199,8 @@ public final class OAuth2DeviceAuthorizationEndpointFilter extends OncePerReques @@ -199,9 +199,8 @@ public final class OAuth2DeviceAuthorizationEndpointFilter extends OncePerReques
OAuth2UserCode userCode = deviceAuthorizationRequestAuthentication.getUserCode();
// Generate the fully-qualified verification URI
String issuerUri = AuthorizationServerContextHolder.getContext().getIssuer();
UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.fromHttpUrl(issuerUri)
.path(this.verificationUri);
UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.fromHttpUrl(UrlUtils.buildFullRequestUrl(request))
.replacePath(this.verificationUri);
String verificationUri = uriComponentsBuilder.build().toUriString();
// @formatter:off
String verificationUriComplete = uriComponentsBuilder

133
oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/AuthorizationServerContextFilterTests.java

@ -0,0 +1,133 @@ @@ -0,0 +1,133 @@
/*
* Copyright 2020-2024 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.config.annotation.web.configurers;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;
import jakarta.servlet.FilterChain;
import org.junit.jupiter.api.Test;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.security.oauth2.server.authorization.context.AuthorizationServerContextHolder;
import org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link AuthorizationServerContextFilter}.
*
* @author Joe Grandja
*/
class AuthorizationServerContextFilterTests {
private static final String SCHEME = "https";
private static final String HOST = "example.com";
private static final int PORT = 8443;
private static final String DEFAULT_ISSUER = SCHEME + "://" + HOST + ":" + PORT;
private AuthorizationServerContextFilter filter;
@Test
public void doFilterWhenDefaultEndpointsThenIssuerResolved() throws Exception {
AuthorizationServerSettings authorizationServerSettings = AuthorizationServerSettings.builder().build();
this.filter = new AuthorizationServerContextFilter(authorizationServerSettings);
String issuerPath = "/issuer1";
String issuerWithPath = DEFAULT_ISSUER.concat(issuerPath);
Set<String> endpointUris = getEndpointUris(authorizationServerSettings);
for (String endpointUri : endpointUris) {
assertResolvedIssuer(issuerPath.concat(endpointUri), issuerWithPath);
}
}
@Test
public void doFilterWhenCustomEndpointsThenIssuerResolved() throws Exception {
AuthorizationServerSettings authorizationServerSettings = AuthorizationServerSettings.builder()
.authorizationEndpoint("/oauth2/v1/authorize")
.deviceAuthorizationEndpoint("/oauth2/v1/device_authorization")
.deviceVerificationEndpoint("/oauth2/v1/device_verification")
.tokenEndpoint("/oauth2/v1/token")
.jwkSetEndpoint("/oauth2/v1/jwks")
.tokenRevocationEndpoint("/oauth2/v1/revoke")
.tokenIntrospectionEndpoint("/oauth2/v1/introspect")
.oidcClientRegistrationEndpoint("/connect/v1/register")
.oidcUserInfoEndpoint("/v1/userinfo")
.oidcLogoutEndpoint("/connect/v1/logout")
.build();
this.filter = new AuthorizationServerContextFilter(authorizationServerSettings);
String issuerPath = "/issuer2";
String issuerWithPath = DEFAULT_ISSUER.concat(issuerPath);
Set<String> endpointUris = getEndpointUris(authorizationServerSettings);
for (String endpointUri : endpointUris) {
assertResolvedIssuer(issuerPath.concat(endpointUri), issuerWithPath);
}
}
@Test
public void doFilterWhenIssuerHasMultiplePathsThenIssuerResolved() throws Exception {
AuthorizationServerSettings authorizationServerSettings = AuthorizationServerSettings.builder().build();
this.filter = new AuthorizationServerContextFilter(authorizationServerSettings);
String issuerPath = "/path1/path2/issuer3";
String issuerWithPath = DEFAULT_ISSUER.concat(issuerPath);
Set<String> endpointUris = getEndpointUris(authorizationServerSettings);
for (String endpointUri : endpointUris) {
assertResolvedIssuer(issuerPath.concat(endpointUri), issuerWithPath);
}
}
private void assertResolvedIssuer(String requestUri, String expectedIssuer) throws Exception {
MockHttpServletRequest request = createRequest(requestUri);
MockHttpServletResponse response = new MockHttpServletResponse();
AtomicReference<String> resolvedIssuer = new AtomicReference<>();
FilterChain filterChain = (req, resp) ->
resolvedIssuer.set(AuthorizationServerContextHolder.getContext().getIssuer());
this.filter.doFilter(request, response, filterChain);
assertThat(resolvedIssuer.get()).isEqualTo(expectedIssuer);
}
private static Set<String> getEndpointUris(AuthorizationServerSettings authorizationServerSettings) {
Set<String> endpointUris = new HashSet<>();
endpointUris.add("/.well-known/oauth-authorization-server");
endpointUris.add("/.well-known/openid-configuration");
for (Map.Entry<String, Object> setting : authorizationServerSettings.getSettings().entrySet()) {
if (setting.getKey().endsWith("-endpoint")) {
endpointUris.add((String) setting.getValue());
}
}
return endpointUris;
}
private static MockHttpServletRequest createRequest(String requestUri) {
MockHttpServletRequest request = new MockHttpServletRequest();
request.setRequestURI(requestUri);
request.setScheme(SCHEME);
request.setServerName(HOST);
request.setServerPort(PORT);
return request;
}
}

10
oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/JwkSetTests.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2020-2022 the original author or authors.
* Copyright 2020-2024 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.
@ -112,6 +112,14 @@ public class JwkSetTests { @@ -112,6 +112,14 @@ public class JwkSetTests {
assertJwkSetRequestThenReturnKeys(authorizationServerSettings.getJwkSetEndpoint());
}
@Test
public void requestWhenJwkSetRequestIncludesIssuerPathThenReturnKeys() throws Exception {
this.spring.register(AuthorizationServerConfigurationCustomEndpoints.class).autowire();
String issuer = "https://example.com:8443/issuer1";
assertJwkSetRequestThenReturnKeys(issuer.concat(authorizationServerSettings.getJwkSetEndpoint()));
}
private void assertJwkSetRequestThenReturnKeys(String jwkSetEndpointUri) throws Exception {
this.mvc.perform(get(jwkSetEndpointUri))
.andExpect(status().isOk())

42
oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2AuthorizationCodeGrantTests.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2020-2023 the original author or authors.
* Copyright 2020-2024 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.
@ -826,6 +826,46 @@ public class OAuth2AuthorizationCodeGrantTests { @@ -826,6 +826,46 @@ public class OAuth2AuthorizationCodeGrantTests {
assertThat(securityContext.getAuthentication()).isNull();
}
@Test
public void requestWhenAuthorizationAndTokenRequestIncludesIssuerPathThenIssuerResolvedWithPath() throws Exception {
this.spring.register(AuthorizationServerConfigurationWithTokenGenerator.class).autowire();
RegisteredClient registeredClient = TestRegisteredClients.registeredPublicClient().build();
this.registeredClientRepository.save(registeredClient);
String issuer = "https://example.com:8443/issuer1";
MvcResult mvcResult = this.mvc.perform(get(issuer.concat(DEFAULT_AUTHORIZATION_ENDPOINT_URI))
.queryParams(getAuthorizationRequestParameters(registeredClient))
.queryParam(PkceParameterNames.CODE_CHALLENGE, S256_CODE_CHALLENGE)
.queryParam(PkceParameterNames.CODE_CHALLENGE_METHOD, "S256")
.with(user("user")))
.andExpect(status().is3xxRedirection())
.andReturn();
String authorizationCode = extractParameterFromRedirectUri(mvcResult.getResponse().getRedirectedUrl(), "code");
OAuth2Authorization authorizationCodeAuthorization = this.authorizationService.findByToken(authorizationCode, AUTHORIZATION_CODE_TOKEN_TYPE);
this.mvc.perform(post(issuer.concat(DEFAULT_TOKEN_ENDPOINT_URI))
.params(getTokenRequestParameters(registeredClient, authorizationCodeAuthorization))
.param(OAuth2ParameterNames.CLIENT_ID, registeredClient.getClientId())
.param(PkceParameterNames.CODE_VERIFIER, S256_CODE_VERIFIER))
.andExpect(header().string(HttpHeaders.CACHE_CONTROL, containsString("no-store")))
.andExpect(header().string(HttpHeaders.PRAGMA, containsString("no-cache")))
.andExpect(status().isOk())
.andExpect(jsonPath("$.access_token").isNotEmpty())
.andExpect(jsonPath("$.token_type").isNotEmpty())
.andExpect(jsonPath("$.expires_in").isNotEmpty())
.andExpect(jsonPath("$.refresh_token").doesNotExist())
.andExpect(jsonPath("$.scope").isNotEmpty())
.andReturn();
ArgumentCaptor<OAuth2TokenContext> tokenContextCaptor = ArgumentCaptor.forClass(OAuth2TokenContext.class);
verify(tokenGenerator).generate(tokenContextCaptor.capture());
OAuth2TokenContext tokenContext = tokenContextCaptor.getValue();
assertThat(tokenContext.getAuthorizationServerContext().getIssuer()).isEqualTo(issuer);
}
private static MultiValueMap<String, String> getAuthorizationRequestParameters(RegisteredClient registeredClient) {
MultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();
parameters.set(OAuth2ParameterNames.RESPONSE_TYPE, OAuth2AuthorizationResponseType.CODE.getValue());

36
oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2AuthorizationServerMetadataTests.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2020-2023 the original author or authors.
* Copyright 2020-2024 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.
@ -65,7 +65,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. @@ -65,7 +65,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
@ExtendWith(SpringTestContextExtension.class)
public class OAuth2AuthorizationServerMetadataTests {
private static final String DEFAULT_OAUTH2_AUTHORIZATION_SERVER_METADATA_ENDPOINT_URI = "/.well-known/oauth-authorization-server";
private static final String ISSUER_URL = "https://example.com";
private static final String ISSUER = "https://example.com";
private static EmbeddedDatabase db;
private static JWKSource<SecurityContext> jwkSource;
@ -105,19 +105,37 @@ public class OAuth2AuthorizationServerMetadataTests { @@ -105,19 +105,37 @@ public class OAuth2AuthorizationServerMetadataTests {
public void requestWhenAuthorizationServerMetadataRequestAndIssuerSetThenUsed() throws Exception {
this.spring.register(AuthorizationServerConfiguration.class).autowire();
this.mvc.perform(get(ISSUER_URL.concat(DEFAULT_OAUTH2_AUTHORIZATION_SERVER_METADATA_ENDPOINT_URI)))
this.mvc.perform(get(ISSUER.concat(DEFAULT_OAUTH2_AUTHORIZATION_SERVER_METADATA_ENDPOINT_URI)))
.andExpect(status().is2xxSuccessful())
.andExpect(jsonPath("issuer").value(ISSUER_URL))
.andExpect(jsonPath("issuer").value(ISSUER))
.andReturn();
}
@Test
public void requestWhenAuthorizationServerMetadataRequestAndIssuerNotSetThenResolveFromRequest() throws Exception {
public void requestWhenAuthorizationServerMetadataRequestIncludesIssuerPathThenMetadataResponseHasIssuerPath() throws Exception {
this.spring.register(AuthorizationServerConfigurationWithIssuerNotSet.class).autowire();
this.mvc.perform(get("http://localhost".concat(DEFAULT_OAUTH2_AUTHORIZATION_SERVER_METADATA_ENDPOINT_URI)))
String host = "https://example.com:8443";
String issuerPath = "/issuer1";
String issuer = host.concat(issuerPath);
this.mvc.perform(get(host.concat(DEFAULT_OAUTH2_AUTHORIZATION_SERVER_METADATA_ENDPOINT_URI).concat(issuerPath)))
.andExpect(status().is2xxSuccessful())
.andExpect(jsonPath("issuer").value(issuer))
.andReturn();
issuerPath = "/path1/issuer2";
issuer = host.concat(issuerPath);
this.mvc.perform(get(host.concat(DEFAULT_OAUTH2_AUTHORIZATION_SERVER_METADATA_ENDPOINT_URI).concat(issuerPath)))
.andExpect(status().is2xxSuccessful())
.andExpect(jsonPath("issuer").value(issuer))
.andReturn();
issuerPath = "/path1/path2/issuer3";
issuer = host.concat(issuerPath);
this.mvc.perform(get(host.concat(DEFAULT_OAUTH2_AUTHORIZATION_SERVER_METADATA_ENDPOINT_URI).concat(issuerPath)))
.andExpect(status().is2xxSuccessful())
.andExpect(jsonPath("issuer").value("http://localhost"))
.andExpect(jsonPath("issuer").value(issuer))
.andReturn();
}
@ -126,7 +144,7 @@ public class OAuth2AuthorizationServerMetadataTests { @@ -126,7 +144,7 @@ public class OAuth2AuthorizationServerMetadataTests {
public void requestWhenAuthorizationServerMetadataRequestAndMetadataCustomizerSetThenReturnCustomMetadataResponse() throws Exception {
this.spring.register(AuthorizationServerConfigurationWithMetadataCustomizer.class).autowire();
this.mvc.perform(get(ISSUER_URL.concat(DEFAULT_OAUTH2_AUTHORIZATION_SERVER_METADATA_ENDPOINT_URI)))
this.mvc.perform(get(ISSUER.concat(DEFAULT_OAUTH2_AUTHORIZATION_SERVER_METADATA_ENDPOINT_URI)))
.andExpect(status().is2xxSuccessful())
.andExpect(jsonPath(OAuth2AuthorizationServerMetadataClaimNames.SCOPES_SUPPORTED,
hasItems("scope1", "scope2")));
@ -156,7 +174,7 @@ public class OAuth2AuthorizationServerMetadataTests { @@ -156,7 +174,7 @@ public class OAuth2AuthorizationServerMetadataTests {
@Bean
AuthorizationServerSettings authorizationServerSettings() {
return AuthorizationServerSettings.builder().issuer(ISSUER_URL).build();
return AuthorizationServerSettings.builder().issuer(ISSUER).build();
}
}

26
oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2ClientCredentialsGrantTests.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2020-2023 the original author or authors.
* Copyright 2020-2024 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.
@ -357,6 +357,30 @@ public class OAuth2ClientCredentialsGrantTests { @@ -357,6 +357,30 @@ public class OAuth2ClientCredentialsGrantTests {
verify(authenticationSuccessHandler).onAuthenticationSuccess(any(), any(), eq(clientPrincipal));
}
@Test
public void requestWhenTokenRequestIncludesIssuerPathThenIssuerResolvedWithPath() throws Exception {
this.spring.register(AuthorizationServerConfiguration.class).autowire();
RegisteredClient registeredClient = TestRegisteredClients.registeredClient2().build();
this.registeredClientRepository.save(registeredClient);
String issuer = "https://example.com:8443/issuer1";
this.mvc.perform(post(issuer.concat(DEFAULT_TOKEN_ENDPOINT_URI))
.param(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.CLIENT_CREDENTIALS.getValue())
.param(OAuth2ParameterNames.SCOPE, "scope1 scope2")
.header(HttpHeaders.AUTHORIZATION, "Basic " + encodeBasicAuth(
registeredClient.getClientId(), registeredClient.getClientSecret())))
.andExpect(status().isOk())
.andExpect(jsonPath("$.access_token").isNotEmpty())
.andExpect(jsonPath("$.scope").value("scope1 scope2"));
ArgumentCaptor<JwtEncodingContext> jwtEncodingContextCaptor = ArgumentCaptor.forClass(JwtEncodingContext.class);
verify(jwtCustomizer).customize(jwtEncodingContextCaptor.capture());
JwtEncodingContext jwtEncodingContext = jwtEncodingContextCaptor.getValue();
assertThat(jwtEncodingContext.getAuthorizationServerContext().getIssuer()).isEqualTo(issuer);
}
private static String encodeBasicAuth(String clientId, String secret) throws Exception {
clientId = URLEncoder.encode(clientId, StandardCharsets.UTF_8.name());
secret = URLEncoder.encode(secret, StandardCharsets.UTF_8.name());

14
oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2DeviceCodeGrantTests.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2020-2023 the original author or authors.
* Copyright 2020-2024 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.
@ -218,8 +218,10 @@ public class OAuth2DeviceCodeGrantTests { @@ -218,8 +218,10 @@ public class OAuth2DeviceCodeGrantTests {
parameters.set(OAuth2ParameterNames.SCOPE,
StringUtils.collectionToDelimitedString(registeredClient.getScopes(), " "));
String issuer = "https://example.com:8443/issuer1";
// @formatter:off
MvcResult mvcResult = this.mvc.perform(post(DEFAULT_DEVICE_AUTHORIZATION_ENDPOINT_URI)
MvcResult mvcResult = this.mvc.perform(post(issuer.concat(DEFAULT_DEVICE_AUTHORIZATION_ENDPOINT_URI))
.params(parameters)
.headers(withClientAuth(registeredClient)))
.andExpect(status().isOk())
@ -240,9 +242,9 @@ public class OAuth2DeviceCodeGrantTests { @@ -240,9 +242,9 @@ public class OAuth2DeviceCodeGrantTests {
String userCode = deviceAuthorizationResponse.getUserCode().getTokenValue();
assertThat(userCode).matches("[A-Z]{4}-[A-Z]{4}");
assertThat(deviceAuthorizationResponse.getVerificationUri())
.isEqualTo("http://localhost/oauth2/device_verification");
.isEqualTo("https://example.com:8443/oauth2/device_verification");
assertThat(deviceAuthorizationResponse.getVerificationUriComplete())
.isEqualTo("http://localhost/oauth2/device_verification?user_code=" + userCode);
.isEqualTo("https://example.com:8443/oauth2/device_verification?user_code=" + userCode);
String deviceCode = deviceAuthorizationResponse.getDeviceCode().getTokenValue();
OAuth2Authorization authorization = this.authorizationService.findByToken(deviceCode, DEVICE_CODE_TOKEN_TYPE);
@ -311,8 +313,10 @@ public class OAuth2DeviceCodeGrantTests { @@ -311,8 +313,10 @@ public class OAuth2DeviceCodeGrantTests {
MultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();
parameters.set(OAuth2ParameterNames.USER_CODE, USER_CODE);
String issuer = "https://example.com:8443/issuer1";
// @formatter:off
MvcResult mvcResult = this.mvc.perform(get(DEFAULT_DEVICE_VERIFICATION_ENDPOINT_URI)
MvcResult mvcResult = this.mvc.perform(get(issuer.concat(DEFAULT_DEVICE_VERIFICATION_ENDPOINT_URI))
.queryParams(parameters)
.with(user("user")))
.andExpect(status().isOk())

51
oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2TokenIntrospectionTests.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2020-2022 the original author or authors.
* Copyright 2020-2024 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.
@ -30,6 +30,7 @@ import java.util.function.Consumer; @@ -30,6 +30,7 @@ import java.util.function.Consumer;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
@ -107,6 +108,7 @@ import static org.assertj.core.api.Assertions.assertThat; @@ -107,6 +108,7 @@ import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
@ -167,6 +169,18 @@ public class OAuth2TokenIntrospectionTests { @@ -167,6 +169,18 @@ public class OAuth2TokenIntrospectionTests {
.build();
}
@SuppressWarnings("unchecked")
@BeforeEach
public void setup() {
reset(authenticationConverter);
reset(authenticationConvertersConsumer);
reset(authenticationProvider);
reset(authenticationProvidersConsumer);
reset(authenticationSuccessHandler);
reset(authenticationFailureHandler);
reset(accessTokenCustomizer);
}
@AfterEach
public void tearDown() {
jdbcOperations.update("truncate table oauth2_authorization");
@ -395,6 +409,41 @@ public class OAuth2TokenIntrospectionTests { @@ -395,6 +409,41 @@ public class OAuth2TokenIntrospectionTests {
verify(authenticationSuccessHandler).onAuthenticationSuccess(any(), any(), eq(tokenIntrospectionAuthentication));
}
@Test
public void requestWhenIntrospectionRequestIncludesIssuerPathThenActive() throws Exception {
this.spring.register(AuthorizationServerConfigurationCustomTokenIntrospectionEndpoint.class).autowire();
RegisteredClient introspectRegisteredClient = TestRegisteredClients.registeredClient2().build();
this.registeredClientRepository.save(introspectRegisteredClient);
RegisteredClient authorizedRegisteredClient = TestRegisteredClients.registeredClient().build();
this.registeredClientRepository.save(authorizedRegisteredClient);
OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(authorizedRegisteredClient).build();
this.authorizationService.save(authorization);
OAuth2AccessToken accessToken = authorization.getAccessToken().getToken();
Authentication clientPrincipal = new OAuth2ClientAuthenticationToken(
introspectRegisteredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, introspectRegisteredClient.getClientSecret());
OAuth2TokenIntrospectionAuthenticationToken tokenIntrospectionAuthentication =
new OAuth2TokenIntrospectionAuthenticationToken(
accessToken.getTokenValue(), clientPrincipal, null, null);
when(authenticationConverter.convert(any())).thenReturn(tokenIntrospectionAuthentication);
when(authenticationProvider.supports(eq(OAuth2TokenIntrospectionAuthenticationToken.class))).thenReturn(true);
when(authenticationProvider.authenticate(any())).thenReturn(tokenIntrospectionAuthentication);
String issuer = "https://example.com:8443/issuer1";
// @formatter:off
this.mvc.perform(post(issuer.concat(authorizationServerSettings.getTokenIntrospectionEndpoint()))
.params(getTokenIntrospectionRequestParameters(accessToken, OAuth2TokenType.ACCESS_TOKEN))
.header(HttpHeaders.AUTHORIZATION, getAuthorizationHeader(introspectRegisteredClient)))
.andExpect(status().isOk());
// @formatter:on
}
private static MultiValueMap<String, String> getTokenIntrospectionRequestParameters(OAuth2Token token,
OAuth2TokenType tokenType) {
MultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();

31
oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2TokenRevocationTests.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2020-2022 the original author or authors.
* Copyright 2020-2024 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.
@ -201,6 +201,35 @@ public class OAuth2TokenRevocationTests { @@ -201,6 +201,35 @@ public class OAuth2TokenRevocationTests {
assertThat(refreshToken.isInvalidated()).isFalse();
}
@Test
public void requestWhenRevokeAccessTokenAndRequestIncludesIssuerPathThenRevoked() throws Exception {
this.spring.register(AuthorizationServerConfiguration.class).autowire();
RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();
this.registeredClientRepository.save(registeredClient);
OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient).build();
OAuth2AccessToken token = authorization.getAccessToken().getToken();
OAuth2TokenType tokenType = OAuth2TokenType.ACCESS_TOKEN;
this.authorizationService.save(authorization);
String issuer = "https://example.com:8443/issuer1";
// @formatter:off
this.mvc.perform(post(issuer.concat(DEFAULT_TOKEN_REVOCATION_ENDPOINT_URI))
.params(getTokenRevocationRequestParameters(token, tokenType))
.header(HttpHeaders.AUTHORIZATION, "Basic " + encodeBasicAuth(
registeredClient.getClientId(), registeredClient.getClientSecret())))
.andExpect(status().isOk());
// @formatter:on
OAuth2Authorization updatedAuthorization = this.authorizationService.findById(authorization.getId());
OAuth2Authorization.Token<OAuth2AccessToken> accessToken = updatedAuthorization.getAccessToken();
assertThat(accessToken.isInvalidated()).isTrue();
OAuth2Authorization.Token<OAuth2RefreshToken> refreshToken = updatedAuthorization.getRefreshToken();
assertThat(refreshToken.isInvalidated()).isFalse();
}
@Test
public void requestWhenTokenRevocationEndpointCustomizedThenUsed() throws Exception {
this.spring.register(AuthorizationServerConfigurationCustomTokenRevocationEndpoint.class).autowire();

18
oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OidcClientRegistrationTests.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2020-2023 the original author or authors.
* Copyright 2020-2024 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.
@ -137,6 +137,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. @@ -137,6 +137,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
*/
@ExtendWith(SpringTestContextExtension.class)
public class OidcClientRegistrationTests {
private static final String ISSUER = "https://example.com:8443/issuer1";
private static final String DEFAULT_TOKEN_ENDPOINT_URI = "/oauth2/token";
private static final String DEFAULT_OIDC_CLIENT_REGISTRATION_ENDPOINT_URI = "/connect/register";
private static final HttpMessageConverter<OAuth2AccessTokenResponse> accessTokenHttpResponseConverter =
@ -374,7 +375,7 @@ public class OidcClientRegistrationTests { @@ -374,7 +375,7 @@ public class OidcClientRegistrationTests {
when(authenticationProvider.authenticate(any())).thenThrow(new OAuth2AuthenticationException("error"));
this.mvc.perform(get(DEFAULT_OIDC_CLIENT_REGISTRATION_ENDPOINT_URI)
this.mvc.perform(get(ISSUER.concat(DEFAULT_OIDC_CLIENT_REGISTRATION_ENDPOINT_URI))
.param(OAuth2ParameterNames.CLIENT_ID, "invalid").with(jwt()));
verify(authenticationFailureHandler).onAuthenticationFailure(any(), any(), any());
@ -399,7 +400,7 @@ public class OidcClientRegistrationTests { @@ -399,7 +400,7 @@ public class OidcClientRegistrationTests {
OidcClientRegistration clientRegistrationResponse = registerClient(clientRegistration);
this.mvc.perform(post(DEFAULT_TOKEN_ENDPOINT_URI)
this.mvc.perform(post(ISSUER.concat(DEFAULT_TOKEN_ENDPOINT_URI))
.param(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.CLIENT_CREDENTIALS.getValue())
.param(OAuth2ParameterNames.SCOPE, "scope1")
.with(httpBasic(clientRegistrationResponse.getClientId(), clientRegistrationResponse.getClientSecret())))
@ -436,7 +437,7 @@ public class OidcClientRegistrationTests { @@ -436,7 +437,7 @@ public class OidcClientRegistrationTests {
JwtClaimsSet jwtClaimsSet = JwtClaimsSet.builder()
.issuer(clientRegistrationResponse.getClientId())
.subject(clientRegistrationResponse.getClientId())
.audience(Collections.singletonList(asUrl(this.authorizationServerSettings.getIssuer(), this.authorizationServerSettings.getTokenEndpoint())))
.audience(Collections.singletonList(asUrl(ISSUER, this.authorizationServerSettings.getTokenEndpoint())))
.issuedAt(issuedAt)
.expiresAt(expiresAt)
.build();
@ -447,7 +448,7 @@ public class OidcClientRegistrationTests { @@ -447,7 +448,7 @@ public class OidcClientRegistrationTests {
Jwt jwtAssertion = jwtClientAssertionEncoder.encode(JwtEncoderParameters.from(jwsHeader, jwtClaimsSet));
this.mvc.perform(post(DEFAULT_TOKEN_ENDPOINT_URI)
this.mvc.perform(post(ISSUER.concat(DEFAULT_TOKEN_ENDPOINT_URI))
.param(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.CLIENT_CREDENTIALS.getValue())
.param(OAuth2ParameterNames.SCOPE, "scope1")
.param(OAuth2ParameterNames.CLIENT_ASSERTION_TYPE, "urn:ietf:params:oauth:client-assertion-type:jwt-bearer")
@ -520,7 +521,7 @@ public class OidcClientRegistrationTests { @@ -520,7 +521,7 @@ public class OidcClientRegistrationTests {
// @formatter:on
Jwt jwtAssertion = jwtClientAssertionEncoder.encode(JwtEncoderParameters.from(jwsHeader, jwtClaimsSet));
MvcResult mvcResult = this.mvc.perform(post(DEFAULT_TOKEN_ENDPOINT_URI)
MvcResult mvcResult = this.mvc.perform(post(ISSUER.concat(DEFAULT_TOKEN_ENDPOINT_URI))
.param(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.CLIENT_CREDENTIALS.getValue())
.param(OAuth2ParameterNames.SCOPE, clientRegistrationScope)
.param(OAuth2ParameterNames.CLIENT_ASSERTION_TYPE, "urn:ietf:params:oauth:client-assertion-type:jwt-bearer")
@ -539,7 +540,7 @@ public class OidcClientRegistrationTests { @@ -539,7 +540,7 @@ public class OidcClientRegistrationTests {
httpHeaders.setBearerAuth(accessToken.getTokenValue());
// Register the client
mvcResult = this.mvc.perform(post(DEFAULT_OIDC_CLIENT_REGISTRATION_ENDPOINT_URI)
mvcResult = this.mvc.perform(post(ISSUER.concat(DEFAULT_OIDC_CLIENT_REGISTRATION_ENDPOINT_URI))
.headers(httpHeaders)
.contentType(MediaType.APPLICATION_JSON)
.content(getClientRegistrationRequestContent(clientRegistration)))
@ -557,7 +558,7 @@ public class OidcClientRegistrationTests { @@ -557,7 +558,7 @@ public class OidcClientRegistrationTests {
return JwtClaimsSet.builder()
.issuer(registeredClient.getClientId())
.subject(registeredClient.getClientId())
.audience(Collections.singletonList(asUrl(this.authorizationServerSettings.getIssuer(), this.authorizationServerSettings.getTokenEndpoint())))
.audience(Collections.singletonList(asUrl(ISSUER, this.authorizationServerSettings.getTokenEndpoint())))
.issuedAt(issuedAt)
.expiresAt(expiresAt);
}
@ -734,7 +735,6 @@ public class OidcClientRegistrationTests { @@ -734,7 +735,6 @@ public class OidcClientRegistrationTests {
@Bean
AuthorizationServerSettings authorizationServerSettings() {
return AuthorizationServerSettings.builder()
.issuer("https://auth-server:9000")
.build();
}

81
oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OidcProviderConfigurationTests.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2020-2023 the original author or authors.
* Copyright 2020-2024 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.
@ -63,7 +63,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. @@ -63,7 +63,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
@ExtendWith(SpringTestContextExtension.class)
public class OidcProviderConfigurationTests {
private static final String DEFAULT_OIDC_PROVIDER_CONFIGURATION_ENDPOINT_URI = "/.well-known/openid-configuration";
private static final String ISSUER_URL = "https://example.com";
private static final String ISSUER = "https://example.com";
public final SpringTestContext spring = new SpringTestContext();
@ -77,9 +77,29 @@ public class OidcProviderConfigurationTests { @@ -77,9 +77,29 @@ public class OidcProviderConfigurationTests {
public void requestWhenConfigurationRequestAndIssuerSetThenReturnDefaultConfigurationResponse() throws Exception {
this.spring.register(AuthorizationServerConfiguration.class).autowire();
this.mvc.perform(get(ISSUER_URL.concat(DEFAULT_OIDC_PROVIDER_CONFIGURATION_ENDPOINT_URI)))
this.mvc.perform(get(ISSUER.concat(DEFAULT_OIDC_PROVIDER_CONFIGURATION_ENDPOINT_URI)))
.andExpect(status().is2xxSuccessful())
.andExpectAll(defaultConfigurationMatchers());
.andExpectAll(defaultConfigurationMatchers(ISSUER));
}
@Test
public void requestWhenConfigurationRequestIncludesIssuerPathThenConfigurationResponseHasIssuerPath() throws Exception {
this.spring.register(AuthorizationServerConfigurationWithIssuerNotSet.class).autowire();
String issuer = "https://example.com:8443/issuer1";
this.mvc.perform(get(issuer.concat(DEFAULT_OIDC_PROVIDER_CONFIGURATION_ENDPOINT_URI)))
.andExpect(status().is2xxSuccessful())
.andExpectAll(defaultConfigurationMatchers(issuer));
issuer = "https://example.com:8443/path1/issuer2";
this.mvc.perform(get(issuer.concat(DEFAULT_OIDC_PROVIDER_CONFIGURATION_ENDPOINT_URI)))
.andExpect(status().is2xxSuccessful())
.andExpectAll(defaultConfigurationMatchers(issuer));
issuer = "https://example.com:8443/path1/path2/issuer3";
this.mvc.perform(get(issuer.concat(DEFAULT_OIDC_PROVIDER_CONFIGURATION_ENDPOINT_URI)))
.andExpect(status().is2xxSuccessful())
.andExpectAll(defaultConfigurationMatchers(issuer));
}
// gh-632
@ -87,10 +107,10 @@ public class OidcProviderConfigurationTests { @@ -87,10 +107,10 @@ public class OidcProviderConfigurationTests {
public void requestWhenConfigurationRequestAndUserAuthenticatedThenReturnConfigurationResponse() throws Exception {
this.spring.register(AuthorizationServerConfiguration.class).autowire();
this.mvc.perform(get(ISSUER_URL.concat(DEFAULT_OIDC_PROVIDER_CONFIGURATION_ENDPOINT_URI))
this.mvc.perform(get(ISSUER.concat(DEFAULT_OIDC_PROVIDER_CONFIGURATION_ENDPOINT_URI))
.with(user("user")))
.andExpect(status().is2xxSuccessful())
.andExpectAll(defaultConfigurationMatchers());
.andExpectAll(defaultConfigurationMatchers(ISSUER));
}
// gh-616
@ -98,7 +118,7 @@ public class OidcProviderConfigurationTests { @@ -98,7 +118,7 @@ public class OidcProviderConfigurationTests {
public void requestWhenConfigurationRequestAndConfigurationCustomizerSetThenReturnCustomConfigurationResponse() throws Exception {
this.spring.register(AuthorizationServerConfigurationWithProviderConfigurationCustomizer.class).autowire();
this.mvc.perform(get(ISSUER_URL.concat(DEFAULT_OIDC_PROVIDER_CONFIGURATION_ENDPOINT_URI)))
this.mvc.perform(get(ISSUER.concat(DEFAULT_OIDC_PROVIDER_CONFIGURATION_ENDPOINT_URI)))
.andExpect(status().is2xxSuccessful())
.andExpect(jsonPath(OAuth2AuthorizationServerMetadataClaimNames.SCOPES_SUPPORTED,
hasItems(OidcScopes.OPENID, OidcScopes.PROFILE, OidcScopes.EMAIL)));
@ -108,35 +128,35 @@ public class OidcProviderConfigurationTests { @@ -108,35 +128,35 @@ public class OidcProviderConfigurationTests {
public void requestWhenConfigurationRequestAndClientRegistrationEnabledThenConfigurationResponseIncludesRegistrationEndpoint() throws Exception {
this.spring.register(AuthorizationServerConfigurationWithClientRegistrationEnabled.class).autowire();
this.mvc.perform(get(ISSUER_URL.concat(DEFAULT_OIDC_PROVIDER_CONFIGURATION_ENDPOINT_URI)))
this.mvc.perform(get(ISSUER.concat(DEFAULT_OIDC_PROVIDER_CONFIGURATION_ENDPOINT_URI)))
.andExpect(status().is2xxSuccessful())
.andExpectAll(defaultConfigurationMatchers())
.andExpect(jsonPath("$.registration_endpoint").value(ISSUER_URL.concat(this.authorizationServerSettings.getOidcClientRegistrationEndpoint())));
.andExpectAll(defaultConfigurationMatchers(ISSUER))
.andExpect(jsonPath("$.registration_endpoint").value(ISSUER.concat(this.authorizationServerSettings.getOidcClientRegistrationEndpoint())));
}
private ResultMatcher[] defaultConfigurationMatchers() {
private ResultMatcher[] defaultConfigurationMatchers(String issuer) {
// @formatter:off
return new ResultMatcher[] {
jsonPath("issuer").value(ISSUER_URL),
jsonPath("authorization_endpoint").value(ISSUER_URL.concat(this.authorizationServerSettings.getAuthorizationEndpoint())),
jsonPath("token_endpoint").value(ISSUER_URL.concat(this.authorizationServerSettings.getTokenEndpoint())),
jsonPath("issuer").value(issuer),
jsonPath("authorization_endpoint").value(issuer.concat(this.authorizationServerSettings.getAuthorizationEndpoint())),
jsonPath("token_endpoint").value(issuer.concat(this.authorizationServerSettings.getTokenEndpoint())),
jsonPath("$.token_endpoint_auth_methods_supported[0]").value(ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue()),
jsonPath("$.token_endpoint_auth_methods_supported[1]").value(ClientAuthenticationMethod.CLIENT_SECRET_POST.getValue()),
jsonPath("$.token_endpoint_auth_methods_supported[2]").value(ClientAuthenticationMethod.CLIENT_SECRET_JWT.getValue()),
jsonPath("$.token_endpoint_auth_methods_supported[3]").value(ClientAuthenticationMethod.PRIVATE_KEY_JWT.getValue()),
jsonPath("jwks_uri").value(ISSUER_URL.concat(this.authorizationServerSettings.getJwkSetEndpoint())),
jsonPath("userinfo_endpoint").value(ISSUER_URL.concat(this.authorizationServerSettings.getOidcUserInfoEndpoint())),
jsonPath("end_session_endpoint").value(ISSUER_URL.concat(this.authorizationServerSettings.getOidcLogoutEndpoint())),
jsonPath("jwks_uri").value(issuer.concat(this.authorizationServerSettings.getJwkSetEndpoint())),
jsonPath("userinfo_endpoint").value(issuer.concat(this.authorizationServerSettings.getOidcUserInfoEndpoint())),
jsonPath("end_session_endpoint").value(issuer.concat(this.authorizationServerSettings.getOidcLogoutEndpoint())),
jsonPath("response_types_supported").value(OAuth2AuthorizationResponseType.CODE.getValue()),
jsonPath("$.grant_types_supported[0]").value(AuthorizationGrantType.AUTHORIZATION_CODE.getValue()),
jsonPath("$.grant_types_supported[1]").value(AuthorizationGrantType.CLIENT_CREDENTIALS.getValue()),
jsonPath("$.grant_types_supported[2]").value(AuthorizationGrantType.REFRESH_TOKEN.getValue()),
jsonPath("revocation_endpoint").value(ISSUER_URL.concat(this.authorizationServerSettings.getTokenRevocationEndpoint())),
jsonPath("revocation_endpoint").value(issuer.concat(this.authorizationServerSettings.getTokenRevocationEndpoint())),
jsonPath("$.revocation_endpoint_auth_methods_supported[0]").value(ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue()),
jsonPath("$.revocation_endpoint_auth_methods_supported[1]").value(ClientAuthenticationMethod.CLIENT_SECRET_POST.getValue()),
jsonPath("$.revocation_endpoint_auth_methods_supported[2]").value(ClientAuthenticationMethod.CLIENT_SECRET_JWT.getValue()),
jsonPath("$.revocation_endpoint_auth_methods_supported[3]").value(ClientAuthenticationMethod.PRIVATE_KEY_JWT.getValue()),
jsonPath("introspection_endpoint").value(ISSUER_URL.concat(this.authorizationServerSettings.getTokenIntrospectionEndpoint())),
jsonPath("introspection_endpoint").value(issuer.concat(this.authorizationServerSettings.getTokenIntrospectionEndpoint())),
jsonPath("$.introspection_endpoint_auth_methods_supported[0]").value(ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue()),
jsonPath("$.introspection_endpoint_auth_methods_supported[1]").value(ClientAuthenticationMethod.CLIENT_SECRET_POST.getValue()),
jsonPath("$.introspection_endpoint_auth_methods_supported[2]").value(ClientAuthenticationMethod.CLIENT_SECRET_JWT.getValue()),
@ -218,7 +238,18 @@ public class OidcProviderConfigurationTests { @@ -218,7 +238,18 @@ public class OidcProviderConfigurationTests {
@Bean
AuthorizationServerSettings authorizationServerSettings() {
return AuthorizationServerSettings.builder()
.issuer(ISSUER_URL)
.issuer(ISSUER)
.build();
}
}
@EnableWebSecurity
static class AuthorizationServerConfigurationWithIssuerNotSet extends AuthorizationServerConfiguration {
@Bean
AuthorizationServerSettings authorizationServerSettings() {
return AuthorizationServerSettings.builder()
.build();
}
@ -306,7 +337,7 @@ public class OidcProviderConfigurationTests { @@ -306,7 +337,7 @@ public class OidcProviderConfigurationTests {
@Bean
AuthorizationServerSettings authorizationServerSettings() {
return AuthorizationServerSettings.builder().issuer(ISSUER_URL + "?param=value").build();
return AuthorizationServerSettings.builder().issuer(ISSUER + "?param=value").build();
}
}
@ -315,7 +346,7 @@ public class OidcProviderConfigurationTests { @@ -315,7 +346,7 @@ public class OidcProviderConfigurationTests {
@Bean
AuthorizationServerSettings authorizationServerSettings() {
return AuthorizationServerSettings.builder().issuer(ISSUER_URL + "#fragment").build();
return AuthorizationServerSettings.builder().issuer(ISSUER + "#fragment").build();
}
}
@ -324,7 +355,7 @@ public class OidcProviderConfigurationTests { @@ -324,7 +355,7 @@ public class OidcProviderConfigurationTests {
@Bean
AuthorizationServerSettings authorizationServerSettings() {
return AuthorizationServerSettings.builder().issuer(ISSUER_URL + "?param=value#fragment").build();
return AuthorizationServerSettings.builder().issuer(ISSUER + "?param=value#fragment").build();
}
}
@ -333,7 +364,7 @@ public class OidcProviderConfigurationTests { @@ -333,7 +364,7 @@ public class OidcProviderConfigurationTests {
@Bean
AuthorizationServerSettings authorizationServerSettings() {
return AuthorizationServerSettings.builder().issuer(ISSUER_URL + "?").build();
return AuthorizationServerSettings.builder().issuer(ISSUER + "?").build();
}
}
@ -342,7 +373,7 @@ public class OidcProviderConfigurationTests { @@ -342,7 +373,7 @@ public class OidcProviderConfigurationTests {
@Bean
AuthorizationServerSettings authorizationServerSettings() {
return AuthorizationServerSettings.builder().issuer(ISSUER_URL + "#").build();
return AuthorizationServerSettings.builder().issuer(ISSUER + "#").build();
}
}

10
oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OidcTests.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2020-2023 the original author or authors.
* Copyright 2020-2024 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.
@ -303,9 +303,11 @@ public class OidcTests { @@ -303,9 +303,11 @@ public class OidcTests {
RegisteredClient registeredClient = TestRegisteredClients.registeredClient().scope(OidcScopes.OPENID).build();
this.registeredClientRepository.save(registeredClient);
String issuer = "https://example.com:8443/issuer1";
// Login
MultiValueMap<String, String> authorizationRequestParameters = getAuthorizationRequestParameters(registeredClient);
MvcResult mvcResult = this.mvc.perform(get(DEFAULT_AUTHORIZATION_ENDPOINT_URI)
MvcResult mvcResult = this.mvc.perform(get(issuer.concat(DEFAULT_AUTHORIZATION_ENDPOINT_URI))
.queryParams(authorizationRequestParameters)
.with(user("user")))
.andExpect(status().is3xxRedirection())
@ -319,7 +321,7 @@ public class OidcTests { @@ -319,7 +321,7 @@ public class OidcTests {
OAuth2Authorization authorization = this.authorizationService.findByToken(authorizationCode, AUTHORIZATION_CODE_TOKEN_TYPE);
// Get ID Token
mvcResult = this.mvc.perform(post(DEFAULT_TOKEN_ENDPOINT_URI)
mvcResult = this.mvc.perform(post(issuer.concat(DEFAULT_TOKEN_ENDPOINT_URI))
.params(getTokenRequestParameters(registeredClient, authorization))
.header(HttpHeaders.AUTHORIZATION, "Basic " + encodeBasicAuth(
registeredClient.getClientId(), registeredClient.getClientSecret())))
@ -334,7 +336,7 @@ public class OidcTests { @@ -334,7 +336,7 @@ public class OidcTests {
String idToken = (String) accessTokenResponse.getAdditionalParameters().get(OidcParameterNames.ID_TOKEN);
// Logout
mvcResult = this.mvc.perform(post(DEFAULT_OIDC_LOGOUT_ENDPOINT_URI)
mvcResult = this.mvc.perform(post(issuer.concat(DEFAULT_OIDC_LOGOUT_ENDPOINT_URI))
.param("id_token_hint", idToken)
.session(session))
.andExpect(status().is3xxRedirection())

24
oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OidcUserInfoTests.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2020-2022 the original author or authors.
* Copyright 2020-2024 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.
@ -24,11 +24,12 @@ import java.util.Set; @@ -24,11 +24,12 @@ import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Function;
import jakarta.servlet.http.HttpServletResponse;
import com.nimbusds.jose.jwk.JWKSet;
import com.nimbusds.jose.jwk.source.ImmutableJWKSet;
import com.nimbusds.jose.jwk.source.JWKSource;
import com.nimbusds.jose.proc.SecurityContext;
import jakarta.servlet.http.HttpServletResponse;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@ -193,6 +194,24 @@ public class OidcUserInfoTests { @@ -193,6 +194,24 @@ public class OidcUserInfoTests {
// @formatter:on
}
@Test
public void requestWhenUserInfoRequestIncludesIssuerPathThenUserInfoResponse() throws Exception {
this.spring.register(AuthorizationServerConfiguration.class).autowire();
OAuth2Authorization authorization = createAuthorization();
this.authorizationService.save(authorization);
String issuer = "https://example.com:8443/issuer1";
OAuth2AccessToken accessToken = authorization.getAccessToken().getToken();
// @formatter:off
this.mvc.perform(get(issuer.concat(DEFAULT_OIDC_USER_INFO_ENDPOINT_URI))
.header(HttpHeaders.AUTHORIZATION, "Bearer " + accessToken.getTokenValue()))
.andExpect(status().is2xxSuccessful())
.andExpectAll(userInfoResponse());
// @formatter:on
}
@Test
public void requestWhenUserInfoEndpointCustomizedThenUsed() throws Exception {
this.spring.register(CustomUserInfoConfiguration.class).autowire();
@ -512,7 +531,6 @@ public class OidcUserInfoTests { @@ -512,7 +531,6 @@ public class OidcUserInfoTests {
@Bean
AuthorizationServerSettings authorizationServerSettings() {
return AuthorizationServerSettings.builder()
.issuer("https://auth-server:9000")
.build();
}

6
oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/OAuth2DeviceAuthorizationEndpointFilterTests.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2020-2023 the original author or authors.
* Copyright 2020-2024 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.
@ -21,6 +21,7 @@ import java.time.temporal.ChronoUnit; @@ -21,6 +21,7 @@ import java.time.temporal.ChronoUnit;
import jakarta.servlet.FilterChain;
import jakarta.servlet.http.HttpServletRequest;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@ -404,6 +405,9 @@ public class OAuth2DeviceAuthorizationEndpointFilterTests { @@ -404,6 +405,9 @@ public class OAuth2DeviceAuthorizationEndpointFilterTests {
request.setRequestURI(AUTHORIZATION_URI);
request.setServletPath(AUTHORIZATION_URI);
request.setRemoteAddr(REMOTE_ADDRESS);
request.setScheme("https");
request.setServerName("provider.com");
request.setServerPort(-1);
return request;
}

6
samples/demo-authorizationserver/src/main/java/sample/config/AuthorizationServerConfig.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2020-2023 the original author or authors.
* Copyright 2020-2024 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.
@ -43,8 +43,6 @@ import org.springframework.security.oauth2.core.oidc.OidcScopes; @@ -43,8 +43,6 @@ import org.springframework.security.oauth2.core.oidc.OidcScopes;
import org.springframework.security.oauth2.jwt.JwtDecoder;
import org.springframework.security.oauth2.server.authorization.JdbcOAuth2AuthorizationConsentService;
import org.springframework.security.oauth2.server.authorization.JdbcOAuth2AuthorizationService;
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationConsentService;
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;
import org.springframework.security.oauth2.server.authorization.client.JdbcRegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
@ -94,7 +92,7 @@ public class AuthorizationServerConfig { @@ -94,7 +92,7 @@ public class AuthorizationServerConfig {
*/
DeviceClientAuthenticationConverter deviceClientAuthenticationConverter =
new DeviceClientAuthenticationConverter(
authorizationServerSettings.getDeviceAuthorizationEndpoint());
"/**" + authorizationServerSettings.getDeviceAuthorizationEndpoint());
DeviceClientAuthenticationProvider deviceClientAuthenticationProvider =
new DeviceClientAuthenticationProvider(registeredClientRepository);

Loading…
Cancel
Save