diff --git a/.env.sample.euid b/.env.sample.euid
index 421bd81..49e5275 100644
--- a/.env.sample.euid
+++ b/.env.sample.euid
@@ -19,6 +19,7 @@ SESSION_KEY="your-session-key-here"
# Client-Side Token Generation (CSTG) - Provided by your EUID integration representative
UID_CSTG_SERVER_PUBLIC_KEY="your-euid-server-public-key"
UID_CSTG_SUBSCRIPTION_ID="your-euid-subscription-id"
+UID_CSTG_ORIGIN="your-app-url.com" # The public URL where this application is deployed (e.g., https://your-domain.com or http://localhost:3034 for local dev)
# React Client-Side Examples - Provided by your EUID integration representative
# Note: These are the same values as the variables above, prefixed with REACT_APP_
diff --git a/.env.sample.uid2 b/.env.sample.uid2
index f57c04b..350e602 100644
--- a/.env.sample.uid2
+++ b/.env.sample.uid2
@@ -19,6 +19,7 @@ SESSION_KEY="your-session-key-here"
# Client-Side Token Generation (CSTG)
UID_CSTG_SERVER_PUBLIC_KEY="UID2-X-I-MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEo+jcPlk8GWn3iG0R5Il2cbFQI9hR3TvHxaBUKHl5Vh+ugr+9uLMiXihka8To07ETFGghEifY96Hrpe5RnYko7Q=="
UID_CSTG_SUBSCRIPTION_ID="DMr7uHxqLU"
+UID_CSTG_ORIGIN="your-app-url.com" # The public URL where this application is deployed (e.g., https://your-domain.com or http://localhost:3034 for local dev)
# React Client-Side Examples
# Note: These are the same values as the variables above, prefixed with REACT_APP_
diff --git a/docker-compose.yml b/docker-compose.yml
index 93cf174..fdf740f 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -20,6 +20,16 @@ services:
env_file:
- .env
+ javascript-sdk-server-side:
+ build:
+ context: .
+ dockerfile: web-integrations/javascript-sdk/server-side-node/Dockerfile
+ ports:
+ - "3034:3034"
+ container_name: javascript-sdk-server-side
+ env_file:
+ - .env
+
# server-side integration (no SDK)
server-side:
build:
diff --git a/web-integrations/javascript-sdk/server-side-node/.gitignore b/web-integrations/javascript-sdk/server-side-node/.gitignore
new file mode 100644
index 0000000..708db9f
--- /dev/null
+++ b/web-integrations/javascript-sdk/server-side-node/.gitignore
@@ -0,0 +1,17 @@
+# Dependencies
+node_modules/
+package-lock.json
+
+# Logs
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+
+# Environment variables
+.env
+.env.local
+.env.*.local
+
+# OS files
+.DS_Store
+
diff --git a/web-integrations/javascript-sdk/server-side-node/Dockerfile b/web-integrations/javascript-sdk/server-side-node/Dockerfile
new file mode 100644
index 0000000..fcb0d80
--- /dev/null
+++ b/web-integrations/javascript-sdk/server-side-node/Dockerfile
@@ -0,0 +1,17 @@
+FROM node:20.11.0-alpine3.18
+
+WORKDIR /usr/src/app
+
+# Copy package files first for better caching
+COPY web-integrations/javascript-sdk/server-side-node/package*.json ./
+RUN npm install
+
+# Copy application files
+COPY web-integrations/javascript-sdk/server-side-node/server.js ./
+COPY web-integrations/javascript-sdk/server-side-node/public ./public/
+COPY web-integrations/javascript-sdk/server-side-node/views ./views/
+
+ENV PORT=3034
+EXPOSE 3034
+CMD ["npm", "start"]
+
diff --git a/web-integrations/javascript-sdk/server-side-node/README.md b/web-integrations/javascript-sdk/server-side-node/README.md
new file mode 100644
index 0000000..f2615dc
--- /dev/null
+++ b/web-integrations/javascript-sdk/server-side-node/README.md
@@ -0,0 +1,101 @@
+# Server-Side UID2 or EUID Integration Example using JavaScript SDK via Node.js
+
+This example showcases how the UID2/EUID **JavaScript SDK works in Node.js** server environments. It uses the same `setIdentityFromEmail` method that runs in browsers, but executes it on the server. This uses **public credentials** (Subscription ID + Server Public Key) which are the same credentials used for client-side integrations.
+
+For more information on the JavaScript SDK, refer to the [UID2 SDK for JavaScript](https://unifiedid.com/docs/sdks/sdk-ref-javascript) or [EUID SDK for JavaScript](https://euid.eu/docs/sdks/sdk-ref-javascript) documentation.
+
+> **Note:** This example can be configured for either UID2 or EUID — the behavior is determined by your environment variable configuration. You cannot use both simultaneously.
+
+## How This Implementation Works
+
+Unlike the browser where the SDK runs natively in the DOM, this example uses **jsdom** to simulate a browser environment within Node.js:
+
+1. **Imports the SDK**: Uses npm packages [`@uid2/uid2-sdk`](https://www.npmjs.com/package/@uid2/uid2-sdk) or [`@unified-id/euid-sdk`](https://www.npmjs.com/package/@unified-id/euid-sdk) (selected dynamically based on the `IDENTITY_NAME` environment variable)
+2. **Creates a virtual DOM**: Uses jsdom to provide `window`, `document`, and `navigator` objects that the SDK expects
+3. **Polyfills browser APIs**: Adds Node.js equivalents for Web Crypto API (`crypto.subtle`) and text encoding APIs (`TextEncoder`/`TextDecoder`)
+4. **Instantiates the SDK**: Creates a new instance of `UID2` or `EUID` class
+5. **Runs SDK methods**: Calls `setIdentityFromEmail` just like in a browser, with the same public credentials
+
+This demonstrates that the client-side SDK can be compatible with server-side Node.js environments when given the proper browser-like context.
+
+## Build and Run the Example Application
+
+### Using Docker Compose (Recommended)
+
+From the repository root directory:
+
+```bash
+# Start the service
+docker compose up javascript-sdk-server-side
+```
+
+The application will be available at http://localhost:3034
+
+To view logs or stop the service:
+
+```bash
+# View logs (in another terminal)
+docker compose logs javascript-sdk-server-side
+
+# Stop the service
+docker compose stop javascript-sdk-server-side
+```
+
+### Using Docker Build
+
+```bash
+# Build the image
+docker build -f web-integrations/javascript-sdk/server-side/Dockerfile -t javascript-sdk-server-side .
+
+# Run the container
+docker run -it --rm -p 3034:3034 --env-file .env javascript-sdk-server-side
+```
+
+## Environment Variables
+
+The following table lists the environment variables that you must specify to start the application.
+
+### Core Configuration
+
+| Variable | Description | Example Values |
+|:---------|:------------|:---------------|
+| `UID_SERVER_BASE_URL` | The base URL of the UID2/EUID service. For details, see [Environments](https://unifiedid.com/docs/getting-started/gs-environments) (UID2) or [Environments](https://euid.eu/docs/getting-started/gs-environments) (EUID). | UID2: `https://operator-integ.uidapi.com`
EUID: `https://integ.euid.eu/v2` |
+| `UID_CSTG_SUBSCRIPTION_ID` | Your UID2/EUID subscription ID for Client-Side Token Generation. **These are public credentials.** | Your assigned subscription ID (e.g., `DMr7uHxqLU`) |
+| `UID_CSTG_SERVER_PUBLIC_KEY` | Your UID2/EUID server public key for Client-Side Token Generation. **These are public credentials.** | Your assigned public key |
+| `UID_CSTG_ORIGIN` | The public URL where this application is deployed. Must match your CSTG subscription's allowed origins. | `https://your-domain.com` (production)
`http://localhost:3034` (local dev default) |
+| `SESSION_KEY` | Used by the cookie-session middleware to encrypt the session data stored in cookies. | Any secure random string |
+
+### Display/UI Configuration
+
+| Variable | Description | Example Values |
+|:---------|:------------|:---------------|
+| `IDENTITY_NAME` | Identity name for UI display | UID2: `UID2`
EUID: `EUID` |
+| `DOCS_BASE_URL` | Documentation base URL | UID2: `https://unifiedid.com/docs`
EUID: `https://euid.eu/docs` |
+
+After you see output similar to the following, the example application is up and running:
+
+```
+Server listening at http://localhost:3034
+```
+
+If needed, to close the application, terminate the docker container or use the `Ctrl+C` keyboard shortcut.
+
+## Test the Example Application
+
+The following table outlines and annotates the steps you may take to test and explore the example application.
+
+| Step | Description | Comments |
+|:----:|:------------|:---------|
+| 1 | In your browser, navigate to the application main page at `http://localhost:3034`. | The displayed main (index) page provides a login form for the user to complete the UID2/EUID login process.IMPORTANT: A real-life application must also display a form for the user to express their consent to targeted advertising. |
+| 2 | Enter the user email address that you want to use for testing and click **Log In**. | This is a call to the `/login` endpoint ([server.js](server.js)). The login initiated on the server side uses the JavaScript SDK's `setIdentityFromEmail` method to generate a token and processes the received response. The SDK handles all encryption/decryption automatically, just as it does in the browser. |
+| | The main page is updated to display the established identity information. | The displayed identity information is the `body` property of the response from the SDK's `setIdentityFromEmail` call. If the response is successful, the returned identity is saved to a session cookie (a real-world application would use a different way to store session data) and the protected index page is rendered. |
+| 3 | Review the displayed identity information. | The server reads the user session and extracts the current identity ([server.js](server.js)). The `advertising_token` on the identity can be used for targeted advertising. Note that the identity contains several timestamps that determine when the advertising token becomes invalid (`identity_expires`) and when the server should attempt to refresh it (`refresh_from`). The `verifyIdentity` function ([server.js](server.js)) uses the SDK to refresh the token as needed.
The user is automatically logged out in the following cases:
- If the identity expires without being refreshed and refresh attempt fails.
- If the refresh token expires.
- If the refresh attempt indicates that the user has opted out. |
+| 4 | To exit the application, click **Log Out**. | This calls the `/logout` endpoint on the server ([server.js](server.js)), which clears the session and presents the user with the login form again.
NOTE: The page displays the **Log Out** button as long as the user is logged in. |
+
+## Key Benefits
+
+This example demonstrates the advantages of using the JavaScript SDK on the server:
+
+- **Secure credential handling**: Public credentials (server public key and subscription ID) remain on the server and are not exposed to the browser
+- **Simplified implementation**: The SDK handles the full token lifecycle including encryption, decryption, and refresh logic automatically
+- **No manual cryptography**: Unlike traditional server-side integrations, there's no need to manually implement encryption/decryption processes
diff --git a/web-integrations/javascript-sdk/server-side-node/package.json b/web-integrations/javascript-sdk/server-side-node/package.json
new file mode 100644
index 0000000..fee0603
--- /dev/null
+++ b/web-integrations/javascript-sdk/server-side-node/package.json
@@ -0,0 +1,33 @@
+{
+ "name": "uid2-javascript-sdk-server-side",
+ "version": "1.0.0",
+ "description": "Server-Side UID2/EUID Integration Example using JavaScript SDK",
+ "main": "server.js",
+ "scripts": {
+ "start": "node server.js",
+ "dev": "nodemon server.js"
+ },
+ "keywords": [
+ "uid2",
+ "euid",
+ "identity",
+ "server-side"
+ ],
+ "author": "",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@uid2/uid2-sdk": "^4.0.1",
+ "@unified-id/euid-sdk": "^4.0.1",
+ "cookie-session": "^2.0.0",
+ "dotenv": "^16.0.3",
+ "ejs": "^3.1.9",
+ "express": "^4.18.2",
+ "jsdom": "^23.0.0",
+ "nocache": "^4.0.0",
+ "xhr2": "^0.2.1"
+ },
+ "devDependencies": {
+ "nodemon": "^3.0.1"
+ }
+}
+
diff --git a/web-integrations/javascript-sdk/server-side-node/public/images/favicon.png b/web-integrations/javascript-sdk/server-side-node/public/images/favicon.png
new file mode 100644
index 0000000..48885e0
Binary files /dev/null and b/web-integrations/javascript-sdk/server-side-node/public/images/favicon.png differ
diff --git a/web-integrations/javascript-sdk/server-side-node/public/stylesheets/app.css b/web-integrations/javascript-sdk/server-side-node/public/stylesheets/app.css
new file mode 100644
index 0000000..58b2cf8
--- /dev/null
+++ b/web-integrations/javascript-sdk/server-side-node/public/stylesheets/app.css
@@ -0,0 +1,99 @@
+body {
+ padding: 50px;
+ font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;
+}
+
+a {
+ color: #00B7FF;
+}
+
+.button {
+ border-style: none;
+ cursor: pointer;
+ align-items: center;
+ height: 40px;
+ width: 401px;
+ text-align: center;
+ position: absolute;
+ letter-spacing: 0.28px;
+ box-sizing: border-box;
+ color: white;
+ font-family: 'Raleway', Helvetica, Arial, serif;
+ font-size: 14px;
+ font-style: normal;
+ font-weight: 700;
+ text-transform: none;
+ text-indent: 0px;
+ text-shadow: none;
+ margin: 0em;
+ padding: 1px 6px;
+ background-color: rgba(2,10,64,1.0);
+ border-image: initial
+}
+
+.form {
+ margin-top: 40px;
+}
+
+.email_prompt {
+ align-items: center;
+ align-self: center;
+ background-color: white;
+ border: 1px solid rgba(2,10,64,1.0);
+ border-radius: 2px;
+ box-sizing: border-box;
+ display: inline-flex;
+ flex-direction: row;
+ flex-shrink: 0;
+ height: 40px;
+ justify-content: flex-start;
+ margin-right: 1.0px;
+ margin-bottom: 20px;
+ min-width: 399px;
+ padding: 0 16.0px;
+ position: relative;
+ width: auto;
+}
+
+#email {
+ background-color: white;
+ flex-shrink: 0;
+ height: auto;
+ letter-spacing: 0.12px;
+ line-height: 16px;
+ min-height: 16px;
+ position: relative;
+ text-align: left;
+ white-space: nowrap;
+ width: 351px;
+ color: rgba(2,10,64,1.0);
+ font-family: 'Raleway', Helvetica, Arial, serif;
+ font-size: 12px;
+ font-style: normal;
+ font-weight: 500;
+ padding: 1px 2px;
+ outline: none;
+}
+
+h1 {
+ padding-bottom: 20px;
+}
+
+#uid2_state .label {
+ white-space: nowrap;
+ padding-right: 20px;
+}
+#uid2_state tr {
+ margin-top: 10px;
+}
+
+.message {
+ color: green;
+ padding: 20px;
+ margin-left: -22px;
+ font-size: 16px;
+ font-weight: 500;
+ border: 2px solid green;
+ border-radius: 5px;
+}
+
diff --git a/web-integrations/javascript-sdk/server-side-node/server.js b/web-integrations/javascript-sdk/server-side-node/server.js
new file mode 100644
index 0000000..2a9367d
--- /dev/null
+++ b/web-integrations/javascript-sdk/server-side-node/server.js
@@ -0,0 +1,227 @@
+"use strict";
+
+// Load environment variables
+require('dotenv').config({ path: '../../../.env' });
+
+const session = require('cookie-session');
+const ejs = require('ejs');
+const express = require('express');
+const crypto = require('crypto');
+const nocache = require('nocache');
+
+
+const app = express();
+const port = process.env.PORT || 3034;
+
+let uidBaseUrl = process.env.UID_SERVER_BASE_URL;
+const subscriptionId = process.env.UID_CSTG_SUBSCRIPTION_ID;
+const serverPublicKey = process.env.UID_CSTG_SERVER_PUBLIC_KEY;
+const identityName = process.env.IDENTITY_NAME;
+const docsBaseUrl = process.env.DOCS_BASE_URL;
+
+
+// additional packages/variables needed to ensure compabitibility with the SDK
+const { JSDOM } = require('jsdom'); // for simulating a browser environment
+const util = require('util'); // for polyfilling TextEncoder and TextDecoder
+const XMLHttpRequest = require('xhr2'); // for making HTTP requests
+const clientOrigin = process.env.UID_CSTG_ORIGIN || `http://localhost:${port}`; // Client origin: the URL where this app is accessible
+
+
+// Create a virtual DOM environment for the SDK to run in
+let SdkClass = null;
+let uidSdk = null;
+let dom = null;
+
+async function initializeSDK() {
+ dom = new JSDOM('
+ This example demonstrates how a content publisher can use <%- identityName %> services and the JavaScript SDK on the server to implement the + server-side <%- identityName %> integration workflow. This proves that the JavaScript SDK (designed for browsers) also works in a Node.js server environment. + [Source Code] +
+ +Something went wrong:
+<%= error %>+ + + + diff --git a/web-integrations/javascript-sdk/server-side-node/views/index.html b/web-integrations/javascript-sdk/server-side-node/views/index.html new file mode 100644 index 0000000..ff40360 --- /dev/null +++ b/web-integrations/javascript-sdk/server-side-node/views/index.html @@ -0,0 +1,39 @@ + + + + +
+ This example demonstrates how a content publisher can use <%- identityName %> services and the JavaScript SDK on the server to implement the + server-side <%- identityName %> integration workflow. This showcases how the JavaScript SDK (designed for browsers) can also work in a Node.js server environment. + [Source Code] +
+ + <% if (identity) { %> + +Current <%- identityName %> Identity:
+<%= JSON.stringify(identity, null, 2) %>+