diff --git a/web-integrations/google-secure-signals/react-client-side/package-lock.json b/web-integrations/google-secure-signals/react-client-side/package-lock.json
index cb3da66..5add2a3 100644
--- a/web-integrations/google-secure-signals/react-client-side/package-lock.json
+++ b/web-integrations/google-secure-signals/react-client-side/package-lock.json
@@ -23,6 +23,7 @@
},
"devDependencies": {
"@babel/plugin-proposal-private-property-in-object": "^7.21.0",
+ "cross-env": "^7.0.3",
"dotenv-cli": "^10.0.0"
}
},
@@ -5495,6 +5496,24 @@
"node": ">=14"
}
},
+ "node_modules/cross-env": {
+ "version": "7.0.3",
+ "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz",
+ "integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==",
+ "dev": true,
+ "dependencies": {
+ "cross-spawn": "^7.0.1"
+ },
+ "bin": {
+ "cross-env": "src/bin/cross-env.js",
+ "cross-env-shell": "src/bin/cross-env-shell.js"
+ },
+ "engines": {
+ "node": ">=10.14",
+ "npm": ">=6",
+ "yarn": ">=1"
+ }
+ },
"node_modules/cross-spawn": {
"version": "7.0.6",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
diff --git a/web-integrations/google-secure-signals/react-client-side/package.json b/web-integrations/google-secure-signals/react-client-side/package.json
index 60e3894..10cd53c 100644
--- a/web-integrations/google-secure-signals/react-client-side/package.json
+++ b/web-integrations/google-secure-signals/react-client-side/package.json
@@ -18,6 +18,7 @@
},
"devDependencies": {
"@babel/plugin-proposal-private-property-in-object": "^7.21.0",
+ "cross-env": "^7.0.3",
"dotenv-cli": "^10.0.0"
},
"overrides": {
@@ -28,7 +29,7 @@
},
"scripts": {
"start": "node server.js",
- "dev": "PORT=3044 dotenv -e ../../../.env -- react-scripts start",
+ "dev": "cross-env PORT=3044 dotenv -e ../../../.env -- react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
diff --git a/web-integrations/google-secure-signals/react-client-side/server.js b/web-integrations/google-secure-signals/react-client-side/server.js
index 871a320..e96cf72 100644
--- a/web-integrations/google-secure-signals/react-client-side/server.js
+++ b/web-integrations/google-secure-signals/react-client-side/server.js
@@ -1,6 +1,11 @@
-require('dotenv').config({ path: '../../../.env' });
-
-console.log('process.env', process.env);
+// Load environment variables from .env file (for local development)
+// In Docker, env_file in docker-compose.yml already provides these, so this is optional
+try {
+ require('dotenv').config({ path: '../../../.env' });
+} catch (error) {
+ // Silently fail if .env file doesn't exist (e.g., in Docker where env vars are provided via env_file)
+ // This is expected behavior when running in Docker
+}
const fs = require('fs');
const path = require('path');
@@ -21,9 +26,19 @@ app.get('/ops/healthcheck', (req, res) => {
// Helper function to serve index.html with environment variable replacement
function serveIndexHtml(req, res) {
// Try build directory first (production), then public (development)
- let indexPath = path.join(__dirname, 'build', 'index.html');
- if (!fs.existsSync(indexPath)) {
- indexPath = path.join(__dirname, 'public', 'index.html');
+ const buildPath = path.join(__dirname, 'build');
+ const buildIndexPath = path.join(buildPath, 'index.html');
+ const publicIndexPath = path.join(__dirname, 'public', 'index.html');
+
+ let indexPath;
+ if (fs.existsSync(buildIndexPath)) {
+ indexPath = buildIndexPath;
+ } else if (fs.existsSync(publicIndexPath)) {
+ console.warn('Warning: build directory not found. Serving from public directory. Run "npm run build" to build the React app.');
+ indexPath = publicIndexPath;
+ } else {
+ res.status(500).send('Error: Neither build nor public index.html found. Please run "npm run build" first.');
+ return;
}
let html = fs.readFileSync(indexPath, 'utf8');
@@ -36,18 +51,40 @@ function serveIndexHtml(req, res) {
html = html.replace(/__UID_JS_SDK_URL_PLACEHOLDER__/g, uidJsSdkUrl);
html = html.replace(/__PUBLIC_URL_PLACEHOLDER__/g, publicUrl);
- // Debug: log if replacement happened
+ // Verify replacement worked - if placeholder still exists, log error
if (html.includes('__UID_JS_SDK_URL_PLACEHOLDER__')) {
- console.warn('Warning: Placeholder __UID_JS_SDK_URL_PLACEHOLDER__ was not replaced in', indexPath);
+ console.error('ERROR: Placeholder __UID_JS_SDK_URL_PLACEHOLDER__ was not replaced in', indexPath);
}
+ // Inject runtime environment variables as a script tag
+ // This allows the React app to read environment variables at runtime (for Kubernetes)
+ const runtimeEnv = {
+ REACT_APP_UID_JS_SDK_NAME: process.env.REACT_APP_UID_JS_SDK_NAME,
+ REACT_APP_UID_CLIENT_BASE_URL: process.env.REACT_APP_UID_CLIENT_BASE_URL,
+ REACT_APP_UID_SECURE_SIGNALS_SDK_URL: process.env.REACT_APP_UID_SECURE_SIGNALS_SDK_URL,
+ REACT_APP_UID_SECURE_SIGNALS_STORAGE_KEY: process.env.REACT_APP_UID_SECURE_SIGNALS_STORAGE_KEY,
+ REACT_APP_IDENTITY_NAME: process.env.REACT_APP_IDENTITY_NAME,
+ REACT_APP_DOCS_BASE_URL: process.env.REACT_APP_DOCS_BASE_URL,
+ REACT_APP_UID_CSTG_SUBSCRIPTION_ID: process.env.REACT_APP_UID_CSTG_SUBSCRIPTION_ID,
+ REACT_APP_UID_CSTG_SERVER_PUBLIC_KEY: process.env.REACT_APP_UID_CSTG_SERVER_PUBLIC_KEY,
+ };
+
+ // Inject script tag before closing tag
+ const envScript = ``;
+ html = html.replace('', `${envScript}`);
+
+ // Set content type explicitly
+ res.setHeader('Content-Type', 'text/html; charset=utf-8');
res.send(html);
}
-// Route handler for index - must be before static middleware
+// Route handlers for index - must be before static middleware
+// These must be registered BEFORE express.static() to ensure they intercept index.html requests
app.get('/', serveIndexHtml);
+app.get('/index.html', serveIndexHtml);
// Serve static files from build directory (production) or public (development)
+// IMPORTANT: This must come AFTER the route handlers to ensure index.html is processed by serveIndexHtml
const buildPath = path.join(__dirname, 'build');
const publicPath = path.join(__dirname, 'public');
@@ -74,5 +111,14 @@ app.get('*', (req, res, next) => {
});
app.listen(port, () => {
- console.log(`Example app listening at http://localhost:${port}`);
+ const buildPath = path.join(__dirname, 'build');
+ if (fs.existsSync(buildPath)) {
+ console.log(`Example app listening at http://localhost:${port}`);
+ console.log('Serving production build from build/ directory');
+ } else {
+ console.log(`Example app listening at http://localhost:${port}`);
+ console.warn('WARNING: build/ directory not found. Serving from public/ directory.');
+ console.warn('For production, run "npm run build" first.');
+ console.warn('For development, use "npm run dev" instead.');
+ }
});
diff --git a/web-integrations/google-secure-signals/react-client-side/src/SecureSignalsApp.tsx b/web-integrations/google-secure-signals/react-client-side/src/SecureSignalsApp.tsx
index 9ae8536..7d40c8f 100644
--- a/web-integrations/google-secure-signals/react-client-side/src/SecureSignalsApp.tsx
+++ b/web-integrations/google-secure-signals/react-client-side/src/SecureSignalsApp.tsx
@@ -7,23 +7,46 @@ declare global {
getAdvertisingToken: any;
google: any;
googletag: any;
+ __REACT_APP_ENV__?: {
+ REACT_APP_UID_JS_SDK_NAME?: string;
+ REACT_APP_UID_CLIENT_BASE_URL?: string;
+ REACT_APP_UID_SECURE_SIGNALS_SDK_URL?: string;
+ REACT_APP_UID_SECURE_SIGNALS_STORAGE_KEY?: string;
+ REACT_APP_IDENTITY_NAME?: string;
+ REACT_APP_DOCS_BASE_URL?: string;
+ REACT_APP_UID_CSTG_SUBSCRIPTION_ID?: string;
+ REACT_APP_UID_CSTG_SERVER_PUBLIC_KEY?: string;
+ };
}
}
// Declare global variables
declare const google: any;
-// Environment variables
-const UID_JS_SDK_NAME = process.env.REACT_APP_UID_JS_SDK_NAME;
-const UID_BASE_URL = process.env.REACT_APP_UID_CLIENT_BASE_URL;
-const SECURE_SIGNALS_SDK_URL = process.env.REACT_APP_UID_SECURE_SIGNALS_SDK_URL;
-const SECURE_SIGNALS_STORAGE_KEY = process.env.REACT_APP_UID_SECURE_SIGNALS_STORAGE_KEY;
-const IDENTITY_NAME = process.env.REACT_APP_IDENTITY_NAME;
-const DOCS_BASE_URL = process.env.REACT_APP_DOCS_BASE_URL;
+// Helper function to get environment variables from runtime (Kubernetes) or build-time
+function getEnvVar(key: string): string | undefined {
+ // First try runtime environment (injected by server.js for Kubernetes)
+ if (typeof window !== 'undefined' && window.__REACT_APP_ENV__) {
+ const value = window.__REACT_APP_ENV__[key as keyof typeof window.__REACT_APP_ENV__];
+ if (value !== undefined && value !== null) {
+ return value;
+ }
+ }
+ // Fallback to build-time environment variable
+ return process.env[key];
+}
+
+// Environment variables (read from runtime or build-time)
+const UID_JS_SDK_NAME = getEnvVar('REACT_APP_UID_JS_SDK_NAME') || '__uid2';
+const UID_BASE_URL = getEnvVar('REACT_APP_UID_CLIENT_BASE_URL');
+const SECURE_SIGNALS_SDK_URL = getEnvVar('REACT_APP_UID_SECURE_SIGNALS_SDK_URL') || '';
+const SECURE_SIGNALS_STORAGE_KEY = getEnvVar('REACT_APP_UID_SECURE_SIGNALS_STORAGE_KEY') || '';
+const IDENTITY_NAME = getEnvVar('REACT_APP_IDENTITY_NAME') || 'UID2';
+const DOCS_BASE_URL = getEnvVar('REACT_APP_DOCS_BASE_URL') || 'https://unifiedid.com/docs';
const clientSideIdentityOptions = {
- subscriptionId: process.env.REACT_APP_UID_CSTG_SUBSCRIPTION_ID,
- serverPublicKey: process.env.REACT_APP_UID_CSTG_SERVER_PUBLIC_KEY,
+ subscriptionId: getEnvVar('REACT_APP_UID_CSTG_SUBSCRIPTION_ID'),
+ serverPublicKey: getEnvVar('REACT_APP_UID_CSTG_SERVER_PUBLIC_KEY'),
};
const SecureSignalsApp = () => {
@@ -49,10 +72,19 @@ const SecureSignalsApp = () => {
const loginAttemptedRef = useRef(false);
// Helper function to get SDK instance
- const getSDK = () => window[UID_JS_SDK_NAME];
+ const getSDK = () => {
+ const sdk = window[UID_JS_SDK_NAME];
+ if (!sdk) {
+ console.error(`SDK not found at window.${UID_JS_SDK_NAME}. Make sure the SDK script is loaded.`);
+ }
+ return sdk;
+ };
const updateElements = useCallback((status) => {
const sdk = getSDK();
+ if (!sdk) {
+ return;
+ }
const token = sdk.getAdvertisingToken();
// Check for opt-out: only if user attempted login, and we got identity null with no token
@@ -178,9 +210,40 @@ const SecureSignalsApp = () => {
useEffect(() => {
// Add callbacks for UID2/EUID JS SDK
let sdk = getSDK();
- sdk = sdk || { callbacks: [] };
+ if (!sdk) {
+ // SDK not loaded yet, wait for it
+ const checkSDK = setInterval(() => {
+ sdk = getSDK();
+ if (sdk) {
+ clearInterval(checkSDK);
+ if (!sdk.callbacks) {
+ sdk.callbacks = [];
+ }
+ sdk.callbacks.push(onIdentityUpdated);
+ sdk.callbacks.push((eventType: string, payload: any) => {
+ if (eventType === 'SdkLoaded') {
+ sdk.init({
+ baseUrl: UID_BASE_URL,
+ });
+ }
+ if (eventType === 'InitCompleted') {
+ if (sdk.isLoginRequired()) {
+ sdk.setIdentity(identity);
+ setIdentity(identity);
+ }
+ }
+ });
+ }
+ }, 100);
+ return () => clearInterval(checkSDK);
+ }
+
+ // SDK is available, set up callbacks
+ if (!sdk.callbacks) {
+ sdk.callbacks = [];
+ }
sdk.callbacks.push(onIdentityUpdated);
- sdk.callbacks.push((eventType, payload) => {
+ sdk.callbacks.push((eventType: string, payload: any) => {
if (eventType === 'SdkLoaded') {
sdk.init({
baseUrl: UID_BASE_URL,
@@ -261,6 +324,18 @@ const SecureSignalsApp = () => {
try {
const sdk = getSDK();
+ if (!sdk) {
+ console.error('SDK not available. Make sure the SDK script is loaded.');
+ return;
+ }
+
+ // Check if crypto.subtle is available (required for SDK)
+ if (typeof window !== 'undefined' && !window.crypto?.subtle) {
+ console.error('crypto.subtle is not available. This requires HTTPS or a secure context.');
+ alert('crypto.subtle is not available. Please access this page over HTTPS.');
+ return;
+ }
+
await sdk.setIdentityFromEmail(email, clientSideIdentityOptions);
loadSecureSignals();
} catch (e) {
diff --git a/web-integrations/javascript-sdk/react-client-side/package-lock.json b/web-integrations/javascript-sdk/react-client-side/package-lock.json
index 2fbe785..3531894 100644
--- a/web-integrations/javascript-sdk/react-client-side/package-lock.json
+++ b/web-integrations/javascript-sdk/react-client-side/package-lock.json
@@ -23,6 +23,7 @@
},
"devDependencies": {
"@babel/plugin-proposal-private-property-in-object": "^7.21.0",
+ "cross-env": "^7.0.3",
"dotenv-cli": "^10.0.0"
}
},
@@ -5523,6 +5524,24 @@
"node": ">=14"
}
},
+ "node_modules/cross-env": {
+ "version": "7.0.3",
+ "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz",
+ "integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==",
+ "dev": true,
+ "dependencies": {
+ "cross-spawn": "^7.0.1"
+ },
+ "bin": {
+ "cross-env": "src/bin/cross-env.js",
+ "cross-env-shell": "src/bin/cross-env-shell.js"
+ },
+ "engines": {
+ "node": ">=10.14",
+ "npm": ">=6",
+ "yarn": ">=1"
+ }
+ },
"node_modules/cross-spawn": {
"version": "7.0.6",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
diff --git a/web-integrations/javascript-sdk/react-client-side/package.json b/web-integrations/javascript-sdk/react-client-side/package.json
index ff49107..8de1da8 100644
--- a/web-integrations/javascript-sdk/react-client-side/package.json
+++ b/web-integrations/javascript-sdk/react-client-side/package.json
@@ -18,6 +18,7 @@
},
"devDependencies": {
"@babel/plugin-proposal-private-property-in-object": "^7.21.0",
+ "cross-env": "^7.0.3",
"dotenv-cli": "^10.0.0"
},
"overrides": {
@@ -28,7 +29,7 @@
},
"scripts": {
"start": "node server.js",
- "dev": "PORT=3034 dotenv -e ../../../.env -- react-scripts start",
+ "dev": "cross-env PORT=3034 dotenv -e ../../../.env -- react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
diff --git a/web-integrations/javascript-sdk/react-client-side/server.js b/web-integrations/javascript-sdk/react-client-side/server.js
index d219188..4e9feef 100644
--- a/web-integrations/javascript-sdk/react-client-side/server.js
+++ b/web-integrations/javascript-sdk/react-client-side/server.js
@@ -1,6 +1,11 @@
-require('dotenv').config({ path: '../../../.env' });
-
-console.log('process.env', process.env);
+// Load environment variables from .env file (for local development)
+// In Docker, env_file in docker-compose.yml already provides these, so this is optional
+try {
+ require('dotenv').config({ path: '../../../.env' });
+} catch (error) {
+ // Silently fail if .env file doesn't exist (e.g., in Docker where env vars are provided via env_file)
+ // This is expected behavior when running in Docker
+}
const fs = require('fs');
const path = require('path');
@@ -21,9 +26,19 @@ app.get('/ops/healthcheck', (req, res) => {
// Helper function to serve index.html with environment variable replacement
function serveIndexHtml(req, res) {
// Try build directory first (production), then public (development)
- let indexPath = path.join(__dirname, 'build', 'index.html');
- if (!fs.existsSync(indexPath)) {
- indexPath = path.join(__dirname, 'public', 'index.html');
+ const buildPath = path.join(__dirname, 'build');
+ const buildIndexPath = path.join(buildPath, 'index.html');
+ const publicIndexPath = path.join(__dirname, 'public', 'index.html');
+
+ let indexPath;
+ if (fs.existsSync(buildIndexPath)) {
+ indexPath = buildIndexPath;
+ } else if (fs.existsSync(publicIndexPath)) {
+ console.warn('Warning: build directory not found. Serving from public directory. Run "npm run build" to build the React app.');
+ indexPath = publicIndexPath;
+ } else {
+ res.status(500).send('Error: Neither build nor public index.html found. Please run "npm run build" first.');
+ return;
}
let html = fs.readFileSync(indexPath, 'utf8');
@@ -36,6 +51,21 @@ function serveIndexHtml(req, res) {
html = html.replace(/__UID_JS_SDK_URL_PLACEHOLDER__/g, uidJsSdkUrl);
html = html.replace(/__PUBLIC_URL_PLACEHOLDER__/g, publicUrl);
+ // Inject runtime environment variables as a script tag
+ // This allows the React app to read environment variables at runtime (for Kubernetes)
+ const runtimeEnv = {
+ REACT_APP_UID_JS_SDK_NAME: process.env.REACT_APP_UID_JS_SDK_NAME,
+ REACT_APP_UID_CLIENT_BASE_URL: process.env.REACT_APP_UID_CLIENT_BASE_URL,
+ REACT_APP_IDENTITY_NAME: process.env.REACT_APP_IDENTITY_NAME,
+ REACT_APP_DOCS_BASE_URL: process.env.REACT_APP_DOCS_BASE_URL,
+ REACT_APP_UID_CSTG_SUBSCRIPTION_ID: process.env.REACT_APP_UID_CSTG_SUBSCRIPTION_ID,
+ REACT_APP_UID_CSTG_SERVER_PUBLIC_KEY: process.env.REACT_APP_UID_CSTG_SERVER_PUBLIC_KEY,
+ };
+
+ // Inject script tag before closing tag
+ const envScript = ``;
+ html = html.replace('', `${envScript}`);
+
// Debug: log if replacement happened
if (html.includes('__UID_JS_SDK_URL_PLACEHOLDER__')) {
console.warn('Warning: Placeholder __UID_JS_SDK_URL_PLACEHOLDER__ was not replaced in', indexPath);
@@ -74,6 +104,15 @@ app.get('*', (req, res, next) => {
});
app.listen(port, () => {
- console.log(`Example app listening at http://localhost:${port}`);
+ const buildPath = path.join(__dirname, 'build');
+ if (fs.existsSync(buildPath)) {
+ console.log(`Example app listening at http://localhost:${port}`);
+ console.log('Serving production build from build/ directory');
+ } else {
+ console.log(`Example app listening at http://localhost:${port}`);
+ console.warn('WARNING: build/ directory not found. Serving from public/ directory.');
+ console.warn('For production, run "npm run build" first.');
+ console.warn('For development, use "npm run dev" instead.');
+ }
});
diff --git a/web-integrations/javascript-sdk/react-client-side/src/ClientSideApp.tsx b/web-integrations/javascript-sdk/react-client-side/src/ClientSideApp.tsx
index 539e6a8..6507f43 100644
--- a/web-integrations/javascript-sdk/react-client-side/src/ClientSideApp.tsx
+++ b/web-integrations/javascript-sdk/react-client-side/src/ClientSideApp.tsx
@@ -7,15 +7,34 @@ declare global {
}
}
-// Environment variables
-const UID_JS_SDK_NAME = process.env.REACT_APP_UID_JS_SDK_NAME;
-const UID_BASE_URL = process.env.REACT_APP_UID_CLIENT_BASE_URL;
-const IDENTITY_NAME = process.env.REACT_APP_IDENTITY_NAME;
-const DOCS_BASE_URL = process.env.REACT_APP_DOCS_BASE_URL;
+// Extend Window interface for runtime environment variables
+interface ReactAppEnv {
+ [key: string]: string | undefined;
+}
+
+// Helper function to get environment variables from runtime (Kubernetes) or build-time
+function getEnvVar(key: string): string | undefined {
+ // First try runtime environment (injected by server.js for Kubernetes)
+ if (typeof window !== 'undefined' && (window as any).__REACT_APP_ENV__) {
+ const env = (window as any).__REACT_APP_ENV__ as ReactAppEnv;
+ const value = env[key];
+ if (value !== undefined && value !== null) {
+ return value;
+ }
+ }
+ // Fallback to build-time environment variable
+ return process.env[key];
+}
+
+// Environment variables (read from runtime or build-time)
+const UID_JS_SDK_NAME = getEnvVar('REACT_APP_UID_JS_SDK_NAME') || '__uid2';
+const UID_BASE_URL = getEnvVar('REACT_APP_UID_CLIENT_BASE_URL');
+const IDENTITY_NAME = getEnvVar('REACT_APP_IDENTITY_NAME') || 'UID2';
+const DOCS_BASE_URL = getEnvVar('REACT_APP_DOCS_BASE_URL') || 'https://unifiedid.com/docs';
const clientSideIdentityOptions = {
- subscriptionId: process.env.REACT_APP_UID_CSTG_SUBSCRIPTION_ID,
- serverPublicKey: process.env.REACT_APP_UID_CSTG_SERVER_PUBLIC_KEY,
+ subscriptionId: getEnvVar('REACT_APP_UID_CSTG_SUBSCRIPTION_ID'),
+ serverPublicKey: getEnvVar('REACT_APP_UID_CSTG_SERVER_PUBLIC_KEY'),
};
const ClientSideApp = () => {