@seamless-auth/express 0.0.2-beta.9 → 0.0.3-beta.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
@@ -110,14 +110,17 @@ This keeps trust boundaries clean and auditable.
110
110
 
111
111
  ## Environment Variables
112
112
 
113
- | Variable | Description | Example |
114
- | -------------------- | ----------------------------------------- | ------------------------------------------------------ |
115
- | `AUTH_SERVER_URL` | Base URL of your Seamless Auth Server | `https://auth.client.com` |
116
- | `COOKIE_SIGNING_KEY` | Secret for signing API session cookies | `local-dev-secret` |
117
- | `API_SERVICE_TOKEN` | API → Auth Server service secret | `shared-m2m-value` |
118
- | `APP_ORIGIN` | Your site URL (or localhost in demo mode) | `https://myapp.com` |
119
- | `DATABASE_URL` | Database URL for your API | `postgres://myuser:mypassword@localhost:5432/seamless` |
120
- | `DB_NAME` | Name of your database | `seamless` |
113
+ | Variable | Description | Example |
114
+ | -------------------- | ----------------------------------------- | ------------------------- |
115
+ | `AUTH_SERVER_URL` | Base URL of your Seamless Auth Server | `https://auth.client.com` |
116
+ | `COOKIE_SIGNING_KEY` | Secret for signing API session cookies | `local-dev-secret` |
117
+ | `API_SERVICE_TOKEN` | API → Auth Server service secret | `shared-m2m-value` |
118
+ | `APP_ORIGIN` | Your site URL (or localhost in demo mode) | `https://myapp.com` |
119
+ | `DB_HOST` | Database Host | `localhost` |
120
+ | `DB_PORT` | Database Port | `5432` |
121
+ | `DB_USER` | Database user | `myuser` |
122
+ | `DB_PASSWORD` | Database password | `mypassword` |
123
+ | `DB_NAME` | Name of your database | `seamless` |
121
124
 
122
125
  ---
123
126
 
@@ -219,7 +222,7 @@ Returned shape (example):
219
222
  AUTH_SERVER_URL=http://localhost:5312
220
223
  SEAMLESS_SERVICE_TOKEN=generated-secret
221
224
  COOKIE_SIGNING_KEY=local-dev-key
