Files

6.1 KiB

Quickstart: OIDC Proxy Script Authentication

Feature: 001-oidc-proxy-script
Date: 2025-07-17


Prerequisites

  • Node.js 18.0.0 or later (node --version)
  • Access to a KME OIDC token service endpoint
  • Project dependencies installed (npm install)

Step 1 — Create adapter_settings.json

cd /path/to/kme-content-adapter
cp src/globalVariables/adapter_settings.json.example \
   src/globalVariables/adapter_settings.json

Edit src/globalVariables/adapter_settings.json with your real credentials:

{
  "tokenUrl":  "https://auth.kme.example.com/protocol/openid-connect/token",
  "username":  "your-service-account@example.com",
  "password":  "your-password",
  "clientId":  "your-client-id",
  "scope":     "openid tags content_entitlements"
}

Security: adapter_settings.json is in .gitignore. Never commit credentials. The .example file (with placeholder values) IS committed and serves as the template.


Step 2 — Start the Adapter

npm start
# or for development with auto-reload:
npm run dev

Expected startup log (structured JSON):

{"message": "Loaded global data: adapter_settings", "keys": ["tokenUrl","username","password","clientId","scope"]}
{"message": "Loaded 1 global variables", "json": 1, "js": 0}
{"message": "Configuration loaded", "port": 3000, "host": "0.0.0.0", ...}
{"message": "Configuration validated successfully"}
{"message": "Server listening", "port": 3000, "host": "0.0.0.0"}

Step 3 — Send a Test Request

curl -v http://localhost:3000/ProxyScript/run/67bca862210071627d32ef12/current/kmeAdapter

Expected response on first request (token fetched from OIDC service):

HTTP/1.1 200 OK
Content-Type: text/plain

Authorized

Expected response on subsequent requests (token served from cache):

HTTP/1.1 200 OK
Content-Type: text/plain

Authorized

No visible difference from the caller's perspective; the adapter log will show no new axios call for the second request (cache hit).

Expected response with invalid credentials:

HTTP/1.1 401 Unauthorized
Content-Type: text/plain

Unauthorized: HTTP 401

Step 4 — Run Tests

Unit tests (fast, no network, no server required)

npm run test:unit
# runs: node --test tests/unit/proxy.test.js

Exercises: cache hit, cache miss, token expiry, stampede prevention, timeout handling, missing id_token, missing required fields, HTTP error from token service.

Contract tests (starts real HTTP server with mock token endpoint)

npm run test:contract
# runs: node --test tests/contract/proxy-http.test.js

Exercises: end-to-end 200 OK / Authorized, end-to-end 401 Unauthorized, verifies response headers and exact body strings.

All tests

npm test
# runs: node --test tests/**/*.test.js

Architecture Summary

Inbound HTTP request
        │
        ▼
  server.js (http.createServer)
        │  creates fresh vm.createContext({
        │    ...globalVMContext,          ← axios, URLSearchParams, console, ...
        │    ...globalVariableContext,    ← adapter_settings (from JSON)
        │    req, res                     ← fresh per request
        │  })
        │
        ▼
  proxy.js (vm.Script, compiled once)
        │
        ├─ reads adapter_settings._cache
        │    ├─ CACHE HIT: token valid → 200 OK / Authorized
        │    └─ CACHE MISS / EXPIRED:
        │         ├─ FETCHING (stampede guard): queue on _cache.pendingFetch → 200/401
        │         └─ NEW FETCH:
        │              POST tokenUrl (timeout: 5s)
        │              ├─ SUCCESS: cache token + expiry → 200 OK / Authorized
        │              └─ FAILURE: → 401 Unauthorized: <message>
        ▼
  HTTP response to caller

Key Files

File Status Purpose
src/globalVariables/adapter_settings.json Create OIDC credentials (gitignored)
src/globalVariables/adapter_settings.json.example Create Template with placeholder values
src/proxyScripts/proxy.js Create OIDC authentication proxy (VM sandbox)
tests/unit/proxy.test.js Create Unit tests (no network, no server)
tests/contract/proxy-http.test.js Create HTTP response contract tests
src/server.js No change Existing infrastructure; auto-loads adapter_settings.json
config/default.json No change Infrastructure settings only (port, host, log level)

Token Lifecycle

Phase What happens
First request _cache.token is null → fresh token fetch → cache id_token + expires_in
Subsequent requests (valid token) Date.now()/1000 < _cache.expiry → return Authorized immediately, no network call
After token expiry Date.now()/1000 ≥ _cache.expiry → fresh token fetch (transparent to caller)
Concurrent requests during fetch All requests await the shared _cache.pendingFetch promise; only ONE HTTP call made
Auth failure Clear _cache.token and _cache.expiry → respond 401 Unauthorized: <reason>
Timeout (5 s) axios ECONNABORTED error → treated as auth failure → 401 Unauthorized

Troubleshooting

Symptom Likely cause Fix
401 Unauthorized: HTTP 401 Wrong credentials Check username, password, clientId in adapter_settings.json
401 Unauthorized: connect ECONNREFUSED Token service unreachable Check tokenUrl is correct and reachable
401 Unauthorized: token service timeout Network slow or token service down Verify connectivity; check token service health
401 Unauthorized: id_token missing from response Token service returns access_token only Ensure openid is in scope and the service issues id_token
Server fails to start with adapter_settings not found JSON file missing Run Step 1 above
SyntaxError in proxy.js at startup import or export statement in proxy.js Remove all import/export — proxy.js must be pure VM sandbox code