This is an automated email from the ASF dual-hosted git repository.

lhotari pushed a commit to branch branch-4.0
in repository https://gitbox.apache.org/repos/asf/pulsar.git

commit 705a99d1109b62e244f0a3ab17f8af00ed530f3d
Author: Hideaki Oguni <[email protected]>
AuthorDate: Fri Feb 6 21:49:59 2026 +0900

    [improve][client] Make authorization server metadata path configurable in 
AuthenticationOAuth2 (#25052)
    
    Co-authored-by: hoguni <[email protected]>
    (cherry picked from commit 3cb7a7b44c667c9777af2279cc6e7a6829c93a09)
---
 .../auth/oauth2/AuthenticationFactoryOAuth2.java   | 25 ++++++++
 .../AuthenticationOAuth2StandardAuthzServer.java   | 70 ++++++++++++++++++++++
 .../impl/auth/oauth2/ClientCredentialsFlow.java    |  7 ++-
 .../pulsar/client/impl/auth/oauth2/FlowBase.java   |  8 ++-
 .../oauth2/protocol/DefaultMetadataResolver.java   | 33 ++++++++--
 .../oauth2/AuthenticationFactoryOAuth2Test.java    | 17 +++++-
 ...uthenticationOAuth2StandardAuthzServerTest.java | 50 ++++++++++++++++
 .../impl/auth/oauth2/AuthenticationOAuth2Test.java | 43 ++++++++++++-
 8 files changed, 243 insertions(+), 10 deletions(-)

diff --git 
a/pulsar-client/src/main/java/org/apache/pulsar/client/impl/auth/oauth2/AuthenticationFactoryOAuth2.java
 
b/pulsar-client/src/main/java/org/apache/pulsar/client/impl/auth/oauth2/AuthenticationFactoryOAuth2.java
index 033d5308a2a..7c89c6cde6d 100644
--- 
a/pulsar-client/src/main/java/org/apache/pulsar/client/impl/auth/oauth2/AuthenticationFactoryOAuth2.java
+++ 
b/pulsar-client/src/main/java/org/apache/pulsar/client/impl/auth/oauth2/AuthenticationFactoryOAuth2.java
@@ -22,6 +22,7 @@ import java.net.URL;
 import java.time.Clock;
 import java.time.Duration;
 import org.apache.pulsar.client.api.Authentication;
+import 
org.apache.pulsar.client.impl.auth.oauth2.protocol.DefaultMetadataResolver;
 
 /**
  * Factory class that allows to create {@link Authentication} instances
@@ -69,6 +70,17 @@ public final class AuthenticationFactoryOAuth2 {
         return new ClientCredentialsBuilder();
     }
 
+    /**
+     * A builder to create an authentication with client credentials using 
standard OAuth 2.0 metadata path
+     * as defined in RFC 8414 ("/.well-known/oauth-authorization-server").
+     *
+     * @return the builder pre-configured to use standard OAuth 2.0 metadata 
path
+     */
+    public static ClientCredentialsBuilder 
clientCredentialsWithStandardAuthzServerBuilder() {
+        return new ClientCredentialsBuilder()
+                
.wellKnownMetadataPath(DefaultMetadataResolver.OAUTH_WELL_KNOWN_METADATA_PATH);
+    }
+
     public static class ClientCredentialsBuilder {
 
         private URL issuerUrl;
@@ -78,6 +90,7 @@ public final class AuthenticationFactoryOAuth2 {
         private Duration connectTimeout;
         private Duration readTimeout;
         private String trustCertsFilePath;
+        private String wellKnownMetadataPath;
 
         private ClientCredentialsBuilder() {
         }
@@ -163,6 +176,17 @@ public final class AuthenticationFactoryOAuth2 {
             return this;
         }
 
+        /**
+         * Optional well-known metadata path.
+         *
+         * @param wellKnownMetadataPath the well-known metadata path (must 
start with "/.well-known/")
+         * @return the builder
+         */
+        public ClientCredentialsBuilder wellKnownMetadataPath(String 
wellKnownMetadataPath) {
+            this.wellKnownMetadataPath = wellKnownMetadataPath;
+            return this;
+        }
+
         /**
          * Authenticate with client credentials.
          *
@@ -177,6 +201,7 @@ public final class AuthenticationFactoryOAuth2 {
                     .connectTimeout(connectTimeout)
                     .readTimeout(readTimeout)
                     .trustCertsFilePath(trustCertsFilePath)
+                    .wellKnownMetadataPath(wellKnownMetadataPath)
                     .build();
             return new AuthenticationOAuth2(flow, Clock.systemDefaultZone());
         }
diff --git 
a/pulsar-client/src/main/java/org/apache/pulsar/client/impl/auth/oauth2/AuthenticationOAuth2StandardAuthzServer.java
 
b/pulsar-client/src/main/java/org/apache/pulsar/client/impl/auth/oauth2/AuthenticationOAuth2StandardAuthzServer.java
new file mode 100644
index 00000000000..c61d6d7b097
--- /dev/null
+++ 
b/pulsar-client/src/main/java/org/apache/pulsar/client/impl/auth/oauth2/AuthenticationOAuth2StandardAuthzServer.java
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.pulsar.client.impl.auth.oauth2;
+
+import java.io.IOException;
+import java.time.Clock;
+import java.util.Map;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.pulsar.client.impl.AuthenticationUtil;
+import 
org.apache.pulsar.client.impl.auth.oauth2.protocol.DefaultMetadataResolver;
+
+/**
+ * Pulsar client authentication provider based on OAuth 2.0 using RFC 8414 
standard metadata path.
+ * This class is identical to {@link AuthenticationOAuth2} but it always uses 
the standard
+ * "/.well-known/oauth-authorization-server" metadata path as defined in RFC 
8414.
+ */
+public class AuthenticationOAuth2StandardAuthzServer extends 
AuthenticationOAuth2 {
+
+    private static final long serialVersionUID = 1L;
+
+    public AuthenticationOAuth2StandardAuthzServer() {
+        super();
+    }
+
+    AuthenticationOAuth2StandardAuthzServer(Flow flow, Clock clock) {
+        super(flow, clock);
+    }
+
+    @Override
+    public void configure(String encodedAuthParamString) {
+        if (StringUtils.isBlank(encodedAuthParamString)) {
+            throw new IllegalArgumentException("No authentication parameters 
were provided");
+        }
+        Map<String, String> params;
+        try {
+            params = 
AuthenticationUtil.configureFromJsonString(encodedAuthParamString);
+        } catch (IOException e) {
+            throw new IllegalArgumentException("Malformed authentication 
parameters", e);
+        }
+
+        // Always set the OAuth 2.0 standard metadata path
+        params.put(FlowBase.CONFIG_PARAM_WELL_KNOWN_METADATA_PATH,
+                DefaultMetadataResolver.OAUTH_WELL_KNOWN_METADATA_PATH);
+
+        String type = params.getOrDefault(CONFIG_PARAM_TYPE, 
TYPE_CLIENT_CREDENTIALS);
+        switch(type) {
+            case TYPE_CLIENT_CREDENTIALS:
+                this.flow = ClientCredentialsFlow.fromParameters(params);
+                break;
+            default:
+                throw new IllegalArgumentException("Unsupported authentication 
type: " + type);
+        }
+    }
+}
diff --git 
a/pulsar-client/src/main/java/org/apache/pulsar/client/impl/auth/oauth2/ClientCredentialsFlow.java
 
b/pulsar-client/src/main/java/org/apache/pulsar/client/impl/auth/oauth2/ClientCredentialsFlow.java
index 7f64c0b18ac..fe7beb47ed2 100644
--- 
a/pulsar-client/src/main/java/org/apache/pulsar/client/impl/auth/oauth2/ClientCredentialsFlow.java
+++ 
b/pulsar-client/src/main/java/org/apache/pulsar/client/impl/auth/oauth2/ClientCredentialsFlow.java
@@ -62,8 +62,9 @@ class ClientCredentialsFlow extends FlowBase {
 
     @Builder
     public ClientCredentialsFlow(URL issuerUrl, String audience, String 
privateKey, String scope,
-                                 Duration connectTimeout, Duration 
readTimeout, String trustCertsFilePath) {
-        super(issuerUrl, connectTimeout, readTimeout, trustCertsFilePath);
+                                 Duration connectTimeout, Duration 
readTimeout, String trustCertsFilePath,
+                                 String wellKnownMetadataPath) {
+        super(issuerUrl, connectTimeout, readTimeout, trustCertsFilePath, 
wellKnownMetadataPath);
         this.audience = audience;
         this.privateKey = privateKey;
         this.scope = scope;
@@ -84,6 +85,7 @@ class ClientCredentialsFlow extends FlowBase {
         Duration connectTimeout = parseParameterDuration(params, 
CONFIG_PARAM_CONNECT_TIMEOUT);
         Duration readTimeout = parseParameterDuration(params, 
CONFIG_PARAM_READ_TIMEOUT);
         String trustCertsFilePath = 
params.get(CONFIG_PARAM_TRUST_CERTS_FILE_PATH);
+        String wellKnownMetadataPath = 
params.get(CONFIG_PARAM_WELL_KNOWN_METADATA_PATH);
 
         return ClientCredentialsFlow.builder()
                 .issuerUrl(issuerUrl)
@@ -93,6 +95,7 @@ class ClientCredentialsFlow extends FlowBase {
                 .connectTimeout(connectTimeout)
                 .readTimeout(readTimeout)
                 .trustCertsFilePath(trustCertsFilePath)
+                .wellKnownMetadataPath(wellKnownMetadataPath)
                 .build();
     }
 
diff --git 
a/pulsar-client/src/main/java/org/apache/pulsar/client/impl/auth/oauth2/FlowBase.java
 
b/pulsar-client/src/main/java/org/apache/pulsar/client/impl/auth/oauth2/FlowBase.java
index 6cc9f8e41b5..8a90712d7ea 100644
--- 
a/pulsar-client/src/main/java/org/apache/pulsar/client/impl/auth/oauth2/FlowBase.java
+++ 
b/pulsar-client/src/main/java/org/apache/pulsar/client/impl/auth/oauth2/FlowBase.java
@@ -47,6 +47,7 @@ abstract class FlowBase implements Flow {
     public static final String CONFIG_PARAM_CONNECT_TIMEOUT = "connectTimeout";
     public static final String CONFIG_PARAM_READ_TIMEOUT = "readTimeout";
     public static final String CONFIG_PARAM_TRUST_CERTS_FILE_PATH = 
"trustCertsFilePath";
+    public static final String CONFIG_PARAM_WELL_KNOWN_METADATA_PATH = 
"wellKnownMetadataPath";
 
     protected static final Duration DEFAULT_CONNECT_TIMEOUT = 
Duration.ofSeconds(10);
     protected static final Duration DEFAULT_READ_TIMEOUT = 
Duration.ofSeconds(30);
@@ -55,12 +56,15 @@ abstract class FlowBase implements Flow {
 
     protected final URL issuerUrl;
     protected final AsyncHttpClient httpClient;
+    protected final String wellKnownMetadataPath;
 
     protected transient Metadata metadata;
 
-    protected FlowBase(URL issuerUrl, Duration connectTimeout, Duration 
readTimeout, String trustCertsFilePath) {
+    protected FlowBase(URL issuerUrl, Duration connectTimeout, Duration 
readTimeout, String trustCertsFilePath,
+                       String wellKnownMetadataPath) {
         this.issuerUrl = issuerUrl;
         this.httpClient = defaultHttpClient(readTimeout, connectTimeout, 
trustCertsFilePath);
+        this.wellKnownMetadataPath = wellKnownMetadataPath;
     }
 
     private AsyncHttpClient defaultHttpClient(Duration readTimeout, Duration 
connectTimeout,
@@ -110,7 +114,7 @@ abstract class FlowBase implements Flow {
     }
 
     protected MetadataResolver createMetadataResolver() {
-        return DefaultMetadataResolver.fromIssuerUrl(issuerUrl, httpClient);
+        return DefaultMetadataResolver.fromIssuerUrl(issuerUrl, httpClient, 
wellKnownMetadataPath);
     }
 
     static String parseParameterString(Map<String, String> params, String 
name) {
diff --git 
a/pulsar-client/src/main/java/org/apache/pulsar/client/impl/auth/oauth2/protocol/DefaultMetadataResolver.java
 
b/pulsar-client/src/main/java/org/apache/pulsar/client/impl/auth/oauth2/protocol/DefaultMetadataResolver.java
index 19d0c1acadd..e43117bb7df 100644
--- 
a/pulsar-client/src/main/java/org/apache/pulsar/client/impl/auth/oauth2/protocol/DefaultMetadataResolver.java
+++ 
b/pulsar-client/src/main/java/org/apache/pulsar/client/impl/auth/oauth2/protocol/DefaultMetadataResolver.java
@@ -36,6 +36,13 @@ import org.asynchttpclient.Response;
  */
 public class DefaultMetadataResolver implements MetadataResolver {
 
+    private static final String WELL_KNOWN_PREFIX = "/.well-known/";
+    private static final String DEFAULT_WELL_KNOWN_METADATA_PATH = 
WELL_KNOWN_PREFIX + "openid-configuration";
+    /**
+     * The OAuth 2.0 Authorization Server Metadata path as defined in RFC 8414.
+     */
+    public static final String OAUTH_WELL_KNOWN_METADATA_PATH = 
WELL_KNOWN_PREFIX + "oauth-authorization-server";
+
     private final URL metadataUrl;
     private final ObjectReader objectReader;
     private final AsyncHttpClient httpClient;
@@ -50,23 +57,41 @@ public class DefaultMetadataResolver implements 
MetadataResolver {
      * Gets a well-known metadata URL for the given OAuth issuer URL.
      *
      * @param issuerUrl The authorization server's issuer identifier
+     * @param httpClient The HTTP client
+     * @param wellKnownMetadataPath The well-known metadata path (must start 
with "/.well-known/")
      * @return a resolver
      */
-    public static DefaultMetadataResolver fromIssuerUrl(URL issuerUrl, 
AsyncHttpClient httpClient) {
-        return new DefaultMetadataResolver(getWellKnownMetadataUrl(issuerUrl), 
httpClient);
+    public static DefaultMetadataResolver fromIssuerUrl(URL issuerUrl, 
AsyncHttpClient httpClient,
+                                                        String 
wellKnownMetadataPath) {
+        return new DefaultMetadataResolver(getWellKnownMetadataUrl(issuerUrl, 
wellKnownMetadataPath), httpClient);
     }
 
     /**
      * Gets a well-known metadata URL for the given OAuth issuer URL.
      *
      * @param issuerUrl The authorization server's issuer identifier
+     * @param wellKnownMetadataPath The well-known metadata path (must start 
with "/.well-known/")
      * @return a URL
      * @see <a 
href="https://tools.ietf.org/id/draft-ietf-oauth-discovery-08.html#ASConfig";>
      * OAuth Discovery: Obtaining Authorization Server Metadata</a>
      */
-    public static URL getWellKnownMetadataUrl(URL issuerUrl) {
+    public static URL getWellKnownMetadataUrl(URL issuerUrl, String 
wellKnownMetadataPath) {
         try {
-            return URI.create(issuerUrl.toExternalForm() + 
"/.well-known/openid-configuration").normalize().toURL();
+            if (wellKnownMetadataPath == null || 
wellKnownMetadataPath.isEmpty()) {
+                return URI.create(issuerUrl.toExternalForm() + 
DEFAULT_WELL_KNOWN_METADATA_PATH).normalize().toURL();
+            }
+            if (wellKnownMetadataPath.startsWith(WELL_KNOWN_PREFIX)) {
+                String issuerUrlString = issuerUrl.toExternalForm();
+                // For OAuth2, insert well-known path before the issuer URL 
path
+                URL url = new URL(issuerUrlString);
+                String path = url.getPath();
+                String basePath = issuerUrlString.substring(0,
+                        issuerUrlString.length() - (path.isEmpty() ? 0 : 
path.length()));
+                return URI.create(basePath + wellKnownMetadataPath + 
path).normalize().toURL();
+            } else {
+                throw new IllegalArgumentException("Metadata path must start 
with '" + WELL_KNOWN_PREFIX
+                        + "', but was: " + wellKnownMetadataPath);
+            }
         } catch (MalformedURLException e) {
             throw new IllegalArgumentException(e);
         }
diff --git 
a/pulsar-client/src/test/java/org/apache/pulsar/client/impl/auth/oauth2/AuthenticationFactoryOAuth2Test.java
 
b/pulsar-client/src/test/java/org/apache/pulsar/client/impl/auth/oauth2/AuthenticationFactoryOAuth2Test.java
index 602aafa7b6c..f76fee6e10d 100644
--- 
a/pulsar-client/src/test/java/org/apache/pulsar/client/impl/auth/oauth2/AuthenticationFactoryOAuth2Test.java
+++ 
b/pulsar-client/src/test/java/org/apache/pulsar/client/impl/auth/oauth2/AuthenticationFactoryOAuth2Test.java
@@ -36,11 +36,26 @@ public class AuthenticationFactoryOAuth2Test {
         Duration connectTimeout = Duration.parse("PT11S");
         Duration readTimeout = Duration.ofSeconds(31);
         String trustCertsFilePath = null;
+        String wellKnownMetadataPath = "/.well-known/custom-path";
         try (Authentication authentication =
                      
AuthenticationFactoryOAuth2.clientCredentialsBuilder().issuerUrl(issuerUrl)
                              
.credentialsUrl(credentialsUrl).audience(audience).scope(scope)
                              
.connectTimeout(connectTimeout).readTimeout(readTimeout)
-                             .trustCertsFilePath(trustCertsFilePath).build()) {
+                             .trustCertsFilePath(trustCertsFilePath)
+                             
.wellKnownMetadataPath(wellKnownMetadataPath).build()) {
+            assertTrue(authentication instanceof AuthenticationOAuth2);
+        }
+    }
+
+    @Test
+    public void testStandardAuthzServerBuilder() throws IOException {
+        URL issuerUrl = new URL("http://localhost";);
+        URL credentialsUrl = new URL("http://localhost";);
+        String audience = "audience";
+        String scope = "scope";
+        try (Authentication authentication =
+                     
AuthenticationFactoryOAuth2.clientCredentialsWithStandardAuthzServerBuilder().issuerUrl(issuerUrl)
+                             
.credentialsUrl(credentialsUrl).audience(audience).scope(scope).build()) {
             assertTrue(authentication instanceof AuthenticationOAuth2);
         }
     }
diff --git 
a/pulsar-client/src/test/java/org/apache/pulsar/client/impl/auth/oauth2/AuthenticationOAuth2StandardAuthzServerTest.java
 
b/pulsar-client/src/test/java/org/apache/pulsar/client/impl/auth/oauth2/AuthenticationOAuth2StandardAuthzServerTest.java
new file mode 100644
index 00000000000..e1403b3aa4b
--- /dev/null
+++ 
b/pulsar-client/src/test/java/org/apache/pulsar/client/impl/auth/oauth2/AuthenticationOAuth2StandardAuthzServerTest.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.pulsar.client.impl.auth.oauth2;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import java.lang.reflect.Field;
+import java.util.HashMap;
+import java.util.Map;
+import 
org.apache.pulsar.client.impl.auth.oauth2.protocol.DefaultMetadataResolver;
+import org.testng.annotations.Test;
+
+public class AuthenticationOAuth2StandardAuthzServerTest {
+
+    @Test
+    public void testConfigureWithOAuth2MetadataPath() throws Exception {
+        Map<String, String> params = new HashMap<>();
+        params.put("type", "client_credentials");
+        params.put("privateKey", "data:base64,e30=");
+        params.put("issuerUrl", "http://localhost";);
+        params.put("audience", "test-audience");
+        ObjectMapper mapper = new ObjectMapper();
+        String authParams = mapper.writeValueAsString(params);
+        AuthenticationOAuth2StandardAuthzServer auth = new 
AuthenticationOAuth2StandardAuthzServer();
+        auth.configure(authParams);
+        assertTrue(auth.flow instanceof ClientCredentialsFlow);
+        ClientCredentialsFlow flow = (ClientCredentialsFlow) auth.flow;
+        Field wellKnownMetadataPathField = 
FlowBase.class.getDeclaredField("wellKnownMetadataPath");
+        wellKnownMetadataPathField.setAccessible(true);
+        String wellKnownMetadataPath = (String) 
wellKnownMetadataPathField.get(flow);
+        assertEquals(wellKnownMetadataPath, 
DefaultMetadataResolver.OAUTH_WELL_KNOWN_METADATA_PATH);
+    }
+}
diff --git 
a/pulsar-client/src/test/java/org/apache/pulsar/client/impl/auth/oauth2/AuthenticationOAuth2Test.java
 
b/pulsar-client/src/test/java/org/apache/pulsar/client/impl/auth/oauth2/AuthenticationOAuth2Test.java
index aef69be74e1..d430d8f0e40 100644
--- 
a/pulsar-client/src/test/java/org/apache/pulsar/client/impl/auth/oauth2/AuthenticationOAuth2Test.java
+++ 
b/pulsar-client/src/test/java/org/apache/pulsar/client/impl/auth/oauth2/AuthenticationOAuth2Test.java
@@ -85,6 +85,7 @@ public class AuthenticationOAuth2Test {
         params.put("issuerUrl", "http://localhost";);
         params.put("audience", "http://localhost";);
         params.put("scope", "http://localhost";);
+        params.put("wellKnownMetadataPath", "/.well-known/custom-path");
         ObjectMapper mapper = new ObjectMapper();
         String authParams = mapper.writeValueAsString(params);
         this.auth.configure(authParams);
@@ -132,8 +133,48 @@ public class AuthenticationOAuth2Test {
 
     @Test
     public void testMetadataResolver() throws MalformedURLException {
-        URL url = 
DefaultMetadataResolver.getWellKnownMetadataUrl(URI.create("http://localhost/path/oauth";).toURL());
+        URL url = DefaultMetadataResolver.getWellKnownMetadataUrl(
+                URI.create("http://localhost/path/oauth";).toURL(),
+                null);
         
assertEquals("http://localhost/path/oauth/.well-known/openid-configuration";, 
url.toString());
+
+        // custom wellKnownMetadataPath with full well-known prefix
+        URL customUrl = DefaultMetadataResolver.getWellKnownMetadataUrl(
+                URI.create("http://localhost/path/oauth";).toURL(),
+                "/.well-known/custom-path");
+        assertEquals("http://localhost/.well-known/custom-path/path/oauth";, 
customUrl.toString());
+
+        // null wellKnownMetadataPath (should use default)
+        URL customUrl2 = DefaultMetadataResolver.getWellKnownMetadataUrl(
+                URI.create("http://localhost/path/oauth";).toURL(),
+                null);
+        
assertEquals("http://localhost/path/oauth/.well-known/openid-configuration";, 
customUrl2.toString());
+
+        // empty wellKnownMetadataPath (should use default)
+        URL customUrl3 = DefaultMetadataResolver.getWellKnownMetadataUrl(
+                URI.create("http://localhost/path/oauth";).toURL(),
+                "");
+        
assertEquals("http://localhost/path/oauth/.well-known/openid-configuration";, 
customUrl3.toString());
+
+        // using RFC8414 OAuth2 metadata path
+        URL oauthUrl = DefaultMetadataResolver.getWellKnownMetadataUrl(
+                URI.create("http://localhost/path/oauth";).toURL(),
+                DefaultMetadataResolver.OAUTH_WELL_KNOWN_METADATA_PATH);
+        
assertEquals("http://localhost/.well-known/oauth-authorization-server/path/oauth";,
 oauthUrl.toString());
+
+        // test with issuer URL without path
+        URL oauthUrlNoPath = DefaultMetadataResolver.getWellKnownMetadataUrl(
+                URI.create("http://localhost";).toURL(),
+                DefaultMetadataResolver.OAUTH_WELL_KNOWN_METADATA_PATH);
+        
assertEquals("http://localhost/.well-known/oauth-authorization-server";, 
oauthUrlNoPath.toString());
+    }
+
+    @Test(expectedExceptions = IllegalArgumentException.class,
+            expectedExceptionsMessageRegExp = ".*Metadata path must start 
with.*")
+    public void testMetadataResolverWithInvalidPath() throws 
MalformedURLException {
+        DefaultMetadataResolver.getWellKnownMetadataUrl(
+                URI.create("http://localhost/path/oauth";).toURL(),
+                "/custom-path");
     }
 
     @Test

Reply via email to