222
- FRONTEND_URL=https://localhost:5001
225
+ FRONTEND_URL=http://localhost:5001
223
226
  ```
224
227
 
225
228
  ---
package/dist/index.d.ts CHANGED
@@ -1,12 +1,26 @@
1
1
  import { Router, Request, Response, NextFunction, RequestHandler } from 'express';
2
2
 
3
- interface SeamlessAuthServerOptions {
3
+ type SeamlessAuthServerOptions = {
4
4
  authServerUrl: string;
5
+ cookieSecret: string;
6
+ serviceSecret: string;
7
+ issuer: string;
8
+ audience: string;
9
+ jwksKid?: string;
5
10
  cookieDomain?: string;
6
11
  accessCookieName?: string;
7
12
  registrationCookieName?: string;
8
13
  refreshCookieName?: string;
9
14
  preAuthCookieName?: string;
15
+ };
16
+ interface SeamlessAuthUser {
17
+ id: string;
18
+ sub: string;
19
+ roles: string[];
20
+ email: string;
21
+ phone: string;
22
+ iat?: number;
23
+ exp?: number;
10
24
  }
11
25
  /**
12
26
  * Creates an Express Router that proxies all authentication traffic to a Seamless Auth server.
@@ -36,6 +50,9 @@ interface SeamlessAuthServerOptions {
36
50
  * app.use("/auth", createSeamlessAuthServer({
37
51
  * authServerUrl: "https://identifier.seamlessauth.com",
38
52
  * cookieDomain: "mycompany.com",
53
+ * cookieSecret: "someLongRandomValue"
54
+ * serviceSecret: "someLongRandomValueToo"
55
+ * jwksKid: "dev-main"
39
56
  * accessCookieName: "sa_access",
40
57
  * registrationCookieName: "sa_registration",
41
58
  * refreshCookieName: "sa_refresh",
@@ -44,6 +61,9 @@ interface SeamlessAuthServerOptions {
44
61
  *
45
62
  * @param opts - Configuration options for the Seamless Auth proxy:
46
63
  * - `authServerUrl` — Base URL of your Seamless Auth instance (required)
64
+ * - `cookieSecret` — The value to encode your cookies secrets with (required)
65
+ * - `serviceSecret` - An machine to machine shared secret that matches your auth servers (required)
66
+ * - `jwksKid` - The active jwks KID
47
67
  * - `cookieDomain` — Domain attribute applied to all auth cookies
48
68
  * - `accessCookieName` — Name of the session access cookie
49
69
  * - `registrationCookieName` — Name of the ephemeral registration cookie
@@ -172,6 +192,6 @@ interface EnsureCookiesMiddlewareOptions {
172
192
  }
173
193
  declare function createEnsureCookiesMiddleware(opts: EnsureCookiesMiddlewareOptions): (req: Request, res: Response, next: NextFunction) => Promise<void>;
174
194
 
175
- declare function getSeamlessUser(req: Request, authServerUrl: string, cookieName?: string): Promise<any>;
195
+ declare function getSeamlessUser(req: Request, opts: SeamlessAuthServerOptions): Promise<any>;
176
196
 
177
- export { createEnsureCookiesMiddleware, createSeamlessAuthServer as default, getSeamlessUser, requireAuth, requireRole };
197
+ export { type SeamlessAuthServerOptions, type SeamlessAuthUser, createEnsureCookiesMiddleware, createSeamlessAuthServer as default, getSeamlessUser, requireAuth, requireRole };
package/dist/index.js CHANGED
@@ -98,7 +98,7 @@ function applyResult(res, req, result, opts, cookieSigner) {
98
98
  import { loginHandler } from "@seamless-auth/core/handlers/login";
99
99
  async function login(req, res, opts) {
100
100
  const cookieSigner = {
101
- secret: process.env.COOKIE_SIGNING_KEY,
101
+ secret: opts.cookieSecret,
102
102
  secure: process.env.NODE_ENV === "production",
103
103
  sameSite: process.env.NODE_ENV === "production" ? "none" : "lax"
104
104
  };
@@ -138,16 +138,16 @@ import { finishLoginHandler } from "@seamless-auth/core/handlers/finishLogin";
138
138
 
139
139
  // src/internal/buildAuthorization.ts
140
140
  import { createServiceToken } from "@seamless-auth/core";
141
- function buildServiceAuthorization(req) {
141
+ function buildServiceAuthorization(req, opts) {
142
142
  if (!req.cookiePayload?.sub && !req.user.sub) {
143
143
  return void 0;
144
144
  }
145
145
  const token = createServiceToken({
146
146
  subject: req.cookiePayload?.sub || req.user.sub,
147
- issuer: process.env.APP_ORIGIN,
148
- audience: process.env.AUTH_SERVER_URL,
149
- serviceSecret: process.env.API_SERVICE_TOKEN,
150
- keyId: "dev-main"
147
+ issuer: opts.issuer,
148
+ audience: opts.audience,
149
+ serviceSecret: opts.serviceSecret,
150
+ keyId: opts.jwksKid || "dev-main"
151
151
  });
152
152
  return `Bearer ${token}`;
153
153
  }
@@ -155,11 +155,11 @@ function buildServiceAuthorization(req) {
155
155
  // src/handlers/finishLogin.ts
156
156
  async function finishLogin(req, res, opts) {
157
157
  const cookieSigner = {
158
- secret: process.env.COOKIE_SIGNING_KEY,
158
+ secret: opts.cookieSecret,
159
159
  secure: process.env.NODE_ENV === "production",
160
160
  sameSite: process.env.NODE_ENV === "production" ? "none" : "lax"
161
161
  };
162
- const authorization = buildServiceAuthorization(req);
162
+ const authorization = buildServiceAuthorization(req, opts);
163
163
  const result = await finishLoginHandler(
164
164
  { body: req.body, authorization },
165
165
  {
@@ -196,7 +196,7 @@ async function finishLogin(req, res, opts) {
196
196
  import { registerHandler } from "@seamless-auth/core/handlers/register";
197
197
  async function register(req, res, opts) {
198
198
  const cookieSigner = {
199
- secret: process.env.COOKIE_SIGNING_KEY,
199
+ secret: opts.cookieSecret,
200
200
  secure: process.env.NODE_ENV === "production",
201
201
  sameSite: process.env.NODE_ENV === "production" ? "none" : "lax"
202
202
  };
@@ -235,11 +235,11 @@ async function register(req, res, opts) {
235
235
  import { finishRegisterHandler } from "@seamless-auth/core/handlers/finishRegister";
236
236
  async function finishRegister(req, res, opts) {
237
237
  const cookieSigner = {
238
- secret: process.env.COOKIE_SIGNING_KEY,
238
+ secret: opts.cookieSecret,
239
239
  secure: process.env.NODE_ENV === "production",
240
240
  sameSite: process.env.NODE_ENV === "production" ? "none" : "lax"
241
241
  };
242
- const authorization = buildServiceAuthorization(req);
242
+ const authorization = buildServiceAuthorization(req, opts);
243
243
  const result = await finishRegisterHandler(
244
244
  { body: req.body, authorization },
245
245
  {
@@ -269,13 +269,13 @@ async function finishRegister(req, res, opts) {
269
269
  if (result.error) {
270
270
  return res.status(result.status).json(result.error);
271
271
  }
272
- res.status(result.status).end();
272
+ res.status(result.status).json({ message: "success" });
273
273
  }
274
274
 
275
275
  // src/handlers/me.ts
276
276
  import { meHandler } from "@seamless-auth/core/handlers/me";
277
277
  async function me(req, res, opts) {
278
- const authorization = buildServiceAuthorization(req);
278
+ const authorization = buildServiceAuthorization(req, opts);
279
279
  const result = await meHandler({
280
280
  authServerUrl: opts.authServerUrl,
281
281
  preAuthCookieName: opts.preAuthCookieName,
@@ -307,8 +307,7 @@ async function logout(req, res, opts) {
307
307
 
308
308
  // src/createServer.ts
309
309
  import {
310
- authFetch,
311
- createServiceToken as createServiceToken2
310
+ authFetch
312
311
  } from "@seamless-auth/core";
313
312
  function createSeamlessAuthServer(opts) {
314
313
  const r = express.Router();
@@ -316,6 +315,11 @@ function createSeamlessAuthServer(opts) {
316
315
  r.use(cookieParser());
317
316
  const resolvedOpts = {
318
317
  authServerUrl: opts.authServerUrl,
318
+ issuer: opts.issuer,
319
+ audience: opts.audience,
320
+ cookieSecret: opts.cookieSecret,
321
+ serviceSecret: opts.serviceSecret,
322
+ jwksKid: opts.jwksKid ?? "dev-main",
319
323
  cookieDomain: opts.cookieDomain ?? "",
320
324
  accessCookieName: opts.accessCookieName ?? "seamless-access",
321
325
  registrationCookieName: opts.registrationCookieName ?? "seamless-ephemeral",
@@ -339,14 +343,11 @@ function createSeamlessAuthServer(opts) {
339
343
  res.status(401).json({ error: "registeration session required" });
340
344
  return;
341
345
  }
342
- const authorization = buildServiceAuthorization2(req);
346
+ const authorization = buildServiceAuthorization(req, resolvedOpts);
347
+ const options = method == "GET" ? { method, authorization } : { method, authorization, body: req.body };
343
348
  const upstream = await authFetch(
344
349
  `${resolvedOpts.authServerUrl}/${path}`,
345
- {
346
- method,
347
- body: req.body,
348
- authorization
349
- }
350
+ options
350
351
  );
351
352
  const data = await upstream.json();
352
353
  res.status(upstream.status).json(data);
@@ -359,26 +360,13 @@ function createSeamlessAuthServer(opts) {
359
360
  registrationCookieName: resolvedOpts.registrationCookieName,
360
361
  refreshCookieName: resolvedOpts.refreshCookieName,
361
362
  preAuthCookieName: resolvedOpts.preAuthCookieName,
362
- cookieSecret: process.env.COOKIE_SIGNING_KEY,
363
- serviceSecret: process.env.API_SERVICE_TOKEN,
364
- issuer: process.env.APP_ORIGIN,
365
- audience: process.env.AUTH_SERVER_URL,
366
- keyId: "dev-main"
363
+ cookieSecret: resolvedOpts.cookieSecret,
364
+ serviceSecret: resolvedOpts.serviceSecret,
365
+ issuer: resolvedOpts.issuer,
366
+ audience: resolvedOpts.authServerUrl,
367
+ keyId: resolvedOpts.jwksKid
367
368
  })
368
369
  );
369
- function buildServiceAuthorization2(req) {
370
- if (!req.cookiePayload?.sub) {
371
- return void 0;
372
- }
373
- const token = createServiceToken2({
374
- subject: req.cookiePayload.sub,
375
- issuer: process.env.APP_ORIGIN,
376
- audience: process.env.AUTH_SERVER_URL,
377
- serviceSecret: process.env.API_SERVICE_TOKEN,
378
- keyId: "dev-main"
379
- });
380
- return `Bearer ${token}`;
381
- }
382
370
  r.post(
383
371
  "/webAuthn/login/start",
384
372
  proxyWithIdentity("webAuthn/login/start", "preAuth")
@@ -445,7 +433,9 @@ function requireAuth(opts) {
445
433
  return;
446
434
  }
447
435
  const user = {
436
+ id: payload.sub,
448
437
  sub: payload.sub,
438
+ // TODO: Silly to store the same value twice. Search every where its used and phase this out.
449
439
  roles: Array.isArray(payload.roles) ? payload.roles : [],
450
440
  email: payload.email,
451
441
  phone: payload.phone,
@@ -491,12 +481,12 @@ function requireRole(requiredRoles) {
491
481
  import {
492
482
  getSeamlessUser as getSeamlessUserCore
493
483
  } from "@seamless-auth/core";
494
- async function getSeamlessUser(req, authServerUrl, cookieName = "seamless-access") {
495
- const authorization = buildServiceAuthorization(req);
484
+ async function getSeamlessUser(req, opts) {
485
+ const authorization = buildServiceAuthorization(req, opts);
496
486
  return getSeamlessUserCore(req.cookies ?? {}, {
497
- authServerUrl,
498
- cookieSecret: process.env.COOKIE_SIGNING_KEY,
499
- cookieName,
487
+ authServerUrl: opts.authServerUrl,
488
+ cookieSecret: opts.cookieSecret,
489
+ cookieName: opts.accessCookieName ?? "seamless-access",
500
490
  authorization
501
491
  });
502
492
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@seamless-auth/express",
3
- "version": "0.0.2-beta.9",
3
+ "version": "0.0.3-beta.0",
4
4
  "description": "Express adapter for Seamless Auth passwordless authentication",
5
5
  "license": "AGPL-3.0-only",
6
6
  "type": "module",
@@ -33,8 +33,8 @@
33
33
  "url": "https://github.com/fells-code/seamless-auth-server/tree/main/packages/express"
34
34
  },
35
35
  "peerDependencies": {
36
- "express": ">=4.18.0",
37
- "@types/express": ">=4.17.0"
36
+ "@types/express": ">=4.17.0",
37
+ "express": ">=4.18.0"
38
38
  },
39
39
  "dependencies": {
40
40
  "@seamless-auth/core": "beta",
@@ -46,8 +46,8 @@
46
46
  "@types/jest": "^29.5.14",
47
47
  "@types/jsonwebtoken": "^9.0.10",
48
48
  "jest": "^29.7.0",
49
- "ts-node": "^10.9.2",
50
49
  "supertest": "^7.2.2",
50
+ "ts-node": "^10.9.2",
51
51
  "tsup": "^8.5.1",
52
52
  "typescript": "^5.5.0"
53
53
  },