{"_id":"@danainnovations/sonance-gate","_rev":"6-01b227d2533e4f79f8b4c0f14ccb424d","name":"@danainnovations/sonance-gate","dist-tags":{"latest":"0.4.0"},"versions":{"0.1.0":{"name":"@danainnovations/sonance-gate","version":"0.1.0","license":"MIT","_id":"@danainnovations/sonance-gate@0.1.0","maintainers":[{"name":"tcpalm-r","email":"thomas.palmer@sonance.com"},{"name":"elliotta1130","email":"elliott.amador@sonance.com"},{"name":"silaswrv","email":"silas@wrv.ca"}],"dist":{"shasum":"671aa98e786e17639b22fdcf9aa8ae05983a7781","tarball":"https://registry.npmjs.org/@danainnovations/sonance-gate/-/sonance-gate-0.1.0.tgz","fileCount":6,"integrity":"sha512-+XQtP0W7F+1ho6nyTqFxsNo0FQqtyc7fc16ghMMhclJxdGHZOK9ymci7FQtADR/o6VFIq6ZPrFKgNhsLadWP8A==","signatures":[{"sig":"MEQCIGV1qIH8/qMS61DdNlO+e+xbGgtfoFsIUDq9p8/WArLBAiAkEsafR51vEmKtAFmHTIMoxr8Zk4rfvxEm8ijqZObbXQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":28272},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","engines":{"node":">=20"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"}},"gitHead":"f0ec072bd5210aec59f8a14b8d07bad14fff2b1a","scripts":{"test":"vitest run","build":"tsup","typecheck":"tsc --noEmit","test:watch":"vitest","prepublishOnly":"npm run build && npm test"},"_npmUser":{"name":"tcpalm-r","email":"thomas.palmer@sonance.com"},"_npmVersion":"10.9.8","description":"Framework-agnostic Sonance Okta SSO gate for Vercel apps (Routing Middleware)","directories":{},"_nodeVersion":"22.22.3","dependencies":{"jose":"^5.9.6","@vercel/functions":"^3.7.5"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.3.5","vitest":"^2.1.8","typescript":"^5.7.2","@types/node":"^26.1.0"},"_npmOperationalInternal":{"tmp":"tmp/sonance-gate_0.1.0_1783467795519_0.37068062042344185","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@danainnovations/sonance-gate","version":"0.2.0","license":"MIT","_id":"@danainnovations/sonance-gate@0.2.0","maintainers":[{"name":"tcpalm-r","email":"thomas.palmer@sonance.com"},{"name":"elliotta1130","email":"elliott.amador@sonance.com"},{"name":"silaswrv","email":"silas@wrv.ca"}],"dist":{"shasum":"e47a42e3b8ed1e31730035ae209929b914f5ac1a","tarball":"https://registry.npmjs.org/@danainnovations/sonance-gate/-/sonance-gate-0.2.0.tgz","fileCount":6,"integrity":"sha512-V+lrNhOA90Ycl7AID04ShkXFJVfjKsHRcD6/577nUY85Yg4KcR58NV3sdM3/ANeGv/OM4FWeRokA7B1fqafPPw==","signatures":[{"sig":"MEUCIQDxTEV8QzrPSPjO4dWzS7ts+WVdScfzAbb4czCBWbk7JwIgMwZi7s8CAfmEGMiFnXN3E9x/KZHdeyqlxpvwhyYUwiA=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":31650},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","engines":{"node":">=20"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"}},"gitHead":"7b3a189a69083834e999972d7c629e09f35e8387","scripts":{"test":"vitest run","build":"tsup","typecheck":"tsc --noEmit","test:watch":"vitest","prepublishOnly":"npm run build && npm test"},"_npmUser":{"name":"tcpalm-r","email":"thomas.palmer@sonance.com"},"_npmVersion":"10.9.8","description":"Framework-agnostic Sonance Okta SSO gate for Vercel apps (Routing Middleware)","directories":{},"_nodeVersion":"22.22.3","dependencies":{"jose":"^5.9.6","@vercel/functions":"^3.7.5"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.3.5","vitest":"^2.1.8","typescript":"^5.7.2","@types/node":"^26.1.0"},"_npmOperationalInternal":{"tmp":"tmp/sonance-gate_0.2.0_1783471702096_0.5069549575552883","host":"s3://npm-registry-packages-npm-production"}},"0.2.1":{"name":"@danainnovations/sonance-gate","version":"0.2.1","license":"MIT","_id":"@danainnovations/sonance-gate@0.2.1","maintainers":[{"name":"tcpalm-r","email":"thomas.palmer@sonance.com"},{"name":"elliotta1130","email":"elliott.amador@sonance.com"},{"name":"silaswrv","email":"silas@wrv.ca"}],"dist":{"shasum":"cf42042ea0f7049df60adc79dff7b13c99ea8605","tarball":"https://registry.npmjs.org/@danainnovations/sonance-gate/-/sonance-gate-0.2.1.tgz","fileCount":6,"integrity":"sha512-I8wEGGXN5Vljx478eIfUKVHUaoFt4DP1AbBB0x9AVBBXYMYVWTBKiMi/sNjPs8Fklzcblpj8u0lsL2BhiL3Lag==","signatures":[{"sig":"MEQCIA6Rm3zb+J9ouuzksDzTnQhaGvnPxtSmpvtHHBHCp1V7AiAbrqD5QQcWU9kD7eq5DG8NAizqlje9jKEXduq2jJH9SQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":31854},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","engines":{"node":">=20"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"}},"gitHead":"616f4e2b2d6ca2cf836b08adcbccb96b95c04305","scripts":{"test":"vitest run","build":"tsup","typecheck":"tsc --noEmit","test:watch":"vitest","prepublishOnly":"npm run build && npm test"},"_npmUser":{"name":"tcpalm-r","email":"thomas.palmer@sonance.com"},"_npmVersion":"10.9.8","description":"Framework-agnostic Sonance Okta SSO gate for Vercel apps (Routing Middleware)","directories":{},"_nodeVersion":"22.22.3","dependencies":{"jose":"^5.9.6","@vercel/functions":"^3.7.5"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.3.5","vitest":"^2.1.8","typescript":"^5.7.2","@types/node":"^26.1.0"},"_npmOperationalInternal":{"tmp":"tmp/sonance-gate_0.2.1_1783548757235_0.16293566828474138","host":"s3://npm-registry-packages-npm-production"}},"0.3.0":{"name":"@danainnovations/sonance-gate","version":"0.3.0","license":"MIT","_id":"@danainnovations/sonance-gate@0.3.0","maintainers":[{"name":"tcpalm-r","email":"thomas.palmer@sonance.com"},{"name":"elliotta1130","email":"elliott.amador@sonance.com"},{"name":"silaswrv","email":"silas@wrv.ca"}],"dist":{"shasum":"83ae07f47b926eec3be760ba16e41b3700c394ab","tarball":"https://registry.npmjs.org/@danainnovations/sonance-gate/-/sonance-gate-0.3.0.tgz","fileCount":6,"integrity":"sha512-EEZNiofleeCfLWW9zS5PzvbmkG/hJXSjGwMUQbc5Uzh60S6nArUCZUTGGEBShnGVJ3gc2BIDifoqLsB5QPkBqw==","signatures":[{"sig":"MEYCIQCDgNh8fFj9AO0A3kvU1sctXUbGnJUdBH4rqACMPDAt5QIhALE3RSaW+42ZQaB30P4qCOQOjplJeds3er0wmx+RScil","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":44409},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","engines":{"node":">=20"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"}},"gitHead":"f4168430136139974571218d1a4a00330ccbd834","scripts":{"test":"vitest run","build":"tsup","typecheck":"tsc --noEmit","test:watch":"vitest","prepublishOnly":"npm run build && npm test"},"_npmUser":{"name":"tcpalm-r","email":"thomas.palmer@sonance.com"},"_npmVersion":"11.18.0","description":"Framework-agnostic Sonance Okta SSO gate for Vercel apps (Routing Middleware)","directories":{},"_nodeVersion":"22.22.3","dependencies":{"jose":"^5.9.6","@vercel/functions":"^3.7.5"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.3.5","vitest":"^2.1.8","typescript":"^5.7.2","@types/node":"^26.1.0"},"_npmOperationalInternal":{"tmp":"tmp/sonance-gate_0.3.0_1784146163012_0.1472980491886735","host":"s3://npm-registry-packages-npm-production"}},"0.3.1":{"name":"@danainnovations/sonance-gate","version":"0.3.1","license":"MIT","_id":"@danainnovations/sonance-gate@0.3.1","maintainers":[{"name":"tcpalm-r","email":"thomas.palmer@sonance.com"},{"name":"elliotta1130","email":"elliott.amador@sonance.com"},{"name":"silaswrv","email":"silas@wrv.ca"}],"dist":{"shasum":"cb963676b3216e7d79b5cbaa44c0a0c9ea57015f","tarball":"https://registry.npmjs.org/@danainnovations/sonance-gate/-/sonance-gate-0.3.1.tgz","fileCount":6,"integrity":"sha512-YEda/upeYWKLqyjVXPCtZO9zFSX8kQAyPzU3BmosQX3kgsNVkXbHOGg2xtNnqZrK/SCpEjDlJ27/WrL7p18vYg==","signatures":[{"sig":"MEUCID717dYLbUpHZjJF31VqoEElADZKwAGvvGkZVvl3Gs9fAiEAzD/TGzB1brvP/xEsS14J4Rysx/0+I2+kCSe2zKX123E=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":49582},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","engines":{"node":">=20"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"}},"gitHead":"653c8c17bb48927f4f9b2e746e5eb40cb7071237","scripts":{"test":"vitest run","build":"tsup","typecheck":"tsc --noEmit","test:watch":"vitest","prepublishOnly":"npm run build && npm test"},"_npmUser":{"name":"tcpalm-r","email":"thomas.palmer@sonance.com"},"_npmVersion":"11.18.0","description":"Framework-agnostic Sonance Okta SSO gate for Vercel apps (Routing Middleware)","directories":{},"_nodeVersion":"22.22.3","dependencies":{"jose":"^5.9.6","@vercel/functions":"^3.7.5"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.3.5","vitest":"^2.1.8","typescript":"^5.7.2","@types/node":"^26.1.0"},"_npmOperationalInternal":{"tmp":"tmp/sonance-gate_0.3.1_1784677049089_0.25115254643935625","host":"s3://npm-registry-packages-npm-production"}},"0.4.0":{"name":"@danainnovations/sonance-gate","version":"0.4.0","description":"Framework-agnostic Sonance Okta SSO gate for Vercel apps (Routing Middleware)","type":"module","main":"./dist/index.cjs","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"}},"publishConfig":{"access":"public"},"engines":{"node":">=20"},"scripts":{"build":"tsup","test":"vitest run","test:watch":"vitest","typecheck":"tsc --noEmit","prepublishOnly":"npm run build && npm test"},"dependencies":{"@vercel/functions":"^3.7.5","jose":"^5.9.6"},"devDependencies":{"@types/node":"^26.1.0","tsup":"^8.3.5","typescript":"^5.7.2","vitest":"^2.1.8"},"license":"MIT","gitHead":"a2ac4ec2d71afd3e4f2a6f7022a40855f67a60ea","_id":"@danainnovations/sonance-gate@0.4.0","_nodeVersion":"26.7.0","_npmVersion":"11.19.1","dist":{"integrity":"sha512-kraq8s39VhiETZrMIpSXmyrMynFhozNvq9PWAfbnpLRso0b4SKwwrmKhBsYL3/F5cYFB2bAxOsk3JDqR3Jloaw==","shasum":"01137736723cd39230a3038bbd32586f25f595be","tarball":"https://registry.npmjs.org/@danainnovations/sonance-gate/-/sonance-gate-0.4.0.tgz","fileCount":6,"unpackedSize":64455,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIG1dJkK+uRhYOFR3cCEP9up4FXGWgJM5kbuwii2QiW9ZAiARpOrVQ5UrdmAMYAfTq5kqOnNmxQPkCBFSqomXBHOBPg=="}]},"_npmUser":{"name":"tcpalm-r","email":"thomas.palmer@sonance.com"},"directories":{},"maintainers":[{"name":"tcpalm-r","email":"thomas.palmer@sonance.com"},{"name":"elliotta1130","email":"elliott.amador@sonance.com"},{"name":"silaswrv","email":"silas@wrv.ca"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/sonance-gate_0.4.0_1788216927752_0.977620818486348"},"_hasShrinkwrap":false}},"time":{"created":"2026-07-07T23:43:15.371Z","modified":"2026-08-31T22:55:28.175Z","0.1.0":"2026-07-07T23:43:15.668Z","0.2.0":"2026-07-08T00:48:22.244Z","0.2.1":"2026-07-08T22:12:37.372Z","0.3.0":"2026-07-15T20:09:23.160Z","0.3.1":"2026-07-21T23:37:29.229Z","0.4.0":"2026-08-31T22:55:27.893Z"},"license":"MIT","description":"Framework-agnostic Sonance Okta SSO gate for Vercel apps (Routing Middleware)","maintainers":[{"name":"tcpalm-r","email":"thomas.palmer@sonance.com"},{"name":"elliotta1130","email":"elliott.amador@sonance.com"},{"name":"silaswrv","email":"silas@wrv.ca"}],"readme":"# @danainnovations/sonance-gate\n\nSonance Okta SSO for any web app on Vercel — one file, any framework (Next.js, Vite, Astro, SvelteKit, static HTML).\n\n## How it behaves\n\n| Environment | Behavior |\n|---|---|\n| Vercel production | Real Okta sign-in required for every route |\n| Vercel preview | Legacy-open by default; internal/data-bearing apps opt into real Okta |\n| Local dev | Local-only Dev User adapter; no Okta credentials |\n\nSet `enforceInPreview: true` for every new internal or company-data-bearing app. Existing apps retain their current preview posture until they are audited and migrated. Sessions last 12 hours; re-auth is silent while your Okta session is alive.\n\n## Install\n\n```bash\nnpm install @danainnovations/sonance-gate\n```\n\nCreate `middleware.ts` at your project root (NOT inside `src/` or `app/`):\n\n```ts\nimport { createGate } from '@danainnovations/sonance-gate'\n\nexport default createGate({\n  // Required for internal/company-data-bearing applications.\n  enforceInPreview: true,\n  // Available only while running locally; selection still enters the standard\n  // server-side identity path used by the app's authorization checks.\n  devUsers: [\n    { sub: 'admin', name: 'Admin', email: 'admin@example.test' },\n    { sub: 'manager', name: 'Manager', email: 'manager@example.test' },\n  ],\n  // Paths reachable without sign-in. '/prefix/*' matches the whole subtree;\n  // a bare '/prefix' must be listed exactly (on its own) to be public.\n  publicPaths: ['/api/webhook/*'],\n  // End the Okta session at sign-out too. Needs a registered sign-out\n  // redirect URI first — see \"Signing out\" below.\n  oktaLogout: true,\n})\n\nexport const config = {\n  runtime: 'nodejs',\n  matcher: ['/((?!_next/static|_next/image|favicon.ico|assets/).*)'],\n}\n```\n\nNext.js note: on Next.js ≤15 the framework owns root `middleware.ts` — compose by calling the gate first inside your existing middleware and returning its Response when defined. On Next.js 16+ use `proxy.ts` for framework logic; `middleware.ts` belongs to the gate.\n\n## Environment variables (Vercel production only)\n\n`OKTA_CLIENT_ID`, `OKTA_CLIENT_SECRET`, `OKTA_ISSUER` — pushed by the Technology team from your app's Okta registration. `SESSION_SECRET` — any random string ≥32 chars. Nothing is needed locally.\n\nIf any are missing in an enforcing deployed environment the gate fails closed with a 500 naming the missing key names. Deployed `AUTH_BYPASS`, `DISABLE_AUTH`, and `NEXT_PUBLIC_DISABLE_AUTH` variables are rejected whenever present, regardless of value; they are not escape hatches.\n\n## Local Dev User Switcher\n\nLocal development needs no IdP credentials. `GET /auth/dev/users` returns the allowlisted `devUsers`; `POST /auth/dev/switch` with `{ \"sub\": \"...\" }` selects one through an httpOnly local session cookie. The gate then injects that selected principal through the same server-side `getUser()` path used in deployment.\n\nThe adapter runs only for loopback hosts (`localhost`, `127.0.0.1`, or `::1`); a local-mode request addressed to any other host fails closed. These are test principals backed by local/isolated development data. Do not use this adapter to reach production data or treat it as a security control: local source and processes are developer-controlled. The endpoints return `404` in every deployed Vercel environment, including legacy-open previews.\n\n## Showing who's signed in\n\nThe gate serves `GET /auth/me` → `{ sub, name, email }` (a fake Dev User outside production). Framework-free widget:\n\n```html\n<div id=\"whoami\"></div>\n<script>\n  fetch('/auth/me').then(r => r.ok ? r.json() : null).then(user => {\n    if (!user) return\n    document.getElementById('whoami').innerHTML =\n      `${user.name} · <a href=\"/auth/signout\">Sign out</a>`\n  })\n</script>\n```\n\nEndpoints: `/auth/signin` (optionally `?returnTo=/path`), `/auth/signout`, `/auth/me`, `/auth/callback` (Okta's redirect URI — register `https://<your-domain>/auth/callback`).\n\n## Identity in backend code (v0.2+)\n\nOn every request the gate lets through, it injects an `x-sonance-user` request header (spoof-proof: any client-supplied value is stripped and replaced). Read it with the bundled helper — works in any route handler or server function:\n\n```ts\nimport { getUser } from '@danainnovations/sonance-gate'\n\nexport async function POST(req: Request) {\n  const user = getUser(req)          // { sub, name, email } | null\n  if (!user) return new Response('unauthenticated', { status: 401 })\n  // ... write rows keyed by user.email, etc.\n}\n```\n\nIn local development it returns the selected dev principal, so backend code exercises the same authorization path. Legacy-open previews return the default Dev User until they are migrated; `enforceInPreview: true` uses the real Okta session. On `publicPaths` routes it returns `null` unless the visitor happens to have a session.\n\n### Signing out\n\nBy default `/auth/signout` clears the app session and forces a fresh Okta\ncredential prompt on the next visit, while your Okta org session for other apps\nstays alive. Because that Okta session survives, some orgs re-authenticate the\nbrowser without a visible prompt, which reads to users as \"sign-out did\nnothing\".\n\n`oktaLogout: true` ends the Okta session as well (OIDC RP-initiated logout):\n`/auth/signout` clears the app cookies and hands the browser to Okta's\nend-session endpoint, which signs the user out and returns them to your app's\nroot.\n\nIt is opt-in per app because it needs one thing from the Technology team first:\n\n> Add `https://<your-domain>/` to **Sign-out redirect URIs** on this app's Okta\n> registration.\n\nTurn the option on only once that is registered — Okta rejects the logout\notherwise. Sign-out degrades to the local-only behaviour, never an error page,\nwhenever the Okta session cannot be ended (no session to prove, a session\nissued before this upgrade, a Teams-issued session, or Okta unreachable).\n\n## Microsoft Teams mode\n\nApps packaged as Teams tabs authenticate silently — no Okta prompt inside Teams.\nEnable per app:\n\n```ts\nexport default createGate({\n  publicPaths: [],\n  teams: true,\n})\n```\n\nProduction env additionally uses `ENTRA_CLIENT_ID` and `ENTRA_TENANT_ID`\n(non-secret, pushed by the Technology team alongside the Okta keys).\n\n`teams: true` is safe to deploy **before** the Entra registration exists. If the\nEntra keys are absent, only the Teams lane degrades — the browser/Okta lane\nenforces exactly as normal (no 500). An `?inTeams=true` visit falls back to the\nOkta redirect instead of the bootstrap page, and `POST /auth/teams` returns a\n503 naming the missing key names (names only, no secrets). The Teams lane\nactivates automatically once both keys are present on the next deploy — no code\nchange and no strict \"Entra keys before `teams: true`\" ordering. (Missing\n`OKTA_*`/`SESSION_SECRET` keys still fail the whole app closed, as before.)\n\nHow it works: the Teams manifest's `contentUrl` carries `?inTeams=true`. For an\nunauthenticated HTML request with that hint (or the `__Host-sg_teams` marker\ncookie), the gate serves a static bootstrap page that calls\n`authentication.getAuthToken()` (Teams JS SDK) and POSTs the Entra ID JWT to\n`/auth/teams`. The gate validates it against Microsoft's JWKS (issuer\n`https://login.microsoftonline.com/{tenant}/v2.0`, audience = client ID or\n`api://{host}/{clientId}`, tenant pinned), then mints the ordinary session.\nServer code stays lane-blind: `getUser(request)` works identically.\n\n- `?inTeams=true` is a routing hint, never a bypass: outside Teams the\n  bootstrap falls back to the normal Okta redirect. Fail closed always.\n- Teams-lane cookies use `SameSite=None; Secure; Partitioned` (CHIPS) so they\n  survive the cross-site iframe. Browser-lane cookies stay `SameSite=Lax`.\n- Foreign-tenant users get a friendly \"Sonance employees only\" page (403).\n- Identity: key on `email`. `sub` is lane-specific (Okta subject vs Entra\n  object ID) — do not use it as a cross-lane key.\n","readmeFilename":"README.md"}