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

smolnar82 pushed a commit to branch knox_idf
in repository https://gitbox.apache.org/repos/asf/knox.git

commit 7341b1b89031c6affe302de5d876ae3476dbca3d
Author: Sandor Molnar <[email protected]>
AuthorDate: Wed Aug 12 00:44:00 2026 +0200

    KNOX-3414: complete OIDC discovery metadata for OAuth/MCP client integration
    
    The .well-known/openid-configuration document was missing a REQUIRED field
    and several fields clients rely on to integrate without out-of-band config.
    
    - subject_types_supported: ["public"] -- REQUIRED by OpenID Connect 
Discovery
      1.0; its absence makes the document non-conformant. Knox derives 'sub' as 
a
      deterministic UUIDv5 over a fixed namespace and the user identity (the 
same
      for every client), so the subject identifier type is "public", not 
pairwise.
    - registration_endpoint -> the /client dynamic client registration resource
      KnoxIDF already serves. This is the spec-sanctioned path for MCP clients 
that
      cannot pre-register.
    - token_endpoint_auth_methods_supported: ["client_secret_post", "none"] --
      matches what the token endpoint actually reads: confidential clients send
      client_secret in request params (client_secret_post), public clients use 
PKCE
      with no secret ("none"). client_secret_basic is intentionally excluded 
because
      the endpoint does not read HTTP Basic credentials.
    - client_id_metadata_document_supported: false -- stated explicitly (the 
CIMD
      draft default when absent) so MCP clients use registration_endpoint rather
      than an HTTPS-URL client_id; Knox does not resolve URL client_ids to 
fetched
      metadata documents.
    
    Test: DiscoveryResourceMetadataTest asserts the added fields and that 
neither
    client_secret_basic nor CIMD support is falsely advertised.
    
    Co-Authored-By: Claude Opus 4.8 <[email protected]>
---
 .../gateway/service/knoxidf/DiscoveryResource.java | 17 ++++++
 .../knoxidf/DiscoveryResourceMetadataTest.java     | 69 ++++++++++++++++++++++
 2 files changed, 86 insertions(+)

diff --git 
a/gateway-service-knoxidf/src/main/java/org/apache/knox/gateway/service/knoxidf/DiscoveryResource.java
 
b/gateway-service-knoxidf/src/main/java/org/apache/knox/gateway/service/knoxidf/DiscoveryResource.java
index e55ea817d..26b6a8cbe 100644
--- 
a/gateway-service-knoxidf/src/main/java/org/apache/knox/gateway/service/knoxidf/DiscoveryResource.java
+++ 
b/gateway-service-knoxidf/src/main/java/org/apache/knox/gateway/service/knoxidf/DiscoveryResource.java
@@ -66,8 +66,25 @@ public class DiscoveryResource {
         }
         config.put("token_endpoint", tokenEndpoint);
         config.put("userinfo_endpoint", userInfoEndpoint);
+        // Dynamic client registration is served on the current topology (no 
token-exchange
+        // substitution); advertise it so clients can discover it per OIDC 
Dynamic Client Registration.
+        config.put("registration_endpoint", baseUrl + 
RegistrationResource.RESOURCE_PATH);
         config.put("jwks_uri", baseUrl + JwksResource.RESOURCE_PATH);
         config.put("response_types_supported", new 
String[]{KnoxIDFConstants.CODE});
+        // REQUIRED by OpenID Connect Discovery 1.0. Knox derives 'sub' as a 
deterministic UUIDv5 over
+        // a fixed namespace and the user identity -- the same for every 
client -- so the subject
+        // identifier type is "public" (not "pairwise").
+        config.put("subject_types_supported", new String[]{"public"});
+        // The token endpoint reads client credentials only from request 
parameters (no HTTP Basic):
+        // confidential clients send client_secret in the body 
(client_secret_post); public clients
+        // authenticate with PKCE and no secret ("none"). client_secret_basic 
is intentionally absent
+        // because it is not honored.
+        config.put("token_endpoint_auth_methods_supported", new 
String[]{"client_secret_post", "none"});
+        // Explicitly false: Knox does not resolve an HTTPS-URL client_id to a 
fetched Client ID
+        // Metadata Document (OAuth CIMD draft, referenced by MCP). This is 
the spec default when the
+        // field is absent, but stating it tells MCP clients to use dynamic 
client registration
+        // (registration_endpoint) rather than a URL client_id. Flip to true 
only if CIMD is implemented.
+        config.put("client_id_metadata_document_supported", Boolean.FALSE);
         config.put("grant_types_supported", new 
String[]{KnoxIDFConstants.AUTH_CODE, KnoxIDFConstants.REFRESH_TOKEN});
         config.put("scopes_supported", KnoxIDFConstants.DEFAULT_SCOPES);
         config.put("id_token_signing_alg_values_supported", new 
String[]{"RS256"});
diff --git 
a/gateway-service-knoxidf/src/test/java/org/apache/knox/gateway/service/knoxidf/DiscoveryResourceMetadataTest.java
 
