diff --git a/api/client/src/main/java/org/projectnessie/client/auth/oauth2/OAuth2Utils.java b/api/client/src/main/java/org/projectnessie/client/auth/oauth2/OAuth2Utils.java
index f64e3e8a27c..26f1e66e793 100644
--- a/api/client/src/main/java/org/projectnessie/client/auth/oauth2/OAuth2Utils.java
+++ b/api/client/src/main/java/org/projectnessie/client/auth/oauth2/OAuth2Utils.java
@@ -18,6 +18,8 @@
import com.fasterxml.jackson.databind.JsonNode;
import java.net.URI;
import java.security.SecureRandom;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Random;
import org.projectnessie.client.http.HttpClient;
import org.projectnessie.client.http.HttpClientException;
@@ -28,6 +30,17 @@ class OAuth2Utils {
private static final Random RANDOM = new SecureRandom();
+ /**
+ * Common locations for OpenID provider metadata.
+ *
+ * @see OpenID
+ * Connect Discovery 1.0
+ * @see RFC 8414 Section 5
+ */
+ private static final String[] WELL_KNOWN_PATHS =
+ new String[] {".well-known/openid-configuration", ".well-known/oauth-authorization-server"};
+
static String randomAlphaNumString(int length) {
return RANDOM
.ints('0', 'z' + 1)
@@ -38,17 +51,32 @@ static String randomAlphaNumString(int length) {
}
public static JsonNode fetchOpenIdProviderMetadata(HttpClient httpClient, URI issuerUrl) {
- HttpResponse response =
- httpClient.newRequest(issuerUrl).path(".well-known/openid-configuration").get();
- Status status = response.getStatus();
- if (status != Status.OK) {
- throw new HttpClientException(
- "OpenID provider metadata request returned status code " + status.getCode());
+ List failures = null;
+ for (String path : WELL_KNOWN_PATHS) {
+ try {
+ HttpResponse response = httpClient.newRequest(issuerUrl).path(path).get();
+ Status status = response.getStatus();
+ if (status != Status.OK) {
+ throw new HttpClientException(
+ "OpenID provider metadata request returned status code " + status.getCode());
+ }
+ JsonNode data = response.readEntity(JsonNode.class);
+ if (!data.has("issuer") || !data.has("authorization_endpoint")) {
+ throw new HttpClientException("Invalid OpenID provider metadata");
+ }
+ return data;
+ } catch (Exception e) {
+ if (failures == null) {
+ failures = new ArrayList<>(WELL_KNOWN_PATHS.length);
+ }
+ failures.add(e);
+ }
}
- JsonNode data = response.readEntity(JsonNode.class);
- if (!data.has("issuer") || !data.has("authorization_endpoint")) {
- throw new HttpClientException("Invalid OpenID provider metadata");
+ HttpClientException e =
+ new HttpClientException("Failed to fetch OpenID provider metadata", failures.get(0));
+ for (int i = 1; i < failures.size(); i++) {
+ e.addSuppressed(failures.get(i));
}
- return data;
+ throw e;
}
}
diff --git a/api/client/src/test/java/org/projectnessie/client/auth/oauth2/TestOAuth2Utils.java b/api/client/src/test/java/org/projectnessie/client/auth/oauth2/TestOAuth2Utils.java
index befab08782f..76bfe75e867 100644
--- a/api/client/src/test/java/org/projectnessie/client/auth/oauth2/TestOAuth2Utils.java
+++ b/api/client/src/test/java/org/projectnessie/client/auth/oauth2/TestOAuth2Utils.java
@@ -16,7 +16,8 @@
package org.projectnessie.client.auth.oauth2;
import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.assertj.core.api.Assertions.catchException;
+import static org.assertj.core.api.InstanceOfAssertFactories.throwable;
import static org.projectnessie.client.auth.oauth2.OAuth2ClientConfig.OBJECT_MAPPER;
import com.fasterxml.jackson.databind.JsonNode;
@@ -57,8 +58,12 @@ void testRandomAlphaNumString(int length) {
@CsvSource({
"'' , /.well-known/openid-configuration",
"/ , /.well-known/openid-configuration",
+ "'' , /.well-known/oauth-authorization-server",
+ "/ , /.well-known/oauth-authorization-server",
"/realms/master , /realms/master/.well-known/openid-configuration",
- "/realms/master/ , /realms/master/.well-known/openid-configuration"
+ "/realms/master/ , /realms/master/.well-known/openid-configuration",
+ "/realms/master , /realms/master/.well-known/oauth-authorization-server",
+ "/realms/master/ , /realms/master/.well-known/oauth-authorization-server"
})
void fetchOpenIdProviderMetadataSuccess(String issuerPath, String wellKnownPath)
throws Exception {
@@ -75,9 +80,18 @@ void fetchOpenIdProviderMetadataWrongEndpoint() throws Exception {
try (HttpTestServer server = new HttpTestServer(handler("/wrong/path", DATA), true);
HttpClient httpClient = newHttpClient(server)) {
URI issuerUrl = server.getUri().resolve("/realms/master/");
- assertThatThrownBy(() -> OAuth2Utils.fetchOpenIdProviderMetadata(httpClient, issuerUrl))
+ Exception e =
+ catchException(() -> OAuth2Utils.fetchOpenIdProviderMetadata(httpClient, issuerUrl));
+ assertThat(e)
+ .isInstanceOf(HttpClientException.class)
+ .hasMessageContaining("Failed to fetch OpenID provider metadata");
+ assertThat(e.getCause())
.isInstanceOf(HttpClientException.class)
.hasMessageContaining("404"); // messages differ between HttpClient impls
+ assertThat(e.getSuppressed())
+ .singleElement()
+ .asInstanceOf(throwable(HttpClientException.class))
+ .hasMessageContaining("404");
}
}
@@ -88,9 +102,18 @@ void fetchOpenIdProviderMetadataWrongData() throws Exception {
handler("/realms/master/.well-known/openid-configuration", WRONG_DATA), true);
HttpClient httpClient = newHttpClient(server)) {
URI issuerUrl = server.getUri().resolve("/realms/master/");
- assertThatThrownBy(() -> OAuth2Utils.fetchOpenIdProviderMetadata(httpClient, issuerUrl))
- .isInstanceOfAny(HttpClientException.class)
+ Exception e =
+ catchException(() -> OAuth2Utils.fetchOpenIdProviderMetadata(httpClient, issuerUrl));
+ assertThat(e)
+ .isInstanceOf(HttpClientException.class)
+ .hasMessageContaining("Failed to fetch OpenID provider metadata");
+ assertThat(e.getCause())
+ .isInstanceOf(HttpClientException.class)
.hasMessage("Invalid OpenID provider metadata");
+ assertThat(e.getSuppressed())
+ .singleElement()
+ .asInstanceOf(throwable(HttpClientException.class))
+ .hasMessageContaining("404"); // messages differ between HttpClient impls
}
}