You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by ro...@apache.org on 2023/07/04 09:59:54 UTC

[sling-whiteboard] branch master updated: oidc-rp: rewrite Endpoints as a OidcProviderMetadataRegistry component

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

rombert pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-whiteboard.git


The following commit(s) were added to refs/heads/master by this push:
     new b39b5856 oidc-rp: rewrite Endpoints as a OidcProviderMetadataRegistry component
b39b5856 is described below

commit b39b58568a8c619acddf3f08c7a2d4ecf45d1d82
Author: Robert Munteanu <ro...@apache.org>
AuthorDate: Tue Jul 4 12:59:43 2023 +0300

    oidc-rp: rewrite Endpoints as a OidcProviderMetadataRegistry component
---
 .../sling/servlets/oidc_rp/impl/Endpoints.java     | 56 ----------------------
 .../servlets/oidc_rp/impl/OidcCallbackServlet.java | 14 +++---
 .../oidc_rp/impl/OidcEntryPointServlet.java        | 14 +++---
 .../oidc_rp/impl/OidcProviderMetadataRegistry.java | 51 ++++++++++++++++++++
 4 files changed, 66 insertions(+), 69 deletions(-)

diff --git a/org.apache.sling.servlets.oidc-rp/src/main/java/org/apache/sling/servlets/oidc_rp/impl/Endpoints.java b/org.apache.sling.servlets.oidc-rp/src/main/java/org/apache/sling/servlets/oidc_rp/impl/Endpoints.java
deleted file mode 100644
index 892ca7ab..00000000
--- a/org.apache.sling.servlets.oidc-rp/src/main/java/org/apache/sling/servlets/oidc_rp/impl/Endpoints.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * 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.sling.servlets.oidc_rp.impl;
-
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.net.URI;
-import java.net.http.HttpClient;
-import java.net.http.HttpRequest;
-import java.net.http.HttpResponse;
-import java.net.http.HttpResponse.BodyHandlers;
-
-import javax.json.Json;
-import javax.json.JsonObject;
-import javax.json.JsonReader;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-record Endpoints(String authorizationEndpoint, String tokenEndpoint) {
-
-    private static final Logger logger = LoggerFactory.getLogger(Endpoints.class);
-
-    // TODO - cache endpoints, move to service?
-    // TODO - can we reuse code from the Nimbus SDK?
-    static Endpoints discover(String base, HttpClient client) throws IOException, InterruptedException {
-        logger.info("Initiating discovery request for baseUrl {}", base);
-        HttpRequest discoveryRequest = HttpRequest.newBuilder()
-                .uri(URI.create(base + "/.well-known/openid-configuration"))
-                .build();
-
-        HttpResponse<byte[]> response = client.send(discoveryRequest, BodyHandlers.ofByteArray());
-        try (JsonReader reader = Json.createReader(new ByteArrayInputStream(response.body()))) {
-            JsonObject cfg = reader.readObject();
-            String authEndpoint = cfg.getJsonString("authorization_endpoint").getString();
-            String tokenEndpoint = cfg.getJsonString("token_endpoint").getString();
-            Endpoints ep = new Endpoints(authEndpoint, tokenEndpoint);
-            logger.info("Discovered auth_endpoint {} , token_endpoint {}", ep.authorizationEndpoint(), ep.tokenEndpoint());
-            return ep;
-        }
-    }
-}
\ No newline at end of file
diff --git a/org.apache.sling.servlets.oidc-rp/src/main/java/org/apache/sling/servlets/oidc_rp/impl/OidcCallbackServlet.java b/org.apache.sling.servlets.oidc-rp/src/main/java/org/apache/sling/servlets/oidc_rp/impl/OidcCallbackServlet.java
index 5c1e4673..3d15265f 100644
--- a/org.apache.sling.servlets.oidc-rp/src/main/java/org/apache/sling/servlets/oidc_rp/impl/OidcCallbackServlet.java
+++ b/org.apache.sling.servlets.oidc-rp/src/main/java/org/apache/sling/servlets/oidc_rp/impl/OidcCallbackServlet.java
@@ -22,7 +22,6 @@ import java.io.IOException;
 import java.net.URI;
 import java.net.URISyntaxException;
 import java.net.URLDecoder;
