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 e0aab56b5c2030768cb757f4e8468a67bcbe6ba9
Author: hsheinblatt <[email protected]>
AuthorDate: Tue Jul 21 13:31:28 2026 -0700

    KNOX-3355 - Add TrustedOidcIssuerService schema and interface (#1311)
    
    * KNOX-3355 - Add TrustedOidcIssuerService schema and interface
    Co-authored-by: Harrison <[email protected]>
---
 .../database/AbstractDataSourceFactory.java        |   4 +
 .../src/main/resources/conf/gateway-site.xml       |  49 +++++++++
 .../createKnoxIDFTrustedOidcIssuersTable.sql       |  23 ++++
 .../createKnoxIDFTrustedOidcIssuersTableDerby.sql  |  22 ++++
 .../createKnoxIDFTrustedOidcIssuersTableOracle.sql |  23 ++++
 .../services/AbstractGatewayServicesTest.java      |   3 +-
 .../trustedoidcissuer/TrustedOidcIssuerTest.java   |  71 ++++++++++++
 .../TrustedOidcIssuersSchemaTest.java              | 121 +++++++++++++++++++++
 .../apache/knox/gateway/services/ServiceType.java  |   3 +-
 .../trustedoidcissuer/TrustedOidcIssuer.java       |  63 +++++++++++
 .../TrustedOidcIssuerService.java                  |  96 ++++++++++++++++
 11 files changed, 476 insertions(+), 2 deletions(-)

diff --git 
a/gateway-server/src/main/java/org/apache/knox/gateway/database/AbstractDataSourceFactory.java
 
b/gateway-server/src/main/java/org/apache/knox/gateway/database/AbstractDataSourceFactory.java
index a9d544fe8..a76b219a9 100644
--- 
a/gateway-server/src/main/java/org/apache/knox/gateway/database/AbstractDataSourceFactory.java
+++ 
b/gateway-server/src/main/java/org/apache/knox/gateway/database/AbstractDataSourceFactory.java
@@ -50,6 +50,10 @@ public abstract class AbstractDataSourceFactory {
     public static final String 
DERBY_KNOXIDF_FED_IDENTITY_TABLE_CREATE_SQL_FILE_NAME = 
"createKnoxIDFFederatedIdentityTableDerby.sql";
     public static final String 
DERBY_KNOXIDF_FED_IDENTITY_ATTR_TABLE_CREATE_SQL_FILE_NAME = 
"createKnoxIDFFederatedIdentityAttributesTableDerby.sql";
 
+    public static final String KNOXIDF_TRUSTED_OIDC_ISSUERS_TABLE_SQL = 
"createKnoxIDFTrustedOidcIssuersTable.sql";
+    public static final String DERBY_KNOXIDF_TRUSTED_OIDC_ISSUERS_TABLE_SQL = 
"createKnoxIDFTrustedOidcIssuersTableDerby.sql";
+    public static final String ORACLE_KNOXIDF_TRUSTED_OIDC_ISSUERS_TABLE_SQL = 
"createKnoxIDFTrustedOidcIssuersTableOracle.sql";
+
     public static final String DATABASE_USER_ALIAS_NAME = 
"gateway_database_user";
     public static final String DATABASE_PASSWORD_ALIAS_NAME = 
"gateway_database_password";
     public static final String DATABASE_TRUSTSTORE_PASSWORD_ALIAS_NAME = 
"gateway_database_ssl_truststore_password";
diff --git a/gateway-server/src/main/resources/conf/gateway-site.xml 
b/gateway-server/src/main/resources/conf/gateway-site.xml
index f549c413f..a44a81d7e 100644
--- a/gateway-server/src/main/resources/conf/gateway-site.xml
+++ b/gateway-server/src/main/resources/conf/gateway-site.xml
@@ -125,4 +125,53 @@ limitations under the License.
         <description>Interceptor type.</description>
     </property>
 
+    <!-- KnoxIDF Trusted OIDC Issuer Service Configuration
+         These properties configure the TrustedOidcIssuerService gateway 
service,
+         which manages the registry of OIDC issuers trusted for dynamic JWKS
+         discovery. They are gateway-scoped and apply to all topologies that
+         deploy the KNOXIDF or KNOXIDF_ADMIN service roles.
+    -->
+    <!--
+    <property>
+        <name>gateway.trustedoidcissuer.max.issuers</name>
+        <value>10000</value>
+        <description>
+            Maximum number of OIDC issuers that may be registered in the 
trusted
+            issuer registry. Increasing this value may delay how quickly 
updates
+            take effect.
+        </description>
+    </property>
+
+    <property>
+        <name>gateway.trustedoidcissuer.discovery.cache.ttl.secs</name>
+        <value>600</value>
+        <description>
+            TTL in seconds for the resolved JWKS URI per trusted issuer. After
+            expiry the JWKS URI is re-resolved on the next use. Use the admin
+            API refresh endpoint to force immediate re-resolution without 
waiting
+            for TTL expiry.
+        </description>
+    </property>
+
+    <property>
+        <name>gateway.trustedoidcissuer.discovery.connect.timeout.ms</name>
+        <value>3000</value>
+        <description>
+            HTTP connect timeout in milliseconds when fetching an OIDC 
discovery
+            document (/.well-known/openid-configuration). A short value fails 
fast
+            if the issuer endpoint is unreachable.
+        </description>
+    </property>
+
+    <property>
+        <name>gateway.trustedoidcissuer.discovery.read.timeout.ms</name>
+        <value>10000</value>
+        <description>
+            HTTP read timeout in milliseconds when fetching an OIDC discovery
+            document. Allows for slower issuer endpoints while bounding the
+            latency of a cache-miss resolution.
+        </description>
+    </property>
+    -->
+
 </configuration>
\ No newline at end of file
diff --git 
a/gateway-server/src/main/resources/createKnoxIDFTrustedOidcIssuersTable.sql 
b/gateway-server/src/main/resources/createKnoxIDFTrustedOidcIssuersTable.sql
new file mode 100644
index 000000000..a97a4a6cc
--- /dev/null
+++ b/gateway-server/src/main/resources/createKnoxIDFTrustedOidcIssuersTable.sql
@@ -0,0 +1,23 @@
+--  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.
+
+CREATE TABLE IF NOT EXISTS TRUSTED_OIDC_ISSUERS (
+    issuer_url    VARCHAR(2048) NOT NULL,
+    dynamic_jwks  BOOLEAN       DEFAULT false NOT NULL,
+    registered_at TIMESTAMP     NOT NULL,
+    registered_by VARCHAR(2048),
+    cluster_name  VARCHAR(256),
+    PRIMARY KEY (issuer_url)
+);
diff --git 
a/gateway-server/src/main/resources/createKnoxIDFTrustedOidcIssuersTableDerby.sql
 
b/gateway-server/src/main/resources/createKnoxIDFTrustedOidcIssuersTableDerby.sql
new file mode 100644
index 000000000..a3e77b465
--- /dev/null
+++ 
b/gateway-server/src/main/resources/createKnoxIDFTrustedOidcIssuersTableDerby.sql
@@ -0,0 +1,22 @@
+--  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.
+
+CREATE TABLE TRUSTED_OIDC_ISSUERS (
+    issuer_url    VARCHAR(2048) PRIMARY KEY NOT NULL,
+    dynamic_jwks  BOOLEAN       DEFAULT false NOT NULL,
+    registered_at TIMESTAMP     NOT NULL,
+    registered_by VARCHAR(2048),
+    cluster_name  VARCHAR(256)
+)
diff --git 
a/gateway-server/src/main/resources/createKnoxIDFTrustedOidcIssuersTableOracle.sql
 
b/gateway-server/src/main/resources/createKnoxIDFTrustedOidcIssuersTableOracle.sql
new file mode 100644
index 000000000..2c0de4bd0
--- /dev/null
+++ 
b/gateway-server/src/main/resources/createKnoxIDFTrustedOidcIssuersTableOracle.sql
@@ -0,0 +1,23 @@
+--  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.
+
+CREATE TABLE TRUSTED_OIDC_ISSUERS (
+    issuer_url    VARCHAR2(2048) NOT NULL,
+    dynamic_jwks  NUMBER(1)      DEFAULT 0 NOT NULL,
+    registered_at TIMESTAMP(6)   NOT NULL,
+    registered_by VARCHAR2(2048),
+    cluster_name  VARCHAR2(256),
+    PRIMARY KEY (issuer_url)
+)
diff --git 
a/gateway-server/src/test/java/org/apache/knox/gateway/services/AbstractGatewayServicesTest.java
 
b/gateway-server/src/test/java/org/apache/knox/gateway/services/AbstractGatewayServicesTest.java
index 57271324c..904111888 100644
--- 
a/gateway-server/src/test/java/org/apache/knox/gateway/services/AbstractGatewayServicesTest.java
+++ 
b/gateway-server/src/test/java/org/apache/knox/gateway/services/AbstractGatewayServicesTest.java
@@ -68,7 +68,8 @@ public class AbstractGatewayServicesTest {
         ServiceType.GATEWAY_STATUS_SERVICE,
         ServiceType.LDAP_SERVICE,
         ServiceType.LDAP_ROLES_LOOKUP_SERVICE,
-        ServiceType.KNOXIDF_FEDERATED_IDENTITY_SERVICE
+        ServiceType.KNOXIDF_FEDERATED_IDENTITY_SERVICE,
+        ServiceType.TRUSTED_OIDC_ISSUER_SERVICE
     };
 
     assertNotEquals(ServiceType.values(), orderedServiceTypes);
diff --git 
a/gateway-server/src/test/java/org/apache/knox/gateway/services/knoxidf/trustedoidcissuer/TrustedOidcIssuerTest.java
 
b/gateway-server/src/test/java/org/apache/knox/gateway/services/knoxidf/trustedoidcissuer/TrustedOidcIssuerTest.java
new file mode 100644
index 000000000..a728e99ac
--- /dev/null
+++ 
b/gateway-server/src/test/java/org/apache/knox/gateway/services/knoxidf/trustedoidcissuer/TrustedOidcIssuerTest.java
@@ -0,0 +1,71 @@
+/*
+ * 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.services.knoxidf.trustedoidcissuer;
+
+import org.junit.Test;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.time.Instant;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+public class TrustedOidcIssuerTest {
+
+  @Test
+  public void testGetters() {
+    Instant now = Instant.now();
+    TrustedOidcIssuer issuer = new TrustedOidcIssuer(
+        "https://issuer.example.com";, true, "cluster-a", now, 
"[email protected]");
+
+    assertEquals("https://issuer.example.com";, issuer.getIssuerUrl());
+    assertTrue(issuer.isDynamicJwks());
+    assertEquals("cluster-a", issuer.getClusterName());
+    assertEquals(now, issuer.getRegisteredAt());
+    assertEquals("[email protected]", issuer.getRegisteredBy());
+  }
+
+  @Test
+  public void testNullableOptionalFields() {
+    TrustedOidcIssuer issuer = new TrustedOidcIssuer(
+        "https://issuer.example.com";, false, null, Instant.now(), null);
+
+    assertNull("clusterName should be nullable", issuer.getClusterName());
+    assertNull("registeredBy should be nullable", issuer.getRegisteredBy());
+    assertFalse(issuer.isDynamicJwks());
+  }
+
+  @Test
+  public void testAllFieldsAreFinal() {
+    for (Field field : TrustedOidcIssuer.class.getDeclaredFields()) {
+      assertTrue("Field '" + field.getName() + "' must be final for 
immutability",
+          Modifier.isFinal(field.getModifiers()));
+    }
+  }
+
+  @Test
+  public void testNoSetterMethods() {
+    for (Method method : TrustedOidcIssuer.class.getDeclaredMethods()) {
+      assertFalse("Setter found in immutable POJO: " + method.getName(),
+          method.getName().startsWith("set"));
+    }
+  }
+}
diff --git 
a/gateway-server/src/test/java/org/apache/knox/gateway/services/knoxidf/trustedoidcissuer/TrustedOidcIssuersSchemaTest.java
 
b/gateway-server/src/test/java/org/apache/knox/gateway/services/knoxidf/trustedoidcissuer/TrustedOidcIssuersSchemaTest.java
new file mode 100644
index 000000000..e8cb56015
--- /dev/null
+++ 
b/gateway-server/src/test/java/org/apache/knox/gateway/services/knoxidf/trustedoidcissuer/TrustedOidcIssuersSchemaTest.java
@@ -0,0 +1,121 @@
+/*
+ * 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.services.knoxidf.trustedoidcissuer;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.knox.gateway.database.AbstractDataSourceFactory;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Validates that the TRUSTED_OIDC_ISSUERS DDL scripts parse and execute
+ * correctly against in-memory databases.
+ */
+public class TrustedOidcIssuersSchemaTest {
+
+  private static final String DERBY_DB = "trustedissuers";
+  private static final String DERBY_URL = "jdbc:derby:memory:" + DERBY_DB + 
";create=true";
+  private static final String DERBY_SHUTDOWN_URL = "jdbc:derby:memory:" + 
DERBY_DB + ";shutdown=true";
+  private static final String HSQL_URL = 
"jdbc:hsqldb:mem:trustedissuersschema;ifexists=false";
+  private static final String HSQL_USER = "SA";
+  private static final String HSQL_PASSWORD = "";
+
+  private static Connection derbyConn;
+  private static Connection hsqlConn;
+
+  @BeforeClass
+  public static void setUp() throws SQLException {
+    derbyConn = DriverManager.getConnection(DERBY_URL);
+    hsqlConn = DriverManager.getConnection(HSQL_URL, HSQL_USER, HSQL_PASSWORD);
+  }
+
+  @AfterClass
+  public static void tearDown() throws Exception {
+    // HSQLDB: follow JDBCTokenStateServiceTest pattern — new connection for 
SHUTDOWN
+    try (Connection conn = DriverManager.getConnection(HSQL_URL, HSQL_USER, 
HSQL_PASSWORD);
+         Statement stmt = conn.createStatement()) {
+      stmt.execute("SHUTDOWN");
+    }
+
+    // Derby: close the shared connection before issuing shutdown
+    if (derbyConn != null && !derbyConn.isClosed()) {
+      derbyConn.close();
+    }
+    try {
+      DriverManager.getConnection(DERBY_SHUTDOWN_URL);
+    } catch (SQLException e) {
+      // Derby signals a successful single-DB shutdown as error code 45000, 
state "08006"
+      if (!(e.getErrorCode() == 45000 && "08006".equals(e.getSQLState()))) {
+        throw e;
+      }
+    }
+  }
+
+  /**
+   * The Derby-dialect DDL must execute without error in a Derby in-memory
+   * database and leave the table queryable.
+   */
+  @Test
+  public void testDerbyDdlCreatesTable() throws Exception {
+    try (Statement stmt = derbyConn.createStatement()) {
+      
stmt.execute(loadSql(AbstractDataSourceFactory.DERBY_KNOXIDF_TRUSTED_OIDC_ISSUERS_TABLE_SQL));
+      try (ResultSet rs = stmt.executeQuery("SELECT COUNT(*) FROM 
TRUSTED_OIDC_ISSUERS")) {
+        assertTrue(rs.next());
+        assertEquals(0, rs.getInt(1));
+      }
+    }
+  }
+
+  /**
+   * The standard SQL script uses IF NOT EXISTS. Running the script twice must
+   * not throw, confirming idempotency.
+   */
+  @Test
+  public void testStandardSqlIdempotent() throws Exception {
+    String sql = 
loadSql(AbstractDataSourceFactory.KNOXIDF_TRUSTED_OIDC_ISSUERS_TABLE_SQL);
+    try (Statement stmt = hsqlConn.createStatement()) {
+      stmt.execute(sql);
+      // Second execution must succeed due to IF NOT EXISTS
+      stmt.execute(sql);
+      try (ResultSet rs = stmt.executeQuery("SELECT COUNT(*) FROM 
TRUSTED_OIDC_ISSUERS")) {
+        assertTrue(rs.next());
+        assertEquals(0, rs.getInt(1));
+      }
+    }
+  }
+
+  private static String loadSql(String fileName) throws IOException {
+    try (InputStream is = 
TrustedOidcIssuersSchemaTest.class.getClassLoader().getResourceAsStream(fileName))
 {
+      assertNotNull("SQL file not found on classpath: " + fileName, is);
+      return IOUtils.toString(is, StandardCharsets.UTF_8);
+    }
+  }
+}
diff --git 
a/gateway-spi/src/main/java/org/apache/knox/gateway/services/ServiceType.java 
b/gateway-spi/src/main/java/org/apache/knox/gateway/services/ServiceType.java
index 21794bc21..85afaefae 100644
--- 
a/gateway-spi/src/main/java/org/apache/knox/gateway/services/ServiceType.java
+++ 
b/gateway-spi/src/main/java/org/apache/knox/gateway/services/ServiceType.java
@@ -41,7 +41,8 @@ public enum ServiceType {
   GATEWAY_STATUS_SERVICE("GatewayStatusService"),
   LDAP_SERVICE("LDAPService"),
   LDAP_ROLES_LOOKUP_SERVICE("LDAPRoleLookupService"),
-  KNOXIDF_FEDERATED_IDENTITY_SERVICE("KnoxIDFFederatedIdentityService");
+  KNOXIDF_FEDERATED_IDENTITY_SERVICE("KnoxIDFFederatedIdentityService"),
+  TRUSTED_OIDC_ISSUER_SERVICE("TrustedOidcIssuerService");
 
   private final String serviceTypeName;
   private final String shortName;
diff --git 
a/gateway-spi/src/main/java/org/apache/knox/gateway/services/knoxidf/trustedoidcissuer/TrustedOidcIssuer.java
 
b/gateway-spi/src/main/java/org/apache/knox/gateway/services/knoxidf/trustedoidcissuer/TrustedOidcIssuer.java
new file mode 100644
index 000000000..fd4622209
--- /dev/null
+++ 
b/gateway-spi/src/main/java/org/apache/knox/gateway/services/knoxidf/trustedoidcissuer/TrustedOidcIssuer.java
@@ -0,0 +1,63 @@
+/*
+ * 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.services.knoxidf.trustedoidcissuer;
+
+import java.time.Instant;
+
+public final class TrustedOidcIssuer {
+
+  private final String issuerUrl;
+  private final boolean dynamicJwks;
+  private final String clusterName;
+  private final Instant registeredAt;
+  private final String registeredBy;
+
+  public TrustedOidcIssuer(String issuerUrl, boolean dynamicJwks, String 
clusterName,
+      Instant registeredAt, String registeredBy) {
+    this.issuerUrl = issuerUrl;
+    this.dynamicJwks = dynamicJwks;
+    this.clusterName = clusterName;
+    this.registeredAt = registeredAt;
+    this.registeredBy = registeredBy;
+  }
+
+  public String getIssuerUrl() {
+    return issuerUrl;
+  }
+
+  public boolean isDynamicJwks() {
+    return dynamicJwks;
+  }
+
+  /**
+   * @return the cluster name this issuer belongs to, or null if not 
cluster-scoped
+   */
+  public String getClusterName() {
+    return clusterName;
+  }
+
+  public Instant getRegisteredAt() {
+    return registeredAt;
+  }
+
+  /**
+   * @return the identity that registered this issuer, or null if not recorded
+   */
+  public String getRegisteredBy() {
+    return registeredBy;
+  }
+}
diff --git 
a/gateway-spi/src/main/java/org/apache/knox/gateway/services/knoxidf/trustedoidcissuer/TrustedOidcIssuerService.java
 
b/gateway-spi/src/main/java/org/apache/knox/gateway/services/knoxidf/trustedoidcissuer/TrustedOidcIssuerService.java
new file mode 100644
index 000000000..273dbfa37
--- /dev/null
+++ 
b/gateway-spi/src/main/java/org/apache/knox/gateway/services/knoxidf/trustedoidcissuer/TrustedOidcIssuerService.java
@@ -0,0 +1,96 @@
+/*
+ * 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.services.knoxidf.trustedoidcissuer;
+
+import org.apache.knox.gateway.services.Service;
+
+import java.util.List;
+import java.util.Optional;
+
+/**
+ * Gateway service managing the registry of OIDC issuers trusted for JWT
+ * verification in Knox. For issuers registered for dynamic JWKS discovery,
+ * resolves JWKS URIs via OpenID Connect Discovery 1.0
+ * (https://openid.net/specs/openid-connect-discovery-1_0.html) rather than
+ * requiring statically configured JWKS endpoints.
+ */
+public interface TrustedOidcIssuerService extends Service {
+
+  /**
+   * Returns {@code true} if the given issuer URL is currently registered as
+   * trusted. This is the primary SSRF gate: callers must verify trust before
+   * requesting any external resource associated with an issuer.
+   */
+  boolean isTrusted(String issuerUrl);
+
+  /**
+   * Returns {@code true} if the given issuer URL is trusted and configured for
+   * OIDC discovery-based JWKS resolution. Returns {@code false} if the issuer
+   * is not trusted, or is trusted but configured for static JWKS only.
+   * <p>
+   * This method combines the trust check with the discovery-mode check.
+   * Callers may use it as a single guard without separately calling
+   * {@link #isTrusted(String)}.
+   */
+  boolean isDynamicJwks(String issuerUrl);
+
+  /**
+   * Resolves the JWKS URI for the given issuer URL using OIDC discovery.
+   * Callers should verify that the issuer is trusted and configured for OIDC
+   * discovery via {@link #isDynamicJwks(String)} before calling this method,
+   * as that check covers both conditions.
+   * <p>
+   * Returns {@link Optional#empty()} in all failure cases — including issuer 
not
+   * trusted, dynamic JWKS not configured, discovery document unreachable or
+   * malformed, or any internal error. Failure details are logged internally 
for
+   * troubleshooting. Callers should treat an empty result uniformly as
+   * "no trusted JWKS URI available" without branching on the failure cause.
+   */
+  Optional<String> resolveJwksUri(String issuerUrl);
+
+  /**
+   * Forces re-resolution of the JWKS URI for the given issuer URL, discarding
+   * any previously resolved value. Use this when a resolved JWKS URI is 
suspected
+   * to be stale (for example, if an issuer has changed its JWKS endpoint).
+   * Has no effect if the issuer is not registered or does not use OIDC 
discovery.
+   */
+  void refreshJwksUri(String issuerUrl);
+
+  /**
+   * Registers a new trusted OIDC issuer.
+   *
+   * @throws IllegalStateException if the maximum registered issuer limit is
+   *     reached
+   * @throws RuntimeException if registration fails due to a storage error such
+   *     as a duplicate issuer URL or a database failure
+   */
+  void register(TrustedOidcIssuer issuer);
+
+  /**
+   * Removes the given issuer URL from the trusted registry and invalidates any
+   * previously resolved JWKS URI for that issuer. Returns silently if the 
issuer
+   * is not currently registered.
+   *
+   * @throws RuntimeException if removal fails due to a storage error
+   */
+  void deregister(String issuerUrl);
+
+  /**
+   * Returns all currently registered trusted issuers.
+   */
+  List<TrustedOidcIssuer> list();
+}

Reply via email to