@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 +12 -9
- package/dist/index.d.ts +23 -3
- package/dist/index.js +34 -44
- package/package.json +4 -4
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
|
-
| `
|
|
120
|
-
| `
|
|
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=
|
|
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
|
-
|
|
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,
|
|
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:
|
|
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:
|
|
148
|
-
audience:
|
|
149
|
-
serviceSecret:
|
|
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:
|
|
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:
|
|
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:
|
|
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).
|
|
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 =
|
|
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:
|
|
363
|
-
serviceSecret:
|
|
364
|
-
issuer:
|
|
365
|
-
audience:
|
|
366
|
-
keyId:
|
|
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,
|
|
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:
|
|
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.
|
|
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.
|
|
37
|
-
"
|
|
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
|
},
|