-import java.net.http.HttpClient;
 import java.nio.charset.StandardCharsets;
 import java.util.List;
 import java.util.Map;
@@ -58,6 +57,7 @@ import com.nimbusds.openid.connect.sdk.AuthenticationErrorResponse;
 import com.nimbusds.openid.connect.sdk.AuthenticationResponse;
 import com.nimbusds.openid.connect.sdk.AuthenticationResponseParser;
 import com.nimbusds.openid.connect.sdk.OIDCTokenResponse;
+import com.nimbusds.openid.connect.sdk.op.OIDCProviderMetadata;
 
 @Component(service = { Servlet.class })
 @SlingServletPaths(OidcCallbackServlet.PATH)
@@ -70,15 +70,18 @@ public class OidcCallbackServlet extends SlingAllMethodsServlet {
     private final Logger logger = LoggerFactory.getLogger(getClass());
     private final Map<String, OidcConnection> connections;
     private final OidcConnectionPersister persister;
+    private final OidcProviderMetadataRegistry metadataRegistry;
 
     static String getCallbackUri(HttpServletRequest request) {
         return request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + PATH;
     }
 
     @Activate
-    public OidcCallbackServlet(@Reference(policyOption = GREEDY) List<OidcConnection> connections, @Reference OidcConnectionPersister persister) {
+    public OidcCallbackServlet(@Reference(policyOption = GREEDY) List<OidcConnection> connections, @Reference OidcConnectionPersister persister,
+            @Reference OidcProviderMetadataRegistry metadataRegistry) {
         this.connections = connections.stream()
                 .collect(Collectors.toMap( OidcConnection::name, Function.identity()));
+        this.metadataRegistry = metadataRegistry;
         this.persister = persister;
     }
 
@@ -126,11 +129,10 @@ public class OidcCallbackServlet extends SlingAllMethodsServlet {
             
             AuthorizationCode code = new AuthorizationCode(authCode);
             
-            HttpClient client = HttpClient.newHttpClient();
-            Endpoints ep = Endpoints.discover(connection.baseUrl(), client);
+            OIDCProviderMetadata providerMetadata = metadataRegistry.getProviderMetadata(connection.baseUrl());
             
             TokenRequest tokenRequest = new TokenRequest(
-                new URI(ep.tokenEndpoint()),
+                providerMetadata.getTokenEndpointURI(),
                 clientCredentials,
                 new AuthorizationCodeGrant(code, new URI(getCallbackUri(request)))
             );
@@ -163,8 +165,6 @@ public class OidcCallbackServlet extends SlingAllMethodsServlet {
             }
         } catch (ParseException | URISyntaxException | IOException e) {
             throw new ServletException(e);
-        } catch (InterruptedException e1) {
-            Thread.currentThread().interrupt();
         }
     }
 
diff --git a/org.apache.sling.servlets.oidc-rp/src/main/java/org/apache/sling/servlets/oidc_rp/impl/OidcEntryPointServlet.java b/org.apache.sling.servlets.oidc-rp/src/main/java/org/apache/sling/servlets/oidc_rp/impl/OidcEntryPointServlet.java
index f9853b0d..8b67aaf9 100644
--- a/org.apache.sling.servlets.oidc-rp/src/main/java/org/apache/sling/servlets/oidc_rp/impl/OidcEntryPointServlet.java
+++ b/org.apache.sling.servlets.oidc-rp/src/main/java/org/apache/sling/servlets/oidc_rp/impl/OidcEntryPointServlet.java
@@ -23,7 +23,6 @@ import static org.osgi.service.component.annotations.ReferencePolicyOption.GREED
 import java.io.IOException;
 import java.net.URI;
 import java.net.URISyntaxException;
-import java.net.http.HttpClient;
 import java.util.List;
 import java.util.Map;
 import java.util.function.Function;
@@ -50,6 +49,7 @@ import com.nimbusds.oauth2.sdk.id.ClientID;
 import com.nimbusds.oauth2.sdk.id.State;
 import com.nimbusds.openid.connect.sdk.AuthenticationRequest;
 import com.nimbusds.openid.connect.sdk.Nonce;
+import com.nimbusds.openid.connect.sdk.op.OIDCProviderMetadata;
 
 @Component(service = { Servlet.class })
 @SlingServletPaths(OidcEntryPointServlet.PATH)
@@ -62,10 +62,14 @@ public class OidcEntryPointServlet extends SlingAllMethodsServlet {
     
     private final Map<String, OidcConnection> connections;
 
+    private final OidcProviderMetadataRegistry metadataRegistry;
+
     @Activate
-    public OidcEntryPointServlet(@Reference(policyOption = GREEDY) List<OidcConnection> connections) {
+    public OidcEntryPointServlet(@Reference(policyOption = GREEDY) List<OidcConnection> connections,
+            @Reference OidcProviderMetadataRegistry metadataRegistry) {
         this.connections = connections.stream()
                 .collect(Collectors.toMap( OidcConnection::name, Function.identity()));
+        this.metadataRegistry = metadataRegistry;
     }
     @Override
     protected void doGet(SlingHttpServletRequest request, SlingHttpServletResponse response)
@@ -89,7 +93,7 @@ public class OidcEntryPointServlet extends SlingAllMethodsServlet {
             throw new ServletException("Misconfigured baseUrl");
         try {
 
-            Endpoints ep = Endpoints.discover(connection.baseUrl(), HttpClient.newHttpClient());
+            OIDCProviderMetadata providerMetadata = metadataRegistry.getProviderMetadata(connection.baseUrl());
 
             // The client ID provisioned by the OpenID provider when
             // the client was registered
@@ -113,7 +117,7 @@ public class OidcEntryPointServlet extends SlingAllMethodsServlet {
                     new ResponseType("code"),
                     new Scope(connection.scopes()),
                     clientID, callback)
-                .endpointURI(new URI(ep.authorizationEndpoint()))
+                .endpointURI(providerMetadata.getAuthorizationEndpointURI())
                 .state(state)
                 .nonce(nonce)
                 .customParameter("access_type", "offline") // request refresh token. TODO - is this Google-specific?
@@ -122,8 +126,6 @@ public class OidcEntryPointServlet extends SlingAllMethodsServlet {
             response.sendRedirect(authRequest.toURI().toString());
         } catch (URISyntaxException e) {
             throw new ServletException(e);
-        } catch (InterruptedException e) {
-            Thread.currentThread().interrupt();
         }
     }
 }
diff --git a/org.apache.sling.servlets.oidc-rp/src/main/java/org/apache/sling/servlets/oidc_rp/impl/OidcProviderMetadataRegistry.java b/org.apache.sling.servlets.oidc-rp/src/main/java/org/apache/sling/servlets/oidc_rp/impl/OidcProviderMetadataRegistry.java
new file mode 100644
index 00000000..ddc75e87
--- /dev/null
+++ b/org.apache.sling.servlets.oidc-rp/src/main/java/org/apache/sling/servlets/oidc_rp/impl/OidcProviderMetadataRegistry.java
@@ -0,0 +1,51 @@
+/*
+ * 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.sling.servlets.oidc_rp.impl;
+
+import java.io.IOException;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import org.osgi.service.component.annotations.Component;
+
+import com.nimbusds.oauth2.sdk.GeneralException;
+import com.nimbusds.oauth2.sdk.id.Issuer;
+import com.nimbusds.openid.connect.sdk.op.OIDCProviderMetadata;
+
+/**
+ * A registry for provider metadata
+ * 
+ * <p>Encapsulates the logic for retrieving the {@link #OIDCProviderMetadata} for a given 
+ * connection.</p>
+ * 
+ * <p>Maintains a best-effort, unbounded, cache for the metadata, on the assumption that
+ * there will be a small number of OIDC connections configured.</p>
+ */
+@Component(service = OidcProviderMetadataRegistry.class)
+public class OidcProviderMetadataRegistry {
+    private final ConcurrentMap<String, OIDCProviderMetadata> cache = new ConcurrentHashMap<>();
+
+    public OIDCProviderMetadata getProviderMetadata(String base) {
+        return cache.computeIfAbsent(base, s -> {
+            try {
+                return OIDCProviderMetadata.resolve(new Issuer(s));
+            } catch (GeneralException | IOException e) {
+                throw new RuntimeException(e);
+            }
+        });
+    }
+}
\ No newline at end of file