@fitzzero/quickdraw-core 5.1.1 → 5.1.2
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 +533 -180
- package/dist/{admin-DKwkIh5i.d.ts → admin-DWWrT1h6.d.ts} +2 -2
- package/dist/{adminSchemas-2DUA_hZ8.d.ts → adminSchemas-D4bFKsrb.d.ts} +6 -4
- package/dist/{auth-BhWqdyb-.d.ts → auth-DXiSJZaf.d.ts} +42 -16
- package/dist/{caller-CXTFswBO.d.ts → caller-BT-Mv4bD.d.ts} +117 -22
- package/dist/{chunk-RT6OILOI.js → chunk-4D2DOGAW.js} +2 -14
- package/dist/chunk-4D2DOGAW.js.map +1 -0
- package/dist/chunk-5CGO426C.js +15 -0
- package/dist/chunk-5CGO426C.js.map +1 -0
- package/dist/{chunk-RZTP4IEW.js → chunk-6K5BY47X.js} +4594 -3481
- package/dist/chunk-6K5BY47X.js.map +1 -0
- package/dist/{chunk-VSQBHPWH.js → chunk-753VJIUP.js} +13 -15
- package/dist/chunk-753VJIUP.js.map +1 -0
- package/dist/{chunk-ZIVPHYRG.js → chunk-7QCUX4TK.js} +4 -4
- package/dist/chunk-7QCUX4TK.js.map +1 -0
- package/dist/chunk-AK23XSWD.js +16 -0
- package/dist/chunk-AK23XSWD.js.map +1 -0
- package/dist/{chunk-SHEGLZEP.js → chunk-AONAFOYU.js} +14 -5
- package/dist/{chunk-SHEGLZEP.js.map → chunk-AONAFOYU.js.map} +1 -1
- package/dist/chunk-DKE6QO26.js +136 -0
- package/dist/chunk-DKE6QO26.js.map +1 -0
- package/dist/{chunk-H23KEFBA.js → chunk-DUBU3BB3.js} +30 -1
- package/dist/chunk-DUBU3BB3.js.map +1 -0
- package/dist/{chunk-CDKG4TFA.js → chunk-GKH3LJ7N.js} +1 -1
- package/dist/chunk-GKH3LJ7N.js.map +1 -0
- package/dist/{chunk-OECZPRPW.js → chunk-GSIPUNJL.js} +49 -9
- package/dist/chunk-GSIPUNJL.js.map +1 -0
- package/dist/{chunk-DD4SKCNO.js → chunk-LCC7HNFP.js} +12 -1
- package/dist/chunk-LCC7HNFP.js.map +1 -0
- package/dist/{chunk-2PWMUVC7.js → chunk-MBH6IDSM.js} +480 -12
- package/dist/chunk-MBH6IDSM.js.map +1 -0
- package/dist/{chunk-QCHKWIRS.js → chunk-NMXYENH4.js} +10 -10
- package/dist/{chunk-QCHKWIRS.js.map → chunk-NMXYENH4.js.map} +1 -1
- package/dist/chunk-NO4M6PO5.js +101 -0
- package/dist/chunk-NO4M6PO5.js.map +1 -0
- package/dist/{chunk-TBMKJ26R.js → chunk-NW6U45HX.js} +3 -1
- package/dist/chunk-NW6U45HX.js.map +1 -0
- package/dist/{chunk-REX5YPHX.js → chunk-RJ5I2VO3.js} +74 -19
- package/dist/chunk-RJ5I2VO3.js.map +1 -0
- package/dist/{chunk-QEKVYG5F.js → chunk-RL4VKLBX.js} +1 -1
- package/dist/{chunk-QEKVYG5F.js.map → chunk-RL4VKLBX.js.map} +1 -1
- package/dist/chunk-XH2IZVLT.js +62 -0
- package/dist/chunk-XH2IZVLT.js.map +1 -0
- package/dist/{chunk-4DIQHASK.js → chunk-Z5ILULF2.js} +26 -7
- package/dist/chunk-Z5ILULF2.js.map +1 -0
- package/dist/cli/quickdraw-docs.js +5 -3
- package/dist/cli/quickdraw-docs.js.map +1 -1
- package/dist/client/index.d.ts +30 -18
- package/dist/client/index.js +8 -6
- package/dist/client/index.js.map +1 -1
- package/dist/{clientTypes-Bc-G8n-U.d.ts → clientTypes-hWcPUVS9.d.ts} +3 -3
- package/dist/{connection-Cr1BU07W.d.ts → connection-CgijdGpT.d.ts} +8 -4
- package/dist/{createServer-CP9eDHZZ.d.ts → createServer-Cp-Wk500.d.ts} +36 -14
- package/dist/{createTestApp-UKVGEmOf.d.ts → createTestApp-BB_PkqQ7.d.ts} +16 -10
- package/dist/{envelope-Cp3YcVxC.d.ts → envelope-DzWyIXjj.d.ts} +18 -4
- package/dist/{http-Djj2Inuu.d.ts → http-DotQ5bj8.d.ts} +68 -17
- package/dist/index.d.ts +7 -7
- package/dist/index.js +37 -13
- package/dist/index.js.map +1 -1
- package/dist/{infer-Teo1xpsd.d.ts → infer-BmoZsL9a.d.ts} +43 -10
- package/dist/logger-Yz93KzdV.d.ts +30 -0
- package/dist/{metrics-BdBu5o8G.d.ts → metrics-DW80Exve.d.ts} +2 -2
- package/dist/origins-kvpxnVcW.d.ts +8 -0
- package/dist/prisma/index.d.ts +5 -5
- package/dist/prisma/index.js +130 -67
- package/dist/prisma/index.js.map +1 -1
- package/dist/{rateLimit-ZMIZJUZF.js → rateLimit-5RKBFLUR.js} +2 -2
- package/dist/server/auth/index.d.ts +82 -33
- package/dist/server/auth/index.js +273 -268
- package/dist/server/auth/index.js.map +1 -1
- package/dist/server/express/index.d.ts +9 -2
- package/dist/server/express/index.js +1 -1
- package/dist/server/index.d.ts +147 -67
- package/dist/server/index.js +520 -381
- package/dist/server/index.js.map +1 -1
- package/dist/server/mcp/index.d.ts +63 -17
- package/dist/server/mcp/index.js +256 -54
- package/dist/server/mcp/index.js.map +1 -1
- package/dist/server/otel.d.ts +3 -3
- package/dist/{sharing-DVa8W-yR.d.ts → sharing-UDLn1049.d.ts} +3 -3
- package/dist/testing/client.d.ts +14 -14
- package/dist/testing/client.js +9 -7
- package/dist/testing/client.js.map +1 -1
- package/dist/testing/index.d.ts +12 -12
- package/dist/testing/index.js +21 -12
- package/dist/testing/index.js.map +1 -1
- package/dist/testing/mock.d.ts +5 -5
- package/dist/testing/mock.js +8 -6
- package/dist/{types-Cu0vmx7c.d.ts → types-CYliTP65.d.ts} +1 -1
- package/dist/utils/index.d.ts +1 -1
- package/package.json +1 -1
- package/dist/chunk-2PWMUVC7.js.map +0 -1
- package/dist/chunk-4DIQHASK.js.map +0 -1
- package/dist/chunk-CDKG4TFA.js.map +0 -1
- package/dist/chunk-DD4SKCNO.js.map +0 -1
- package/dist/chunk-H23KEFBA.js.map +0 -1
- package/dist/chunk-OECZPRPW.js.map +0 -1
- package/dist/chunk-REX5YPHX.js.map +0 -1
- package/dist/chunk-RT6OILOI.js.map +0 -1
- package/dist/chunk-RZTP4IEW.js.map +0 -1
- package/dist/chunk-TBMKJ26R.js.map +0 -1
- package/dist/chunk-VSQBHPWH.js.map +0 -1
- package/dist/chunk-YSWH776Y.js +0 -13
- package/dist/chunk-YSWH776Y.js.map +0 -1
- package/dist/chunk-ZIVPHYRG.js.map +0 -1
- package/dist/logger-BY28acW-.d.ts +0 -16
- /package/dist/{rateLimit-ZMIZJUZF.js.map → rateLimit-5RKBFLUR.js.map} +0 -0
|
@@ -1,7 +1,15 @@
|
|
|
1
|
+
import {
|
|
2
|
+
OAUTH_RETURN_ORIGIN_COOKIE,
|
|
3
|
+
originAllowlist,
|
|
4
|
+
rememberRouteOrigins,
|
|
5
|
+
routeOriginsOf,
|
|
6
|
+
validateRedirectOrigin
|
|
7
|
+
} from "../../chunk-NO4M6PO5.js";
|
|
1
8
|
import {
|
|
2
9
|
HOST_SESSION_COOKIE,
|
|
3
10
|
SESSION_COOKIE,
|
|
4
11
|
clearSessionCookie,
|
|
12
|
+
clearStaleHostSession,
|
|
5
13
|
cookieDomainFromEnv,
|
|
6
14
|
cookieToken,
|
|
7
15
|
cookiesOf,
|
|
@@ -11,20 +19,22 @@ import {
|
|
|
11
19
|
httpCredentialOf,
|
|
12
20
|
isJsonRequest,
|
|
13
21
|
isSecureSessionRequest,
|
|
22
|
+
markSocketAuth,
|
|
14
23
|
readJsonInput,
|
|
24
|
+
readVerifiedJWT,
|
|
15
25
|
recordSocketSession,
|
|
16
26
|
sessionCookieNameFor,
|
|
17
27
|
sessionCookieNamesFor,
|
|
18
28
|
setSessionCookie,
|
|
19
|
-
tokenOf,
|
|
20
29
|
transportCookieNaming,
|
|
21
30
|
verifyJWT
|
|
22
|
-
} from "../../chunk-
|
|
23
|
-
import "../../chunk-
|
|
31
|
+
} from "../../chunk-RJ5I2VO3.js";
|
|
32
|
+
import "../../chunk-DUBU3BB3.js";
|
|
24
33
|
import "../../chunk-Z2LZ7IZ7.js";
|
|
25
34
|
import {
|
|
26
|
-
consoleLogger
|
|
27
|
-
|
|
35
|
+
consoleLogger,
|
|
36
|
+
errorText
|
|
37
|
+
} from "../../chunk-DKE6QO26.js";
|
|
28
38
|
import {
|
|
29
39
|
INTERNAL_MESSAGE,
|
|
30
40
|
QuickdrawError,
|
|
@@ -128,38 +138,15 @@ var googleProvider = {
|
|
|
128
138
|
// src/server/auth/mock.ts
|
|
129
139
|
import { randomBytes } from "crypto";
|
|
130
140
|
|
|
131
|
-
// src/server/auth/
|
|
132
|
-
var
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
if (!origin) return null;
|
|
141
|
-
const clientUrl = options.clientUrl ?? process.env.CLIENT_URL;
|
|
142
|
-
let parsedOrigin;
|
|
143
|
-
try {
|
|
144
|
-
parsedOrigin = new URL(origin);
|
|
145
|
-
} catch {
|
|
146
|
-
return null;
|
|
147
|
-
}
|
|
148
|
-
const cleanOrigin = parsedOrigin.origin;
|
|
149
|
-
if (clientUrl && cleanOrigin === clientUrl) {
|
|
150
|
-
return cleanOrigin;
|
|
151
|
-
}
|
|
152
|
-
const extra = options.extraAllowedOrigins ?? envExtraOrigins();
|
|
153
|
-
if (extra.includes(cleanOrigin)) {
|
|
154
|
-
return cleanOrigin;
|
|
155
|
-
}
|
|
156
|
-
if (options.allowedPatterns?.some((pattern) => pattern.test(cleanOrigin))) {
|
|
157
|
-
return cleanOrigin;
|
|
158
|
-
}
|
|
159
|
-
if ((options.allowLocalhostInDev ?? true) && process.env.NODE_ENV !== "production" && LOCALHOST_REGEX.test(cleanOrigin)) {
|
|
160
|
-
return cleanOrigin;
|
|
161
|
-
}
|
|
162
|
-
return null;
|
|
141
|
+
// src/server/auth/devSignIns.ts
|
|
142
|
+
var DEVELOPMENT_ENVS = /* @__PURE__ */ new Set(["development", "test"]);
|
|
143
|
+
function developmentSignInsAllowed() {
|
|
144
|
+
const env = process.env.NODE_ENV;
|
|
145
|
+
return env !== void 0 && DEVELOPMENT_ENVS.has(env);
|
|
146
|
+
}
|
|
147
|
+
function nodeEnvLabel() {
|
|
148
|
+
const env = process.env.NODE_ENV;
|
|
149
|
+
return env === void 0 ? "unset" : JSON.stringify(env);
|
|
163
150
|
}
|
|
164
151
|
|
|
165
152
|
// src/server/auth/mock.ts
|
|
@@ -168,7 +155,7 @@ var CODE_TTL_MS = 5 * 60 * 1e3;
|
|
|
168
155
|
var TOKEN_TTL_MS = 60 * 60 * 1e3;
|
|
169
156
|
var DEV_PRIVATE_ORIGIN_REGEX = /^http:\/\/(?:localhost|127\.0\.0\.1|0\.0\.0\.0|10\.\d{1,3}\.\d{1,3}\.\d{1,3}|192\.168\.\d{1,3}\.\d{1,3}|172\.(?:1[6-9]|2\d|3[01])\.\d{1,3}\.\d{1,3}):\d+$/;
|
|
170
157
|
function isMockOAuthEnabled() {
|
|
171
|
-
return process.env.ENABLE_MOCK_OAUTH === "true" &&
|
|
158
|
+
return process.env.ENABLE_MOCK_OAUTH === "true" && developmentSignInsAllowed();
|
|
172
159
|
}
|
|
173
160
|
function createMockOAuthProvider(apiBaseUrl, options = {}) {
|
|
174
161
|
const prefix = options.pathPrefix ?? DEFAULT_MOCK_PATH_PREFIX;
|
|
@@ -269,7 +256,7 @@ function sweep(store) {
|
|
|
269
256
|
}
|
|
270
257
|
}
|
|
271
258
|
function guard(res) {
|
|
272
|
-
if (
|
|
259
|
+
if (!developmentSignInsAllowed()) {
|
|
273
260
|
res.status(404).json({ error: "Not found" });
|
|
274
261
|
return false;
|
|
275
262
|
}
|
|
@@ -356,7 +343,9 @@ function registerMockOAuthProvider(router, options) {
|
|
|
356
343
|
const logger = options.logger ?? consoleLogger;
|
|
357
344
|
if (!isMockOAuthEnabled()) {
|
|
358
345
|
if (process.env.ENABLE_MOCK_OAUTH === "true") {
|
|
359
|
-
logger.error(
|
|
346
|
+
logger.error(
|
|
347
|
+
`Mock OAuth is enabled but NODE_ENV is ${nodeEnvLabel()}: the mock runs only when NODE_ENV is development or test, so its routes are not mounted; set NODE_ENV=development to use it`
|
|
348
|
+
);
|
|
360
349
|
}
|
|
361
350
|
return false;
|
|
362
351
|
}
|
|
@@ -370,7 +359,9 @@ function registerMockOAuthProvider(router, options) {
|
|
|
370
359
|
router.get(`${endpoints.prefix}/authorize`, authorizeHandler(endpoints));
|
|
371
360
|
router.post(`${endpoints.prefix}/token`, tokenHandler(endpoints));
|
|
372
361
|
router.get(`${endpoints.prefix}/userinfo`, userinfoHandler(endpoints));
|
|
373
|
-
logger.
|
|
362
|
+
logger.warn(
|
|
363
|
+
`Mock OAuth provider mounted at ${endpoints.prefix}: anyone who reaches it signs in as any user it lists (NODE_ENV=${process.env.NODE_ENV ?? ""}; development and tests only)`
|
|
364
|
+
);
|
|
374
365
|
return true;
|
|
375
366
|
}
|
|
376
367
|
|
|
@@ -401,6 +392,14 @@ function sendJson(res, status, body) {
|
|
|
401
392
|
function refuse(res, code, message) {
|
|
402
393
|
sendJson(res, httpStatus(code), { error: code, message });
|
|
403
394
|
}
|
|
395
|
+
function failRoute(logger, req, res, error) {
|
|
396
|
+
logger.error("An auth route failed", {
|
|
397
|
+
category: "quickdraw.auth",
|
|
398
|
+
path: pathOf(req),
|
|
399
|
+
error: errorText(error)
|
|
400
|
+
});
|
|
401
|
+
refuse(res, "INTERNAL", INTERNAL_MESSAGE);
|
|
402
|
+
}
|
|
404
403
|
function redirect(res, location) {
|
|
405
404
|
noStore(res, 302);
|
|
406
405
|
res.setHeader("location", location);
|
|
@@ -411,67 +410,8 @@ function noContent(res) {
|
|
|
411
410
|
res.end();
|
|
412
411
|
}
|
|
413
412
|
|
|
414
|
-
// src/server/auth/routes/origins.ts
|
|
415
|
-
function exactOrigin(entry, owner) {
|
|
416
|
-
let url;
|
|
417
|
-
try {
|
|
418
|
-
url = typeof entry === "string" ? new URL(entry) : void 0;
|
|
419
|
-
} catch {
|
|
420
|
-
url = void 0;
|
|
421
|
-
}
|
|
422
|
-
const bare = url !== void 0 && (url.protocol === "https:" || url.protocol === "http:") && url.username === "" && url.password === "" && url.pathname === "/" && url.search === "" && url.hash === "";
|
|
423
|
-
if (url === void 0 || !bare) {
|
|
424
|
-
throw new TypeError(
|
|
425
|
-
`${owner}: allowedOrigins entries are origins such as "https://app.example.com" or anchored patterns; got ${String(entry)}`
|
|
426
|
-
);
|
|
427
|
-
}
|
|
428
|
-
return url.origin;
|
|
429
|
-
}
|
|
430
|
-
function anchoredPattern(pattern, owner) {
|
|
431
|
-
const { source } = pattern;
|
|
432
|
-
const anchored = source.startsWith("^") && source.endsWith("$") && !source.endsWith("\\$");
|
|
433
|
-
if (!anchored || pattern.global || pattern.sticky) {
|
|
434
|
-
throw new TypeError(
|
|
435
|
-
`${owner}: an allowedOrigins pattern must match whole origins, anchored with ^ and $ and without the g or y flag; got ${String(pattern)}`
|
|
436
|
-
);
|
|
437
|
-
}
|
|
438
|
-
return new RegExp(`^(?:${source})$`, pattern.flags);
|
|
439
|
-
}
|
|
440
|
-
function originAllowlist(allowedOrigins, owner, allowEmpty = false) {
|
|
441
|
-
if (!Array.isArray(allowedOrigins) || allowedOrigins.length === 0 && !allowEmpty) {
|
|
442
|
-
throw new TypeError(`${owner}: allowedOrigins must list the web app's origins`);
|
|
443
|
-
}
|
|
444
|
-
const exact = [];
|
|
445
|
-
const patterns = [];
|
|
446
|
-
for (const entry of allowedOrigins) {
|
|
447
|
-
if (entry instanceof RegExp) {
|
|
448
|
-
patterns.push(anchoredPattern(entry, owner));
|
|
449
|
-
} else {
|
|
450
|
-
exact.push(exactOrigin(entry, owner));
|
|
451
|
-
}
|
|
452
|
-
}
|
|
453
|
-
const options = {
|
|
454
|
-
// An empty client URL, not undefined: undefined falls back to process.env.CLIENT_URL.
|
|
455
|
-
clientUrl: "",
|
|
456
|
-
extraAllowedOrigins: exact,
|
|
457
|
-
allowedPatterns: patterns,
|
|
458
|
-
allowLocalhostInDev: false
|
|
459
|
-
};
|
|
460
|
-
return Object.freeze({
|
|
461
|
-
allowed: (value) => typeof value === "string" && value !== "" ? validateRedirectOrigin(value, options) : null,
|
|
462
|
-
fallback: exact[0]
|
|
463
|
-
});
|
|
464
|
-
}
|
|
465
|
-
var ROUTE_ORIGINS = /* @__PURE__ */ new WeakMap();
|
|
466
|
-
function rememberRouteOrigins(sessions, origins) {
|
|
467
|
-
ROUTE_ORIGINS.set(sessions, origins);
|
|
468
|
-
}
|
|
469
|
-
function routeOriginsOf(sessions) {
|
|
470
|
-
return ROUTE_ORIGINS.get(sessions);
|
|
471
|
-
}
|
|
472
|
-
|
|
473
413
|
// src/server/auth/routes/state.ts
|
|
474
|
-
import { randomBytes as randomBytes2, timingSafeEqual } from "crypto";
|
|
414
|
+
import { createHmac, randomBytes as randomBytes2, timingSafeEqual } from "crypto";
|
|
475
415
|
var OAUTH_STATE_COOKIE = "qd_oauth";
|
|
476
416
|
var HOST_OAUTH_STATE_COOKIE = "__Host-qd_oauth";
|
|
477
417
|
var OAUTH_STATE_TTL_MS = 10 * 60 * 1e3;
|
|
@@ -479,16 +419,30 @@ var MAX_REDEEMED = 1e4;
|
|
|
479
419
|
function newState() {
|
|
480
420
|
return randomBytes2(32).toString("base64url");
|
|
481
421
|
}
|
|
482
|
-
function
|
|
483
|
-
return
|
|
422
|
+
function oauthStateKey(jwtSecret) {
|
|
423
|
+
return createHmac("sha256", jwtSecret).update("quickdraw:oauth-state").digest();
|
|
424
|
+
}
|
|
425
|
+
function macOf(payload, key) {
|
|
426
|
+
return createHmac("sha256", key).update(payload).digest();
|
|
427
|
+
}
|
|
428
|
+
function encodePending(pending, key) {
|
|
429
|
+
const payload = Buffer.from(JSON.stringify(pending), "utf8").toString("base64url");
|
|
430
|
+
return `${payload}.${macOf(payload, key).toString("base64url")}`;
|
|
484
431
|
}
|
|
485
|
-
function decodePending(raw) {
|
|
486
|
-
|
|
432
|
+
function decodePending(raw, key) {
|
|
433
|
+
const dot = raw === void 0 ? -1 : raw.lastIndexOf(".");
|
|
434
|
+
if (raw === void 0 || dot <= 0) {
|
|
435
|
+
return null;
|
|
436
|
+
}
|
|
437
|
+
const payload = raw.slice(0, dot);
|
|
438
|
+
const mac = Buffer.from(raw.slice(dot + 1), "base64url");
|
|
439
|
+
const expected = macOf(payload, key);
|
|
440
|
+
if (mac.length !== expected.length || !timingSafeEqual(mac, expected)) {
|
|
487
441
|
return null;
|
|
488
442
|
}
|
|
489
443
|
let value;
|
|
490
444
|
try {
|
|
491
|
-
value = JSON.parse(Buffer.from(
|
|
445
|
+
value = JSON.parse(Buffer.from(payload, "base64url").toString("utf8"));
|
|
492
446
|
} catch {
|
|
493
447
|
return null;
|
|
494
448
|
}
|
|
@@ -564,7 +518,7 @@ async function issueSession(keys, userId, meta, ttlMs = DEFAULT_SESSION_TTL_MS)
|
|
|
564
518
|
}
|
|
565
519
|
async function liveSession(keys, token) {
|
|
566
520
|
checkSessionKeys(keys, "liveSession");
|
|
567
|
-
const payload = await
|
|
521
|
+
const payload = await readVerifiedJWT(token, keys.jwtSecret);
|
|
568
522
|
if (payload === null || typeof payload.userId !== "string" || payload.userId === "" || payload.sid === void 0) {
|
|
569
523
|
return null;
|
|
570
524
|
}
|
|
@@ -641,9 +595,9 @@ function warnOnCookieDomain(cookie, logger) {
|
|
|
641
595
|
if (cookie.name !== void 0 || cookie.domain !== void 0 === transportsSeeDomain) {
|
|
642
596
|
return;
|
|
643
597
|
}
|
|
644
|
-
const why = cookie.domain === void 0 ? 'cookie.domain is "" while COOKIE_DOMAIN is set, so over HTTPS the routes set __Host-session
|
|
598
|
+
const why = cookie.domain === void 0 ? 'cookie.domain is "" while COOKIE_DOMAIN is set, so over HTTPS the routes set __Host-session, but socketAuth and the HTTP transport also take a "session" cookie there, which any site under COOKIE_DOMAIN can set for a browser that holds no __Host-session. Unset COOKIE_DOMAIN' : `cookie.domain is set and COOKIE_DOMAIN is not, so the routes set "session" on every request, which socketAuth and the HTTP transport read over HTTPS only under that name. Set COOKIE_DOMAIN to the cookie's domain instead`;
|
|
645
599
|
logger.warn(
|
|
646
|
-
`${OWNER}: ${why}
|
|
600
|
+
`${OWNER}: ${why}, or name the cookie in all three places: cookie.name, socketAuth's cookieName and createServer's http.cookieName.`,
|
|
647
601
|
{ category: "quickdraw.auth" }
|
|
648
602
|
);
|
|
649
603
|
}
|
|
@@ -704,8 +658,9 @@ function routeSettings(options) {
|
|
|
704
658
|
const cookie = cookieOf(options.cookie);
|
|
705
659
|
const logger = options.logger ?? consoleLogger;
|
|
706
660
|
const publicUrl = publicUrlOf(options.publicUrl);
|
|
661
|
+
const keys = checkSessionKeys(options, OWNER);
|
|
707
662
|
const settings = Object.freeze({
|
|
708
|
-
keys
|
|
663
|
+
keys,
|
|
709
664
|
origins: originAllowlist(options.allowedOrigins, OWNER),
|
|
710
665
|
publicUrl,
|
|
711
666
|
basePath: basePathOf(options.basePath),
|
|
@@ -716,6 +671,7 @@ function routeSettings(options) {
|
|
|
716
671
|
onRevoke: options.onRevoke,
|
|
717
672
|
logger,
|
|
718
673
|
redeemed: redeemedStates(),
|
|
674
|
+
stateKey: oauthStateKey(keys.jwtSecret),
|
|
719
675
|
watchHost: hostWatcher(publicUrl, logger)
|
|
720
676
|
});
|
|
721
677
|
warnOnCookieDomain(cookie, logger);
|
|
@@ -772,16 +728,25 @@ function setSession(res, settings, req, token, pageOrigin) {
|
|
|
772
728
|
const from = fromPage(req, pageOrigin);
|
|
773
729
|
const name = sessionCookieName(settings, from);
|
|
774
730
|
res.cookie(name, token, sessionCookieSettings(settings, from, name, true));
|
|
731
|
+
if (settings.cookie.name === void 0) {
|
|
732
|
+
clearStaleHostSession(res, req, name, settings.cookie.sameSite);
|
|
733
|
+
}
|
|
775
734
|
}
|
|
776
735
|
function clearSession(res, settings, req) {
|
|
777
736
|
const name = sessionCookieName(settings, req);
|
|
778
737
|
res.clearCookie(name, sessionCookieSettings(settings, req, name, false));
|
|
738
|
+
if (settings.cookie.name === void 0) {
|
|
739
|
+
clearStaleHostSession(res, req, name, settings.cookie.sameSite);
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
function stateSecure(settings, req) {
|
|
743
|
+
return secureFor(settings, req) || settings.publicUrl.startsWith("https:");
|
|
779
744
|
}
|
|
780
745
|
function stateCookieName(settings, req) {
|
|
781
|
-
return
|
|
746
|
+
return stateSecure(settings, req) ? HOST_OAUTH_STATE_COOKIE : OAUTH_STATE_COOKIE;
|
|
782
747
|
}
|
|
783
748
|
function stateCookieOf(settings, req, live = false) {
|
|
784
|
-
const secure =
|
|
749
|
+
const secure = stateSecure(settings, req);
|
|
785
750
|
return {
|
|
786
751
|
httpOnly: true,
|
|
787
752
|
secure,
|
|
@@ -800,6 +765,151 @@ function issueFor(settings, userId, provider, req) {
|
|
|
800
765
|
return issueSession(settings.keys, userId, meta, settings.cookie.ttlMs);
|
|
801
766
|
}
|
|
802
767
|
|
|
768
|
+
// src/server/auth/routes/socketAuth.ts
|
|
769
|
+
function handshakeOf(request) {
|
|
770
|
+
const { handshake } = request.socket;
|
|
771
|
+
return { headers: request.headers, secure: handshake?.secure === true };
|
|
772
|
+
}
|
|
773
|
+
function credentialOf(request, naming) {
|
|
774
|
+
if (request.transport === "http") {
|
|
775
|
+
const { sessionCookie } = request;
|
|
776
|
+
if (typeof sessionCookie === "string" && sessionCookie !== "") {
|
|
777
|
+
return { token: sessionCookie, checkOrigin: true };
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
const { token } = request.auth;
|
|
781
|
+
if (typeof token === "string" && token !== "") {
|
|
782
|
+
return { token, checkOrigin: request.transport === "http" && request.credential === "cookie" };
|
|
783
|
+
}
|
|
784
|
+
if (request.transport === "http") {
|
|
785
|
+
return null;
|
|
786
|
+
}
|
|
787
|
+
const names = sessionCookieNamesFor(handshakeOf(request), naming);
|
|
788
|
+
const cookie = cookieToken(cookiesOf({ headers: request.headers }), names);
|
|
789
|
+
return cookie === null ? null : { token: cookie, checkOrigin: true };
|
|
790
|
+
}
|
|
791
|
+
function originAccepted(headers, origins, allowMissing) {
|
|
792
|
+
const { origin } = headers;
|
|
793
|
+
if (origin !== void 0) {
|
|
794
|
+
return origins.allowed(origin) !== null;
|
|
795
|
+
}
|
|
796
|
+
return allowMissing || headers["sec-fetch-site"] === "same-origin";
|
|
797
|
+
}
|
|
798
|
+
function httpOriginAccepted(headers, origins) {
|
|
799
|
+
const { origin } = headers;
|
|
800
|
+
if (origin !== void 0) {
|
|
801
|
+
return origins.allowed(origin) !== null;
|
|
802
|
+
}
|
|
803
|
+
const site = headers["sec-fetch-site"];
|
|
804
|
+
return site === void 0 || site === "same-origin";
|
|
805
|
+
}
|
|
806
|
+
var compiled = /* @__PURE__ */ new WeakMap();
|
|
807
|
+
function cookieOriginAllowed(request, allowedOrigins, options = {}) {
|
|
808
|
+
let origins = compiled.get(allowedOrigins);
|
|
809
|
+
if (origins === void 0) {
|
|
810
|
+
origins = originAllowlist(allowedOrigins, "cookieOriginAllowed", true);
|
|
811
|
+
compiled.set(allowedOrigins, origins);
|
|
812
|
+
}
|
|
813
|
+
return request.transport === "socket" ? originAccepted(request.headers, origins, options.allowMissingOrigin === true) : httpOriginAccepted(request.headers, origins);
|
|
814
|
+
}
|
|
815
|
+
function httpCookieOriginAllowed(headers, origins) {
|
|
816
|
+
return httpOriginAccepted(headers, origins);
|
|
817
|
+
}
|
|
818
|
+
var COOKIE_REFUSED = "This page's origin may not use the session cookie; send it from an allowed origin or use a bearer token";
|
|
819
|
+
function checkCookieOrigin(request, origins, allowMissing) {
|
|
820
|
+
if (request.transport === "http") {
|
|
821
|
+
if (!httpOriginAccepted(request.headers, origins)) {
|
|
822
|
+
throw new QuickdrawError("FORBIDDEN", COOKIE_REFUSED);
|
|
823
|
+
}
|
|
824
|
+
return;
|
|
825
|
+
}
|
|
826
|
+
if (!originAccepted(request.headers, origins, allowMissing)) {
|
|
827
|
+
throw refused("This page's origin may not use the session cookie on a socket");
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
function refused(message) {
|
|
831
|
+
return new QuickdrawError("UNAUTHENTICATED", message);
|
|
832
|
+
}
|
|
833
|
+
function checkDevCredentials(devCredentials) {
|
|
834
|
+
if (devCredentials === void 0) {
|
|
835
|
+
return void 0;
|
|
836
|
+
}
|
|
837
|
+
if (typeof devCredentials !== "function") {
|
|
838
|
+
throw new TypeError("socketAuth: devCredentials must be a function of the user id");
|
|
839
|
+
}
|
|
840
|
+
if (!developmentSignInsAllowed()) {
|
|
841
|
+
throw new TypeError(
|
|
842
|
+
`socketAuth: devCredentials signs sockets in by a user id alone, so it runs only when NODE_ENV is development or test (NODE_ENV is ${nodeEnvLabel()}); set NODE_ENV=development to use it`
|
|
843
|
+
);
|
|
844
|
+
}
|
|
845
|
+
return devCredentials;
|
|
846
|
+
}
|
|
847
|
+
function devUserOf(request) {
|
|
848
|
+
const { token, userId } = request.auth;
|
|
849
|
+
const tokenless = token === void 0 || token === null || token === "";
|
|
850
|
+
return request.transport === "socket" && tokenless && typeof userId === "string" && userId !== "" ? userId : void 0;
|
|
851
|
+
}
|
|
852
|
+
async function devPrincipal(devCredentials, userId) {
|
|
853
|
+
if (!developmentSignInsAllowed()) {
|
|
854
|
+
throw refused("Development credentials are refused unless NODE_ENV is development or test");
|
|
855
|
+
}
|
|
856
|
+
const principal = await devCredentials(userId);
|
|
857
|
+
if (principal === null || principal === void 0) {
|
|
858
|
+
throw refused("No development user has that id");
|
|
859
|
+
}
|
|
860
|
+
if (principal.userId !== userId) {
|
|
861
|
+
throw new TypeError("socketAuth: devCredentials must return the principal of the user named");
|
|
862
|
+
}
|
|
863
|
+
return principal;
|
|
864
|
+
}
|
|
865
|
+
async function principalOfSession(session, loadPrincipal, owner) {
|
|
866
|
+
if (loadPrincipal === void 0) {
|
|
867
|
+
return { userId: session.userId, kind: "user" };
|
|
868
|
+
}
|
|
869
|
+
const principal = await loadPrincipal(session.userId, session);
|
|
870
|
+
if (principal === null || principal === void 0) {
|
|
871
|
+
throw refused("The session's user has no principal");
|
|
872
|
+
}
|
|
873
|
+
if (principal.userId !== session.userId) {
|
|
874
|
+
throw new TypeError(`${owner}: loadPrincipal must return the principal of the session's user`);
|
|
875
|
+
}
|
|
876
|
+
return principal;
|
|
877
|
+
}
|
|
878
|
+
function socketAuth(options) {
|
|
879
|
+
const keys = checkSessionKeys(options, "socketAuth");
|
|
880
|
+
const origins = originAllowlist(options.allowedOrigins, "socketAuth", true);
|
|
881
|
+
const naming = transportCookieNaming(options.cookieName);
|
|
882
|
+
const allowMissing = options.allowMissingOrigin === true;
|
|
883
|
+
const { loadPrincipal } = options;
|
|
884
|
+
if (loadPrincipal !== void 0 && typeof loadPrincipal !== "function") {
|
|
885
|
+
throw new TypeError("socketAuth: loadPrincipal must be a function");
|
|
886
|
+
}
|
|
887
|
+
const devCredentials = checkDevCredentials(options.devCredentials);
|
|
888
|
+
const authenticate = async (request) => {
|
|
889
|
+
const devUser = devCredentials === void 0 ? void 0 : devUserOf(request);
|
|
890
|
+
if (devCredentials !== void 0 && devUser !== void 0) {
|
|
891
|
+
return await devPrincipal(devCredentials, devUser);
|
|
892
|
+
}
|
|
893
|
+
const credential = credentialOf(request, naming);
|
|
894
|
+
if (credential === null) {
|
|
895
|
+
return null;
|
|
896
|
+
}
|
|
897
|
+
if (credential.checkOrigin) {
|
|
898
|
+
checkCookieOrigin(request, origins, allowMissing);
|
|
899
|
+
}
|
|
900
|
+
const session = await liveSession(keys, credential.token);
|
|
901
|
+
if (session === null) {
|
|
902
|
+
throw refused("The session is not valid");
|
|
903
|
+
}
|
|
904
|
+
if (request.transport === "socket") {
|
|
905
|
+
recordSocketSession(request.socket, session.id, session.expiresAt);
|
|
906
|
+
}
|
|
907
|
+
return await principalOfSession(session, loadPrincipal, "socketAuth");
|
|
908
|
+
};
|
|
909
|
+
markSocketAuth(authenticate, { devCredentials: devCredentials !== void 0 });
|
|
910
|
+
return authenticate;
|
|
911
|
+
}
|
|
912
|
+
|
|
803
913
|
// src/server/auth/routes/guest.ts
|
|
804
914
|
var GUEST_MAX_BODY_BYTES = 16384;
|
|
805
915
|
function guest(options) {
|
|
@@ -832,7 +942,7 @@ async function createGuest(settings, provider, req, res) {
|
|
|
832
942
|
}
|
|
833
943
|
settings.logger.error("Creating a guest user failed", {
|
|
834
944
|
category: "quickdraw.auth",
|
|
835
|
-
error:
|
|
945
|
+
error: errorText(error)
|
|
836
946
|
});
|
|
837
947
|
refuse(res, "INTERNAL", INTERNAL_MESSAGE);
|
|
838
948
|
return null;
|
|
@@ -840,6 +950,10 @@ async function createGuest(settings, provider, req, res) {
|
|
|
840
950
|
}
|
|
841
951
|
function guestRoute(settings, provider) {
|
|
842
952
|
return async (req, res) => {
|
|
953
|
+
if (!httpCookieOriginAllowed(req.headers, settings.origins)) {
|
|
954
|
+
refuse(res, "FORBIDDEN", COOKIE_REFUSED);
|
|
955
|
+
return;
|
|
956
|
+
}
|
|
843
957
|
const user = await createGuest(settings, provider, req, res);
|
|
844
958
|
if (user === null) {
|
|
845
959
|
return;
|
|
@@ -880,7 +994,7 @@ async function loadLimiters(option) {
|
|
|
880
994
|
if (option.signIn !== void 0 && option.session !== void 0 && option.providers !== void 0) {
|
|
881
995
|
return option;
|
|
882
996
|
}
|
|
883
|
-
const presets = await import("../../rateLimit-
|
|
997
|
+
const presets = await import("../../rateLimit-5RKBFLUR.js");
|
|
884
998
|
return {
|
|
885
999
|
signIn: option.signIn ?? presets.createAuthLimiter({ max: SIGN_IN_LIMIT }),
|
|
886
1000
|
session: option.session ?? presets.createAuthStatusLimiter(),
|
|
@@ -944,9 +1058,6 @@ function mockServes() {
|
|
|
944
1058
|
|
|
945
1059
|
// src/server/auth/routes/oauth.ts
|
|
946
1060
|
var CATEGORY = "quickdraw.auth";
|
|
947
|
-
function messageOf(error) {
|
|
948
|
-
return error instanceof Error ? error.message : String(error);
|
|
949
|
-
}
|
|
950
1061
|
function startRoute(settings, flow) {
|
|
951
1062
|
return (req, res) => {
|
|
952
1063
|
if (flow.enabled?.() === false) {
|
|
@@ -961,7 +1072,10 @@ function startRoute(settings, flow) {
|
|
|
961
1072
|
}
|
|
962
1073
|
const state = newState();
|
|
963
1074
|
const location = flow.authorizeUrl(state, redirectUriOf(settings, flow.id));
|
|
964
|
-
const pending = encodePending(
|
|
1075
|
+
const pending = encodePending(
|
|
1076
|
+
{ state, provider: flow.id, origin, issuedAt: Date.now() },
|
|
1077
|
+
settings.stateKey
|
|
1078
|
+
);
|
|
965
1079
|
res.cookie(stateCookieName(settings, req), pending, stateCookieOf(settings, req, true));
|
|
966
1080
|
redirect(res, location);
|
|
967
1081
|
};
|
|
@@ -974,7 +1088,7 @@ async function userFor(settings, flow, code) {
|
|
|
974
1088
|
settings.logger.warn("A sign-in's code exchange failed", {
|
|
975
1089
|
category: CATEGORY,
|
|
976
1090
|
provider: flow.id,
|
|
977
|
-
error:
|
|
1091
|
+
error: errorText(error)
|
|
978
1092
|
});
|
|
979
1093
|
return { error: "failed" };
|
|
980
1094
|
}
|
|
@@ -989,7 +1103,7 @@ async function userFor(settings, flow, code) {
|
|
|
989
1103
|
settings.logger.error("onLogin failed", {
|
|
990
1104
|
category: CATEGORY,
|
|
991
1105
|
provider: flow.id,
|
|
992
|
-
error:
|
|
1106
|
+
error: errorText(error)
|
|
993
1107
|
});
|
|
994
1108
|
return { error: "failed" };
|
|
995
1109
|
}
|
|
@@ -1012,7 +1126,7 @@ async function complete(settings, flow, req, res, origin) {
|
|
|
1012
1126
|
} catch (error) {
|
|
1013
1127
|
settings.logger.error("Creating a session failed", {
|
|
1014
1128
|
category: CATEGORY,
|
|
1015
|
-
error:
|
|
1129
|
+
error: errorText(error)
|
|
1016
1130
|
});
|
|
1017
1131
|
redirect(res, landingOf(settings, origin, "failed"));
|
|
1018
1132
|
return;
|
|
@@ -1028,7 +1142,7 @@ function callbackRoute(settings, flow) {
|
|
|
1028
1142
|
return;
|
|
1029
1143
|
}
|
|
1030
1144
|
const name = stateCookieName(settings, req);
|
|
1031
|
-
const pending = decodePending(cookiesOf(req)[name]);
|
|
1145
|
+
const pending = decodePending(cookiesOf(req)[name], settings.stateKey);
|
|
1032
1146
|
res.clearCookie(name, stateCookieOf(settings, req));
|
|
1033
1147
|
if (pending === null) {
|
|
1034
1148
|
const { fallback } = settings.origins;
|
|
@@ -1060,13 +1174,24 @@ function callbackRoute(settings, flow) {
|
|
|
1060
1174
|
|
|
1061
1175
|
// src/server/auth/routes/session.ts
|
|
1062
1176
|
var NOT_SIGNED_IN = "Not signed in";
|
|
1063
|
-
function
|
|
1064
|
-
const
|
|
1177
|
+
function tokenFor(settings, req, res) {
|
|
1178
|
+
const credential = httpCredentialOf(req, sessionCookieNaming(settings));
|
|
1179
|
+
if (credential?.from === "cookie" && !httpCookieOriginAllowed(req.headers, settings.origins)) {
|
|
1180
|
+
refuse(res, "FORBIDDEN", COOKIE_REFUSED);
|
|
1181
|
+
return void 0;
|
|
1182
|
+
}
|
|
1183
|
+
return credential?.token ?? null;
|
|
1184
|
+
}
|
|
1185
|
+
function sessionOf(settings, token) {
|
|
1065
1186
|
return token === null ? Promise.resolve(null) : liveSession(settings.keys, token);
|
|
1066
1187
|
}
|
|
1067
1188
|
function meRoute(settings) {
|
|
1068
1189
|
return async (req, res) => {
|
|
1069
|
-
const
|
|
1190
|
+
const token = tokenFor(settings, req, res);
|
|
1191
|
+
if (token === void 0) {
|
|
1192
|
+
return;
|
|
1193
|
+
}
|
|
1194
|
+
const session = await sessionOf(settings, token);
|
|
1070
1195
|
if (session === null) {
|
|
1071
1196
|
refuse(res, "UNAUTHENTICATED", NOT_SIGNED_IN);
|
|
1072
1197
|
return;
|
|
@@ -1081,14 +1206,17 @@ async function revoked(settings, userId, sessionId) {
|
|
|
1081
1206
|
settings.logger.error("onRevoke failed", {
|
|
1082
1207
|
category: "quickdraw.auth",
|
|
1083
1208
|
userId,
|
|
1084
|
-
error:
|
|
1209
|
+
error: errorText(error)
|
|
1085
1210
|
});
|
|
1086
1211
|
}
|
|
1087
1212
|
}
|
|
1088
1213
|
function logoutRoute(settings) {
|
|
1089
1214
|
return async (req, res) => {
|
|
1090
|
-
const token =
|
|
1091
|
-
|
|
1215
|
+
const token = tokenFor(settings, req, res);
|
|
1216
|
+
if (token === void 0) {
|
|
1217
|
+
return;
|
|
1218
|
+
}
|
|
1219
|
+
const payload = token === null ? null : await readVerifiedJWT(token, settings.keys.jwtSecret);
|
|
1092
1220
|
if (payload?.sid !== void 0) {
|
|
1093
1221
|
await settings.keys.sessions.revoke(payload.sid);
|
|
1094
1222
|
settings.logger.info("Signed out", { category: "quickdraw.auth", userId: payload.userId });
|
|
@@ -1100,7 +1228,11 @@ function logoutRoute(settings) {
|
|
|
1100
1228
|
}
|
|
1101
1229
|
function logoutAllRoute(settings) {
|
|
1102
1230
|
return async (req, res) => {
|
|
1103
|
-
const
|
|
1231
|
+
const token = tokenFor(settings, req, res);
|
|
1232
|
+
if (token === void 0) {
|
|
1233
|
+
return;
|
|
1234
|
+
}
|
|
1235
|
+
const session = await sessionOf(settings, token);
|
|
1104
1236
|
clearSession(res, settings, req);
|
|
1105
1237
|
if (session === null) {
|
|
1106
1238
|
refuse(res, "UNAUTHENTICATED", NOT_SIGNED_IN);
|
|
@@ -1187,7 +1319,7 @@ function routeTable(settings, listed) {
|
|
|
1187
1319
|
const mounted = providers.filter((provider) => addProvider(table, settings, provider));
|
|
1188
1320
|
if (mounted.length === 0) {
|
|
1189
1321
|
settings.logger.warn(
|
|
1190
|
-
"createAuthRoutes: no provider can sign anyone in (the mock is mounted only while ENABLE_MOCK_OAUTH is set
|
|
1322
|
+
"createAuthRoutes: no provider can sign anyone in (the mock is mounted only while ENABLE_MOCK_OAUTH is set and NODE_ENV is development or test): every sign-in route answers 404",
|
|
1191
1323
|
{ category: "quickdraw.auth" }
|
|
1192
1324
|
);
|
|
1193
1325
|
}
|
|
@@ -1216,12 +1348,7 @@ async function serve(route, req, res) {
|
|
|
1216
1348
|
}
|
|
1217
1349
|
function dispatch(settings, limiters, route, req, res) {
|
|
1218
1350
|
const fail = (error) => {
|
|
1219
|
-
settings.logger
|
|
1220
|
-
category: "quickdraw.auth",
|
|
1221
|
-
path: pathOf(req),
|
|
1222
|
-
error: error instanceof Error ? error.message : String(error)
|
|
1223
|
-
});
|
|
1224
|
-
refuse(res, "INTERNAL", INTERNAL_MESSAGE);
|
|
1351
|
+
failRoute(settings.logger, req, res, error);
|
|
1225
1352
|
};
|
|
1226
1353
|
if (limiters === null) {
|
|
1227
1354
|
refuse(res, "INTERNAL", "The auth routes' rate limiters are unavailable");
|
|
@@ -1270,151 +1397,28 @@ function createAuthRoutes(options) {
|
|
|
1270
1397
|
return Object.assign(routes, { providers: () => servedProviders(mounted) });
|
|
1271
1398
|
}
|
|
1272
1399
|
|
|
1273
|
-
// src/server/auth/routes/
|
|
1274
|
-
function
|
|
1275
|
-
const
|
|
1276
|
-
return { headers: request.headers, secure: handshake?.secure === true };
|
|
1277
|
-
}
|
|
1278
|
-
function credentialOf(request, naming) {
|
|
1279
|
-
const { token } = request.auth;
|
|
1280
|
-
if (typeof token === "string" && token !== "") {
|
|
1281
|
-
return { token, checkOrigin: request.transport === "http" && request.credential === "cookie" };
|
|
1282
|
-
}
|
|
1283
|
-
if (request.transport === "http") {
|
|
1284
|
-
return null;
|
|
1285
|
-
}
|
|
1286
|
-
const names = sessionCookieNamesFor(handshakeOf(request), naming);
|
|
1287
|
-
const cookie = cookieToken(cookiesOf({ headers: request.headers }), names);
|
|
1288
|
-
return cookie === null ? null : { token: cookie, checkOrigin: true };
|
|
1289
|
-
}
|
|
1290
|
-
function originAccepted(headers, origins, allowMissing) {
|
|
1291
|
-
const { origin } = headers;
|
|
1292
|
-
if (origin !== void 0) {
|
|
1293
|
-
return origins.allowed(origin) !== null;
|
|
1294
|
-
}
|
|
1295
|
-
return allowMissing || headers["sec-fetch-site"] === "same-origin";
|
|
1296
|
-
}
|
|
1297
|
-
function httpOriginAccepted(headers, origins) {
|
|
1298
|
-
const { origin } = headers;
|
|
1299
|
-
if (origin !== void 0) {
|
|
1300
|
-
return origins.allowed(origin) !== null;
|
|
1301
|
-
}
|
|
1302
|
-
const site = headers["sec-fetch-site"];
|
|
1303
|
-
return site === void 0 || site === "same-origin";
|
|
1304
|
-
}
|
|
1305
|
-
var compiled = /* @__PURE__ */ new WeakMap();
|
|
1306
|
-
function cookieOriginAllowed(request, allowedOrigins, options = {}) {
|
|
1307
|
-
let origins = compiled.get(allowedOrigins);
|
|
1308
|
-
if (origins === void 0) {
|
|
1309
|
-
origins = originAllowlist(allowedOrigins, "cookieOriginAllowed", true);
|
|
1310
|
-
compiled.set(allowedOrigins, origins);
|
|
1311
|
-
}
|
|
1312
|
-
return request.transport === "socket" ? originAccepted(request.headers, origins, options.allowMissingOrigin === true) : httpOriginAccepted(request.headers, origins);
|
|
1313
|
-
}
|
|
1314
|
-
function httpCookieOriginAllowed(headers, origins) {
|
|
1315
|
-
return httpOriginAccepted(headers, origins);
|
|
1316
|
-
}
|
|
1317
|
-
function checkCookieOrigin(request, origins, allowMissing) {
|
|
1318
|
-
if (request.transport === "http") {
|
|
1319
|
-
if (!httpOriginAccepted(request.headers, origins)) {
|
|
1320
|
-
throw new QuickdrawError(
|
|
1321
|
-
"FORBIDDEN",
|
|
1322
|
-
"This page's origin may not use the session cookie; send it from an allowed origin or use a bearer token"
|
|
1323
|
-
);
|
|
1324
|
-
}
|
|
1325
|
-
return;
|
|
1326
|
-
}
|
|
1327
|
-
if (!originAccepted(request.headers, origins, allowMissing)) {
|
|
1328
|
-
throw refused("This page's origin may not use the session cookie on a socket");
|
|
1329
|
-
}
|
|
1330
|
-
}
|
|
1331
|
-
function refused(message) {
|
|
1332
|
-
return new QuickdrawError("UNAUTHENTICATED", message);
|
|
1333
|
-
}
|
|
1334
|
-
function production() {
|
|
1335
|
-
return process.env.NODE_ENV === "production";
|
|
1336
|
-
}
|
|
1337
|
-
function checkDevCredentials(devCredentials) {
|
|
1338
|
-
if (devCredentials === void 0) {
|
|
1339
|
-
return void 0;
|
|
1340
|
-
}
|
|
1341
|
-
if (typeof devCredentials !== "function") {
|
|
1342
|
-
throw new TypeError("socketAuth: devCredentials must be a function of the user id");
|
|
1343
|
-
}
|
|
1344
|
-
if (production()) {
|
|
1345
|
-
throw new TypeError(
|
|
1346
|
-
"socketAuth: devCredentials signs sockets in by a user id alone, so it cannot be used when NODE_ENV is production"
|
|
1347
|
-
);
|
|
1348
|
-
}
|
|
1349
|
-
return devCredentials;
|
|
1350
|
-
}
|
|
1351
|
-
function devUserOf(request) {
|
|
1352
|
-
const { token, userId } = request.auth;
|
|
1353
|
-
const tokenless = token === void 0 || token === null || token === "";
|
|
1354
|
-
return request.transport === "socket" && tokenless && typeof userId === "string" && userId !== "" ? userId : void 0;
|
|
1355
|
-
}
|
|
1356
|
-
async function devPrincipal(devCredentials, userId) {
|
|
1357
|
-
if (production()) {
|
|
1358
|
-
throw refused("Development credentials are refused in production");
|
|
1359
|
-
}
|
|
1360
|
-
const principal = await devCredentials(userId);
|
|
1361
|
-
if (principal === null || principal === void 0) {
|
|
1362
|
-
throw refused("No development user has that id");
|
|
1363
|
-
}
|
|
1364
|
-
if (principal.userId !== userId) {
|
|
1365
|
-
throw new TypeError("socketAuth: devCredentials must return the principal of the user named");
|
|
1366
|
-
}
|
|
1367
|
-
return principal;
|
|
1368
|
-
}
|
|
1369
|
-
function socketAuth(options) {
|
|
1370
|
-
const keys = checkSessionKeys(options, "socketAuth");
|
|
1371
|
-
const origins = originAllowlist(options.allowedOrigins, "socketAuth", true);
|
|
1372
|
-
const naming = transportCookieNaming(options.cookieName);
|
|
1373
|
-
const allowMissing = options.allowMissingOrigin === true;
|
|
1400
|
+
// src/server/auth/routes/bearerAuth.ts
|
|
1401
|
+
function bearerAuth(options) {
|
|
1402
|
+
const keys = checkSessionKeys(options, "bearerAuth");
|
|
1374
1403
|
const { loadPrincipal } = options;
|
|
1375
1404
|
if (loadPrincipal !== void 0 && typeof loadPrincipal !== "function") {
|
|
1376
|
-
throw new TypeError("
|
|
1405
|
+
throw new TypeError("bearerAuth: loadPrincipal must be a function");
|
|
1377
1406
|
}
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
const devUser = devCredentials === void 0 ? void 0 : devUserOf(request);
|
|
1381
|
-
if (devCredentials !== void 0 && devUser !== void 0) {
|
|
1382
|
-
return await devPrincipal(devCredentials, devUser);
|
|
1383
|
-
}
|
|
1384
|
-
const credential = credentialOf(request, naming);
|
|
1385
|
-
if (credential === null) {
|
|
1407
|
+
return async (token) => {
|
|
1408
|
+
if (typeof token !== "string" || token === "") {
|
|
1386
1409
|
return null;
|
|
1387
1410
|
}
|
|
1388
|
-
|
|
1389
|
-
checkCookieOrigin(request, origins, allowMissing);
|
|
1390
|
-
}
|
|
1391
|
-
const session = await liveSession(keys, credential.token);
|
|
1411
|
+
const session = await liveSession(keys, token);
|
|
1392
1412
|
if (session === null) {
|
|
1393
|
-
throw
|
|
1394
|
-
}
|
|
1395
|
-
if (request.transport === "socket") {
|
|
1396
|
-
recordSocketSession(request.socket, session.id);
|
|
1397
|
-
}
|
|
1398
|
-
if (loadPrincipal === void 0) {
|
|
1399
|
-
return { userId: session.userId, kind: "user" };
|
|
1413
|
+
throw new QuickdrawError("UNAUTHENTICATED", "The session is not valid");
|
|
1400
1414
|
}
|
|
1401
|
-
|
|
1402
|
-
if (principal === null || principal === void 0) {
|
|
1403
|
-
throw refused("The session's user has no principal");
|
|
1404
|
-
}
|
|
1405
|
-
if (principal.userId !== session.userId) {
|
|
1406
|
-
throw new TypeError(
|
|
1407
|
-
"socketAuth: loadPrincipal must return the principal of the session's user"
|
|
1408
|
-
);
|
|
1409
|
-
}
|
|
1410
|
-
return principal;
|
|
1415
|
+
return await principalOfSession(session, loadPrincipal, "bearerAuth");
|
|
1411
1416
|
};
|
|
1412
1417
|
}
|
|
1413
1418
|
|
|
1414
1419
|
// src/server/auth/routes/requireSession.ts
|
|
1415
1420
|
var SESSIONS = /* @__PURE__ */ new WeakMap();
|
|
1416
1421
|
var NO_ORIGINS = originAllowlist([], "requireSession", true);
|
|
1417
|
-
var COOKIE_REFUSED = "This page's origin may not use the session cookie; send it from an allowed origin or use a bearer token";
|
|
1418
1422
|
var NO_LIST = "requireSession has no origin list, so no page may use the session cookie: pass it the sessions object createAuthRoutes was given (the same store object: a second store over the same table is another), or pass allowedOrigins";
|
|
1419
1423
|
function requireSession(keys, options = {}) {
|
|
1420
1424
|
checkSessionKeys(keys, "requireSession");
|
|
@@ -1625,6 +1629,7 @@ export {
|
|
|
1625
1629
|
OAUTH_STATE_COOKIE,
|
|
1626
1630
|
OAUTH_STATE_TTL_MS,
|
|
1627
1631
|
SESSION_COOKIE,
|
|
1632
|
+
bearerAuth,
|
|
1628
1633
|
clearSessionCookie,
|
|
1629
1634
|
cookieOriginAllowed,
|
|
1630
1635
|
createAuthRoutes,
|