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 } : {};
+ },
+ }),
});
}