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

asf-gitbox-commits pushed a commit to branch fix/graphql-websocket-auth
in repository https://gitbox.apache.org/repos/asf/unomi.git


The following commit(s) were added to refs/heads/fix/graphql-websocket-auth by 
this push:
     new 4b5885003 Authenticate the GraphQL playground's subscription WebSocket
4b5885003 is described below

commit 4b58850039c63848b2b812c691f4fec790ea4c2d
Author: Serge Huber <[email protected]>
AuthorDate: Thu Sep 3 12:25:46 2026 +0200

    Authenticate the GraphQL playground's subscription WebSocket
    
    The shipped GraphiQL playground opened its subscription WebSocket with only 
a
    URL, so it presented no credential over a handshake that authenticates from 
the
    connection_init payload. The browser WebSocket API cannot set request 
headers on
    the upgrade, so reuse the Authorization the operator enters in GraphiQL's 
Headers
    tab (the same credential used for HTTP queries) and send it as the 
connection_init
    connectionParams, so HTTP and WebSocket subscriptions authenticate 
identically.
    
    Co-Authored-By: Claude Opus 4.8 <[email protected]>
---
 .../src/main/resources/assets/js/index.jsx         | 27 +++++++++++++++++++++-
 1 file changed, 26 insertions(+), 1 deletion(-)

diff --git a/graphql/graphql-ui/src/main/resources/assets/js/index.jsx 
b/graphql/graphql-ui/src/main/resources/assets/js/index.jsx
index a0869796f..013ea5ec4 100644
--- a/graphql/graphql-ui/src/main/resources/assets/js/index.jsx
+++ b/graphql/graphql-ui/src/main/resources/assets/js/index.jsx
@@ -33,10 +33,35 @@ function graphqlWsUrl() {
     return protocol + '//' + window.location.host + '/graphql';
 }
 
+// The browser WebSocket API cannot set request headers on the handshake, so 
the server authenticates a
+// subscription from the connection_init payload instead. Reuse the 
Authorization the operator already
+// enters in GraphiQL's "Headers" tab (the same credential used for HTTP admin 
queries) so HTTP and
+// WebSocket authenticate identically. GraphiQL persists that editor to 
localStorage.
+function authorizationFromHeadersEditor() {
+    try {
+        const stored = window.localStorage.getItem('graphiql:headers');
+        if (!stored) {
+            return null;
+        }
+        const headers = JSON.parse(stored);
+        const key = Object.keys(headers).find((name) => name.toLowerCase() === 
'authorization');
+        return key && headers[key] ? headers[key] : null;
+    } catch (e) {
+        return null;
+    }
+}
+
 function createFetcher() {
     return createGraphiQLFetcher({
         url: graphqlHttpUrl(),
-        wsClient: createClient({ url: graphqlWsUrl() }),
+        wsClient: createClient({
+            url: graphqlWsUrl(),
+            // Evaluated on each (re)connect; sent as the connection_init 
payload.
+            connectionParams: () => {
+                const authorization = authorizationFromHeadersEditor();
+                return authorization ? { Authorization: authorization } : {};
+            },
+        }),
     });
 }
 

Reply via email to