b/gateway-service-knoxidf/src/test/java/org/apache/knox/gateway/service/knoxidf/DiscoveryResourceMetadataTest.java
new file mode 100644
index 000000000..4f5c90689
--- /dev/null
+++ 
b/gateway-service-knoxidf/src/test/java/org/apache/knox/gateway/service/knoxidf/DiscoveryResourceMetadataTest.java
@@ -0,0 +1,69 @@
+/*
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.knox.gateway.service.knoxidf;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.net.URI;
+
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
+
+import org.easymock.EasyMock;
+import org.junit.Test;
+
+/**
+ * Verifies the discovery document carries required/expected OIDC provider 
metadata.
+ * subject_types_supported is REQUIRED by OpenID Connect Discovery 1.0; a 
strict client or
+ * conformance validator rejects a document that omits it. 
registration_endpoint must point at the
+ * dynamic client registration resource that KnoxIDF actually serves.
+ */
+public class DiscoveryResourceMetadataTest {
+
+  @Test
+  public void testAdvertisesSubjectTypesAndRegistrationEndpoint() {
+    final UriInfo uriInfo = EasyMock.createNiceMock(UriInfo.class);
+    
EasyMock.expect(uriInfo.getBaseUri()).andReturn(URI.create("https://knox:8443/gateway/knoxidf/";)).anyTimes();
+    EasyMock.replay(uriInfo);
+
+    final Response response = new DiscoveryResource().getConfig(uriInfo);
+    final String body = String.valueOf(response.getEntity());
+
+    // subject_types_supported is REQUIRED; Knox uses a shared (non-pairwise) 
subject -> "public".
+    assertTrue("subject_types_supported must be present (REQUIRED by OIDC 
Discovery).",
+        body.contains("subject_types_supported"));
+    assertTrue("subject_types_supported must advertise 'public'.",
+        body.contains("\"public\""));
+
+    // registration_endpoint must resolve to the dynamic client registration 
resource.
+    assertTrue("registration_endpoint must point at the /client registration 
resource.",
+        body.contains("registration_endpoint") && 
body.contains(RegistrationResource.RESOURCE_PATH));
+
+    // The token endpoint authenticates clients via body params only: 
client_secret_post + none (PKCE).
+    assertTrue("token_endpoint_auth_methods_supported must advertise 
client_secret_post and none.",
+        body.contains("token_endpoint_auth_methods_supported")
+            && body.contains("client_secret_post") && 
body.contains("\"none\""));
+    // It must NOT claim HTTP Basic client auth, which the token endpoint does 
not read.
+    assertFalse("Discovery must not advertise client_secret_basic, which is 
not honored.",
+        body.contains("client_secret_basic"));
+
+    // CIMD is not implemented, so it must be advertised explicitly as false 
(never true).
+    assertTrue("client_id_metadata_document_supported must be present and 
false.",
+        body.contains("\"client_id_metadata_document_supported\":false"));
+  }
+}

Reply via email to