@danainnovations/sonance-gate 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -60,4 +60,20 @@ The gate serves `GET /auth/me` → `{ sub, name, email }` (a fake Dev User outsi
60
60
 
61
61
  Endpoints: `/auth/signin` (optionally `?returnTo=/path`), `/auth/signout`, `/auth/me`, `/auth/callback` (Okta's redirect URI — register `https://<your-domain>/auth/callback`).
62
62
 
63
+ ## Identity in backend code (v0.2+)
64
+
65
+ On 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:
66
+
67
+ ```ts
68
+ import { getUser } from '@danainnovations/sonance-gate'
69
+
70
+ export async function POST(req: Request) {
71
+ const user = getUser(req) // { sub, name, email } | null
72
+ if (!user) return new Response('unauthenticated', { status: 401 })
73
+ // ... write rows keyed by user.sub, etc.
74
+ }
75
+ ```
76
+
77
+ Outside production it returns the fake Dev User, so backend code behaves identically in local dev. On `publicPaths` routes it returns `null` unless the visitor happens to have a session.
78
+
63
79
  Sign-out clears the app session and forces a fresh Okta credential prompt on the next visit (your Okta org session for other apps stays alive).
package/dist/index.cjs CHANGED
@@ -31,7 +31,8 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
31
31
  var index_exports = {};
32
32
  __export(index_exports, {
33
33
  DEV_USER: () => DEV_USER,
34
- createGate: () => createGate
34
+ createGate: () => createGate,
35
+ getUser: () => getUser
35
36
  });
36
37
  module.exports = __toCommonJS(index_exports);
37
38
  var import_middleware = require("@vercel/functions/middleware");
@@ -197,7 +198,7 @@ async function handleRequest(request, opts = {}, vars = process.env, deps = {})
197
198
  if (env.mode === "open") {
198
199
  if (path === "/auth/me") return json(DEV_USER);
199
200
  if (path === "/auth/signin" || path === "/auth/signout") return redirect("/");
200
- return void 0;
201
+ return { user: DEV_USER };
201
202
  }
202
203
  if (env.mode === "misconfigured") {
203
204
  return new Response(
@@ -278,8 +279,9 @@ async function enforce(request, path, env, opts, deps) {
278
279
  const { sub, name, email } = session;
279
280
  return json({ sub, name, email });
280
281
  }
281
- if (isPublic(path, opts.publicPaths)) return void 0;
282
- if (session) return void 0;
282
+ const passUser = session ? { sub: session.sub, name: session.name, email: session.email } : null;
283
+ if (isPublic(path, opts.publicPaths)) return { user: passUser };
284
+ if (session) return { user: passUser };
283
285
  if (wantsHtml(request)) {
284
286
  return startSignin(url, url.pathname + url.search, env, deps, signedOut);
285
287
  }
@@ -348,13 +350,34 @@ async function handleCallback(request, url, env, deps) {
348
350
  }
349
351
 
350
352
  // src/index.ts
351
- function createGate(opts = {}) {
353
+ var USER_HEADER = "x-sonance-user";
354
+ function createGate(opts = {}, vars, deps) {
352
355
  return async (request) => {
353
- return await handleRequest(request, opts) ?? (0, import_middleware.next)();
356
+ const result = await handleRequest(request, opts, vars, deps);
357
+ if (result instanceof Response) return result;
358
+ const headers = new Headers(request.headers);
359
+ headers.delete(USER_HEADER);
360
+ if (result.user) {
361
+ headers.set(USER_HEADER, Buffer.from(JSON.stringify(result.user)).toString("base64url"));
362
+ }
363
+ return (0, import_middleware.next)({ request: { headers } });
354
364
  };
355
365
  }
366
+ function getUser(source) {
367
+ const headers = source instanceof Headers ? source : source.headers;
368
+ const raw = headers.get(USER_HEADER);
369
+ if (!raw) return null;
370
+ try {
371
+ const parsed = JSON.parse(Buffer.from(raw, "base64url").toString());
372
+ if (typeof parsed?.sub === "string" && typeof parsed?.email === "string") return parsed;
373
+ return null;
374
+ } catch {
375
+ return null;
376
+ }
377
+ }
356
378
  // Annotate the CommonJS export names for ESM import in node:
357
379
  0 && (module.exports = {
358
380
  DEV_USER,
359
- createGate
381
+ createGate,
382
+ getUser
360
383
  });
package/dist/index.d.cts CHANGED
@@ -1,12 +1,22 @@
1
- interface GateOptions {
2
- publicPaths?: string[];
3
- }
4
- declare const DEV_USER: {
1
+ import { JWTVerifyGetKey } from 'jose';
2
+
3
+ interface SessionClaims {
5
4
  sub: string;
6
5
  name: string;
7
6
  email: string;
8
- };
7
+ }
8
+
9
+ interface GateOptions {
10
+ publicPaths?: string[];
11
+ }
12
+ interface GateDeps {
13
+ fetchFn?: typeof fetch;
14
+ jwks?: JWTVerifyGetKey;
15
+ }
16
+ declare const DEV_USER: SessionClaims;
9
17
 
10
- declare function createGate(opts?: GateOptions): (request: Request) => Promise<Response>;
18
+ declare function createGate(opts?: GateOptions, vars?: Record<string, string | undefined>, deps?: GateDeps): (request: Request) => Promise<Response>;
19
+ /** Read the gate-injected identity from an incoming request (server-side). */
20
+ declare function getUser(source: Request | Headers): SessionClaims | null;
11
21
 
12
- export { DEV_USER, type GateOptions, createGate };
22
+ export { DEV_USER, type GateOptions, type SessionClaims, createGate, getUser };
package/dist/index.d.ts CHANGED
@@ -1,12 +1,22 @@
1
- interface GateOptions {
2
- publicPaths?: string[];
3
- }
4
- declare const DEV_USER: {
1
+ import { JWTVerifyGetKey } from 'jose';
2
+
3
+ interface SessionClaims {
5
4
  sub: string;
6
5
  name: string;
7
6
  email: string;
8
- };
7
+ }
8
+
9
+ interface GateOptions {
10
+ publicPaths?: string[];
11
+ }
12
+ interface GateDeps {
13
+ fetchFn?: typeof fetch;
14
+ jwks?: JWTVerifyGetKey;
15
+ }
16
+ declare const DEV_USER: SessionClaims;
9
17
 
10
- declare function createGate(opts?: GateOptions): (request: Request) => Promise<Response>;
18
+ declare function createGate(opts?: GateOptions, vars?: Record<string, string | undefined>, deps?: GateDeps): (request: Request) => Promise<Response>;
19
+ /** Read the gate-injected identity from an incoming request (server-side). */
20
+ declare function getUser(source: Request | Headers): SessionClaims | null;
11
21
 
12
- export { DEV_USER, type GateOptions, createGate };
22
+ export { DEV_USER, type GateOptions, type SessionClaims, createGate, getUser };
package/dist/index.js CHANGED
@@ -162,7 +162,7 @@ async function handleRequest(request, opts = {}, vars = process.env, deps = {})
162
162
  if (env.mode === "open") {
163
163
  if (path === "/auth/me") return json(DEV_USER);
164
164
  if (path === "/auth/signin" || path === "/auth/signout") return redirect("/");
165
- return void 0;
165
+ return { user: DEV_USER };
166
166
  }
167
167
  if (env.mode === "misconfigured") {
168
168
  return new Response(
@@ -243,8 +243,9 @@ async function enforce(request, path, env, opts, deps) {
243
243
  const { sub, name, email } = session;
244
244
  return json({ sub, name, email });
245
245
  }
246
- if (isPublic(path, opts.publicPaths)) return void 0;
247
- if (session) return void 0;
246
+ const passUser = session ? { sub: session.sub, name: session.name, email: session.email } : null;
247
+ if (isPublic(path, opts.publicPaths)) return { user: passUser };
248
+ if (session) return { user: passUser };
248
249
  if (wantsHtml(request)) {
249
250
  return startSignin(url, url.pathname + url.search, env, deps, signedOut);
250
251
  }
@@ -313,12 +314,33 @@ async function handleCallback(request, url, env, deps) {
313
314
  }
314
315
 
315
316
  // src/index.ts
316
- function createGate(opts = {}) {
317
+ var USER_HEADER = "x-sonance-user";
318
+ function createGate(opts = {}, vars, deps) {
317
319
  return async (request) => {
318
- return await handleRequest(request, opts) ?? next();
320
+ const result = await handleRequest(request, opts, vars, deps);
321
+ if (result instanceof Response) return result;
322
+ const headers = new Headers(request.headers);
323
+ headers.delete(USER_HEADER);
324
+ if (result.user) {
325
+ headers.set(USER_HEADER, Buffer.from(JSON.stringify(result.user)).toString("base64url"));
326
+ }
327
+ return next({ request: { headers } });
319
328
  };
320
329
  }
330
+ function getUser(source) {
331
+ const headers = source instanceof Headers ? source : source.headers;
332
+ const raw = headers.get(USER_HEADER);
333
+ if (!raw) return null;
334
+ try {
335
+ const parsed = JSON.parse(Buffer.from(raw, "base64url").toString());
336
+ if (typeof parsed?.sub === "string" && typeof parsed?.email === "string") return parsed;
337
+ return null;
338
+ } catch {
339
+ return null;
340
+ }
341
+ }
321
342
  export {
322
343
  DEV_USER,
323
- createGate
344
+ createGate,
345
+ getUser
324
346
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@danainnovations/sonance-gate",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Framework-agnostic Sonance Okta SSO gate for Vercel apps (Routing Middleware)",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",