From a04ea977846da607bffa285e159f4eadd517bf46 Mon Sep 17 00:00:00 2001
From: Desirree Adegunle <87389186+dess890@users.noreply.github.com>
Date: Wed, 9 Jul 2025 18:27:07 -0400
Subject: [PATCH 1/4] fea(index): initialized graphistry client-api class and
login using the auth token
---
projects/client-api-react/src/index.js | 12 ++++++++++--
1 file changed, 10 insertions(+), 2 deletions(-)
diff --git a/projects/client-api-react/src/index.js b/projects/client-api-react/src/index.js
index 9b1e7ea..b6fa117 100644
--- a/projects/client-api-react/src/index.js
+++ b/projects/client-api-react/src/index.js
@@ -104,7 +104,8 @@ const propTypes = {
onLabelsUpdate: PropTypes.func,
selectionUpdateOptions: PropTypes.object,
- queryParamExtra: PropTypes.object
+ queryParamExtra: PropTypes.object,
+ client: PropTypes.object
};
const defaultProps = {
@@ -465,13 +466,16 @@ const Graphistry = forwardRef((props, ref) => {
onUpdateObservableG,
onSelectionUpdate,
onLabelsUpdate,
- selectionUpdateOptions
+ selectionUpdateOptions,
+ client
} = props;
const [loading, setLoading] = useState(!!props.loading);
const [dataset, setDataset] = useState(props.dataset);
const [loadingMessage, setLoadingMessage] = useState(props.loadingMessage || '');
+ const authToken = client && client._token && client.authTokenValid() ? client._token : null;
+
const [g, setG] = useState(null);
const [gObs, setGObs] = useState(null);
const [gSub, setGSub] = useState(null);
@@ -569,6 +573,10 @@ const Graphistry = forwardRef((props, ref) => {
}
}
+ if (authToken) {
+ extraParams += `&authToken=${encodeURIComponent(authToken)}`;
+ }
+
const url = `${graphistryHost || ''}/graph/graph.html${''
}?play=${playNormalized
}&info=${showInfo
From aede36dc562108d2872e8314482d11fb6025b8a8 Mon Sep 17 00:00:00 2001
From: Desirree Adegunle <87389186+dess890@users.noreply.github.com>
Date: Mon, 14 Jul 2025 11:46:00 -0400
Subject: [PATCH 2/4] fix(index): abstracts authtoken (doesnt pass into url
params)
---
projects/client-api-react/src/index.js | 19 +++++++++++++------
1 file changed, 13 insertions(+), 6 deletions(-)
diff --git a/projects/client-api-react/src/index.js b/projects/client-api-react/src/index.js
index b6fa117..f0a6b53 100644
--- a/projects/client-api-react/src/index.js
+++ b/projects/client-api-react/src/index.js
@@ -366,7 +366,8 @@ function generateIframeRef({
url, dataset, props,
axesMap,
iframeStyle, iframeClassName, iframeProps, allowFullScreen,
- tolerateLoadErrors
+ tolerateLoadErrors,
+ authToken
}) {
console.debug('@generateIframeRef', { url, dataset, props, axesMap, iframeStyle, iframeClassName, iframeProps, allowFullScreen, tolerateLoadErrors });
@@ -417,6 +418,15 @@ function generateIframeRef({
),
tap((g) => {
console.debug('new iframe all init updates handled, if any', g);
+ // for sending authToken to the iframe
+ if (authToken && iframe && iframe.contentWindow) {
+ console.debug('Sending auth token to iframe via postMessage');
+ iframe.contentWindow.postMessage({
+ type: 'auth',
+ authToken: authToken
+ }, '*');
+ }
+
setFirstRun(false);
if (props.onClientAPIConnected) {
console.debug('has onClientAPIConnected(), calling', props.onClientAPIConnected);
@@ -573,10 +583,6 @@ const Graphistry = forwardRef((props, ref) => {
}
}
- if (authToken) {
- extraParams += `&authToken=${encodeURIComponent(authToken)}`;
- }
-
const url = `${graphistryHost || ''}/graph/graph.html${''
}?play=${playNormalized
}&info=${showInfo
@@ -591,7 +597,8 @@ const Graphistry = forwardRef((props, ref) => {
url, dataset, props,
axesMap,
iframeStyle, iframeClassName, iframeProps, allowFullScreen,
- tolerateLoadErrors
+ tolerateLoadErrors,
+ authToken
});
const children = [
From 34a274148ca6a454b978113ad9520966714ac582 Mon Sep 17 00:00:00 2001
From: Desirree Adegunle <87389186+dess890@users.noreply.github.com>
Date: Fri, 18 Jul 2025 12:04:20 -0400
Subject: [PATCH 3/4] fea(readme): updated docs
---
README.md | 38 ++++++++++++++++++++++++++++++++++++--
1 file changed, 36 insertions(+), 2 deletions(-)
diff --git a/README.md b/README.md
index 1699ca9..4cf75e9 100644
--- a/README.md
+++ b/README.md
@@ -82,6 +82,38 @@ import { Graphistry } from '@graphistry/client-api-react';` // + variants for di
See [@graphistry/client-api-react project](projects/client-api-react/README.md), [interactive storybook docs](https://graphistry.github.io/graphistry-js/), and [Create React App project sample](projects/cra-test/README.md)
+### Authentication with Client API
+
+For secure authentication, create a `Client` instance and pass it to your components:
+
+```javascript
+import { Client, Dataset, EdgeFile, NodeFile } from '@graphistry/client-api';
+import { Graphistry } from '@graphistry/client-api-react';
+
+// Create authenticated client
+const client = new Client(
+ 'my_username',
+ 'my_password',
+ '', // org (optional)
+ 'https', // protocol
+ 'hub.graphistry.com' // host
+);
+
+// Use with React component
+return (
+
+);
+```
+
+This approach provides:
+- JWT-based authentication (vs deprecated API keys)
+- Automatic token management and refresh
+- Secure server-to-server communication
+
## @graphistry/node-api
@@ -140,9 +172,11 @@ To support server-acceleration and fast interactions, Graphistry decouples uploa
- You can configure your Graphistry server to run as http, https, or both
- Uploads require authentication
+ - **Recommended**: The `Client` class provides modern JWT-based authentication for both browser and Node.js environments
- The `node-api` client already uses the new JWT-based protocol ("API 3")
- - Deprecated: The clientside JavaScript convenience APIs still use the deprecrated "API 1" protocol (key-based), which lacks JWT-based authentication and authorization
+ - **Deprecated**: The clientside JavaScript convenience APIs still use the deprecated "API 1" protocol (key-based), which lacks JWT-based authentication and authorization
- We recommend clients instead use `fetch` or other HTTP callers to directly invoke the REST API: See how the `node-api` performs it
- The client JavaScript APIs will updated to the new JWT method alongside recent CORS and SSO updates; contact staff if you desire assistance
+ - **Deprecated**: Legacy API key authentication is still supported but will be phased out
- Sessions are based on unguessable web keys: sharing a secret ID means sharing read access
-- Datasets are immutable and thus their integrity is safe for sharing, while session state (e.g., filters) are writable: share a copy when in doubt
+- Datasets are immutable and thus their integrity is safe for sharing, while session state (e.g., filters) are writable: share a copy when in doubt
\ No newline at end of file
From d14eb6b95213ee64596131979d32019426496a9c Mon Sep 17 00:00:00 2001
From: Desirree Adegunle <87389186+dess890@users.noreply.github.com>
Date: Tue, 16 Sep 2025 11:27:02 -0400
Subject: [PATCH 4/4] fix(auth): add client JWT auth support to React component
---
projects/client-api-react/src/index.js | 30 ++++++++++++++++++--------
1 file changed, 21 insertions(+), 9 deletions(-)
diff --git a/projects/client-api-react/src/index.js b/projects/client-api-react/src/index.js
index f0a6b53..b120cd9 100644
--- a/projects/client-api-react/src/index.js
+++ b/projects/client-api-react/src/index.js
@@ -418,13 +418,24 @@ function generateIframeRef({
),
tap((g) => {
console.debug('new iframe all init updates handled, if any', g);
- // for sending authToken to the iframe
- if (authToken && iframe && iframe.contentWindow) {
- console.debug('Sending auth token to iframe via postMessage');
- iframe.contentWindow.postMessage({
- type: 'auth',
- authToken: authToken
- }, '*');
+
+ // store JWT token for iframe auth
+ if (authToken && client.authTokenValid()) {
+ try {
+ localStorage.setItem('graphistry_auth_token', authToken);
+ console.info('Stored JWT token for iframe authentication');
+
+ if (iframe && iframe.contentWindow) {
+ const targetOrigin = new URL(props.graphistryHost).origin;
+ iframe.contentWindow.postMessage({
+ type: 'auth-token-available',
+ agent: 'graphistryjs',
+ token: authToken
+ }, targetOrigin);
+ }
+ } catch (error) {
+ console.warn('Failed to store auth token:', error);
+ }
}
setFirstRun(false);
@@ -484,7 +495,7 @@ const Graphistry = forwardRef((props, ref) => {
const [dataset, setDataset] = useState(props.dataset);
const [loadingMessage, setLoadingMessage] = useState(props.loadingMessage || '');
- const authToken = client && client._token && client.authTokenValid() ? client._token : null;
+ const authToken = props.client && props.client.authTokenValid() ? props.client._token : null;
const [g, setG] = useState(null);
const [gObs, setGObs] = useState(null);
@@ -537,9 +548,10 @@ const Graphistry = forwardRef((props, ref) => {
return {
g,
+ client,
...exportedCalls
};
- }, [g]);
+ }, [g, client, dataset]);
useEffect(() => {
if (g && onSelectionUpdate) {