diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/registration/ClientRegistrations.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/registration/ClientRegistrations.java index 54efef5107..9470c3f0ba 100644 --- a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/registration/ClientRegistrations.java +++ b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/registration/ClientRegistrations.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2024 the original author or authors. + * Copyright 2002-2025 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. @@ -17,6 +17,7 @@ package org.springframework.security.oauth2.client.registration; import java.net.URI; +import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -49,6 +50,7 @@ import org.springframework.web.util.UriComponentsBuilder; * @author Rob Winch * @author Josh Cummings * @author Rafiullah Hamedy + * @author Evgeniy Cheban * @since 5.1 */ public final class ClientRegistrations { @@ -265,6 +267,7 @@ public final class ClientRegistrations { private static ClientRegistration.Builder getBuilder(String issuer, Supplier... suppliers) { String errorMessage = "Unable to resolve Configuration with the provided Issuer of \"" + issuer + "\""; + List errors = new ArrayList<>(); for (Supplier supplier : suppliers) { try { return supplier.get(); @@ -273,6 +276,7 @@ public final class ClientRegistrations { if (!ex.getStatusCode().is4xxClientError()) { throw ex; } + errors.add(ex.getMessage()); // else try another endpoint } catch (IllegalArgumentException | IllegalStateException ex) { @@ -282,6 +286,9 @@ public final class ClientRegistrations { throw new IllegalArgumentException(errorMessage, ex); } } + if (!errors.isEmpty()) { + throw new IllegalArgumentException(errorMessage + ", errors: " + errors); + } throw new IllegalArgumentException(errorMessage); } diff --git a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/registration/ClientRegistrationsTests.java b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/registration/ClientRegistrationsTests.java index f66fe39454..ad2395cdc5 100644 --- a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/registration/ClientRegistrationsTests.java +++ b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/registration/ClientRegistrationsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2021 the original author or authors. + * Copyright 2002-2025 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. @@ -37,12 +37,14 @@ import org.springframework.security.oauth2.core.ClientAuthenticationMethod; import org.springframework.web.util.UriComponents; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; import static org.assertj.core.api.Assertions.assertThatIllegalStateException; /** * @author Rob Winch * @author Rafiullah Hamedy + * @author Evgeniy Cheban * @since 5.1 */ public class ClientRegistrationsTests { @@ -581,6 +583,33 @@ public class ClientRegistrationsTests { assertThat(oidcRfc8414.getHost()).isEqualTo("elated_sutherland"); } + @Test + public void issuerWhenAllEndpointsFailedThenExceptionIncludesFailureInformation() { + this.issuer = createIssuerFromServer("issuer1"); + this.server.setDispatcher(new Dispatcher() { + @Override + public MockResponse dispatch(RecordedRequest request) { + int responseCode = switch (request.getPath()) { + case "/issuer1/.well-known/openid-configuration" -> 405; + case "/.well-known/openid-configuration/issuer1" -> 400; + default -> 404; + }; + return new MockResponse().setResponseCode(responseCode); + } + }); + String message = """ + Unable to resolve Configuration with the provided Issuer of "%s", errors: [\ + 405 Client Error on GET request for "%s": [no body], \ + 400 Client Error on GET request for "%s": [no body], \ + 404 Client Error on GET request for "%s": [no body]]\ + """.formatted(this.issuer, this.server.url("/issuer1/.well-known/openid-configuration"), + this.server.url("/.well-known/openid-configuration/issuer1"), + this.server.url("/.well-known/oauth-authorization-server/issuer1")); + assertThatExceptionOfType(IllegalArgumentException.class) + .isThrownBy(() -> ClientRegistrations.fromIssuerLocation(this.issuer).build()) + .withMessage(message); + } + private ClientRegistration.Builder registration(String path) throws Exception { this.issuer = createIssuerFromServer(path); this.response.put("issuer", this.issuer);