@cedarjs/auth-dbauth-api 9.0.0-canary.1784 → 9.0.0-canary.3124
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/dist/DbAuthHandler.d.ts +37 -43
- package/dist/DbAuthHandler.d.ts.map +1 -1
- package/dist/DbAuthHandler.js +171 -162
- package/dist/decoder.js +5 -30
- package/dist/errors.d.ts +2 -2
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +7 -61
- package/dist/index.d.ts +6 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -36
- package/dist/originValidation.d.ts +131 -0
- package/dist/originValidation.d.ts.map +1 -0
- package/dist/originValidation.js +92 -0
- package/dist/session.d.ts +114 -0
- package/dist/session.d.ts.map +1 -0
- package/dist/session.js +103 -0
- package/dist/shared.d.ts.map +1 -1
- package/dist/shared.js +21 -68
- package/package.json +25 -16
- package/LICENSE +0 -21
package/dist/errors.js
CHANGED
|
@@ -1,56 +1,3 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __defProp = Object.defineProperty;
|
|
3
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
-
var __export = (target, all) => {
|
|
7
|
-
for (var name in all)
|
|
8
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
-
};
|
|
10
|
-
var __copyProps = (to, from, except, desc) => {
|
|
11
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
-
for (let key of __getOwnPropNames(from))
|
|
13
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
-
}
|
|
16
|
-
return to;
|
|
17
|
-
};
|
|
18
|
-
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
-
var errors_exports = {};
|
|
20
|
-
__export(errors_exports, {
|
|
21
|
-
CsrfTokenMismatchError: () => CsrfTokenMismatchError,
|
|
22
|
-
DuplicateUsernameError: () => DuplicateUsernameError,
|
|
23
|
-
FieldRequiredError: () => FieldRequiredError,
|
|
24
|
-
FlowNotEnabledError: () => FlowNotEnabledError,
|
|
25
|
-
GenericError: () => GenericError,
|
|
26
|
-
IncorrectPasswordError: () => IncorrectPasswordError,
|
|
27
|
-
MissingWebAuthnConfigError: () => MissingWebAuthnConfigError,
|
|
28
|
-
NoForgotPasswordHandlerError: () => NoForgotPasswordHandlerError,
|
|
29
|
-
NoLoginHandlerError: () => NoLoginHandlerError,
|
|
30
|
-
NoResetPasswordHandlerError: () => NoResetPasswordHandlerError,
|
|
31
|
-
NoSessionExpirationError: () => NoSessionExpirationError,
|
|
32
|
-
NoSessionSecretError: () => NoSessionSecretError,
|
|
33
|
-
NoSignupHandlerError: () => NoSignupHandlerError,
|
|
34
|
-
NoUserIdError: () => NoUserIdError,
|
|
35
|
-
NoWebAuthnConfigError: () => NoWebAuthnConfigError,
|
|
36
|
-
NoWebAuthnSessionError: () => NoWebAuthnSessionError,
|
|
37
|
-
NotLoggedInError: () => NotLoggedInError,
|
|
38
|
-
PasswordRequiredError: () => PasswordRequiredError,
|
|
39
|
-
PasswordValidationError: () => PasswordValidationError,
|
|
40
|
-
ResetTokenExpiredError: () => ResetTokenExpiredError,
|
|
41
|
-
ResetTokenInvalidError: () => ResetTokenInvalidError,
|
|
42
|
-
ResetTokenRequiredError: () => ResetTokenRequiredError,
|
|
43
|
-
ReusedPasswordError: () => ReusedPasswordError,
|
|
44
|
-
SessionDecryptionError: () => SessionDecryptionError,
|
|
45
|
-
UnknownAuthMethodError: () => UnknownAuthMethodError,
|
|
46
|
-
UserNotFoundError: () => UserNotFoundError,
|
|
47
|
-
UsernameAndPasswordRequiredError: () => UsernameAndPasswordRequiredError,
|
|
48
|
-
UsernameNotFoundError: () => UsernameNotFoundError,
|
|
49
|
-
UsernameRequiredError: () => UsernameRequiredError,
|
|
50
|
-
WebAuthnError: () => WebAuthnError,
|
|
51
|
-
WrongVerbError: () => WrongVerbError
|
|
52
|
-
});
|
|
53
|
-
module.exports = __toCommonJS(errors_exports);
|
|
54
1
|
class NoSessionSecretError extends Error {
|
|
55
2
|
constructor() {
|
|
56
3
|
super(
|
|
@@ -165,10 +112,10 @@ class IncorrectPasswordError extends Error {
|
|
|
165
112
|
this.name = "IncorrectPasswordError";
|
|
166
113
|
}
|
|
167
114
|
}
|
|
168
|
-
class
|
|
169
|
-
constructor() {
|
|
170
|
-
super(
|
|
171
|
-
this.name = "
|
|
115
|
+
class UntrustedOriginError extends Error {
|
|
116
|
+
constructor(message = "Request origin is not trusted") {
|
|
117
|
+
super(message);
|
|
118
|
+
this.name = "UntrustedOriginError";
|
|
172
119
|
}
|
|
173
120
|
}
|
|
174
121
|
class SessionDecryptionError extends Error {
|
|
@@ -249,9 +196,7 @@ class NoWebAuthnSessionError extends WebAuthnError {
|
|
|
249
196
|
this.name = "NoWebAuthnSessionError";
|
|
250
197
|
}
|
|
251
198
|
}
|
|
252
|
-
|
|
253
|
-
0 && (module.exports = {
|
|
254
|
-
CsrfTokenMismatchError,
|
|
199
|
+
export {
|
|
255
200
|
DuplicateUsernameError,
|
|
256
201
|
FieldRequiredError,
|
|
257
202
|
FlowNotEnabledError,
|
|
@@ -276,10 +221,11 @@ class NoWebAuthnSessionError extends WebAuthnError {
|
|
|
276
221
|
ReusedPasswordError,
|
|
277
222
|
SessionDecryptionError,
|
|
278
223
|
UnknownAuthMethodError,
|
|
224
|
+
UntrustedOriginError,
|
|
279
225
|
UserNotFoundError,
|
|
280
226
|
UsernameAndPasswordRequiredError,
|
|
281
227
|
UsernameNotFoundError,
|
|
282
228
|
UsernameRequiredError,
|
|
283
229
|
WebAuthnError,
|
|
284
230
|
WrongVerbError
|
|
285
|
-
}
|
|
231
|
+
};
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
export * from './DbAuthHandler';
|
|
2
|
-
export { PasswordValidationError } from './errors';
|
|
3
|
-
export * from './
|
|
4
|
-
export
|
|
1
|
+
export * from './DbAuthHandler.js';
|
|
2
|
+
export { PasswordValidationError } from './errors.js';
|
|
3
|
+
export * from './session.js';
|
|
4
|
+
export * from './shared.js';
|
|
5
|
+
export { authDecoder, createAuthDecoder } from './decoder.js';
|
|
6
|
+
export * from './originValidation.js';
|
|
5
7
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,oBAAoB,CAAA;AAClC,OAAO,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAA;AACrD,cAAc,cAAc,CAAA;AAC5B,cAAc,aAAa,CAAA;AAC3B,OAAO,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAA;AAC7D,cAAc,uBAAuB,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -1,38 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
-
};
|
|
10
|
-
var __copyProps = (to, from, except, desc) => {
|
|
11
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
-
for (let key of __getOwnPropNames(from))
|
|
13
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
-
}
|
|
16
|
-
return to;
|
|
17
|
-
};
|
|
18
|
-
var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget && __copyProps(secondTarget, mod, "default"));
|
|
19
|
-
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
20
|
-
var src_exports = {};
|
|
21
|
-
__export(src_exports, {
|
|
22
|
-
PasswordValidationError: () => import_errors.PasswordValidationError,
|
|
23
|
-
authDecoder: () => import_decoder.authDecoder,
|
|
24
|
-
createAuthDecoder: () => import_decoder.createAuthDecoder
|
|
25
|
-
});
|
|
26
|
-
module.exports = __toCommonJS(src_exports);
|
|
27
|
-
__reExport(src_exports, require("./DbAuthHandler"), module.exports);
|
|
28
|
-
var import_errors = require("./errors");
|
|
29
|
-
__reExport(src_exports, require("./shared"), module.exports);
|
|
30
|
-
var import_decoder = require("./decoder");
|
|
31
|
-
// Annotate the CommonJS export names for ESM import in node:
|
|
32
|
-
0 && (module.exports = {
|
|
1
|
+
export * from "./DbAuthHandler.js";
|
|
2
|
+
import { PasswordValidationError } from "./errors.js";
|
|
3
|
+
export * from "./session.js";
|
|
4
|
+
export * from "./shared.js";
|
|
5
|
+
import { authDecoder, createAuthDecoder } from "./decoder.js";
|
|
6
|
+
export * from "./originValidation.js";
|
|
7
|
+
export {
|
|
33
8
|
PasswordValidationError,
|
|
34
9
|
authDecoder,
|
|
35
|
-
createAuthDecoder
|
|
36
|
-
|
|
37
|
-
...require("./shared")
|
|
38
|
-
});
|
|
10
|
+
createAuthDecoder
|
|
11
|
+
};
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import type { CorsConfig } from '@cedarjs/api';
|
|
2
|
+
/**
|
|
3
|
+
* Minimal, transport-agnostic shape of an incoming request needed to decide
|
|
4
|
+
* whether its `Origin` header should be trusted for a state-changing
|
|
5
|
+
* request. Deliberately decoupled from any specific request/event type (no
|
|
6
|
+
* `DbAuthHandler` coupling) so this helper can be reused by other handler
|
|
7
|
+
* packages.
|
|
8
|
+
*/
|
|
9
|
+
export interface OriginValidationRequest {
|
|
10
|
+
/**
|
|
11
|
+
* HTTP method of the request, e.g. `GET`, `POST`. Used by
|
|
12
|
+
* {@link requiresOriginValidation} to decide whether a request needs to be
|
|
13
|
+
* checked at all.
|
|
14
|
+
*/
|
|
15
|
+
method: string;
|
|
16
|
+
/**
|
|
17
|
+
* Request headers, including `origin` if the browser sent one.
|
|
18
|
+
*/
|
|
19
|
+
headers: Headers;
|
|
20
|
+
/**
|
|
21
|
+
* Host (and port, if non-default) the request was sent to, resolved with
|
|
22
|
+
* {@link resolveRequestHost}. Used to allow same-origin requests through
|
|
23
|
+
* with no extra configuration, which covers the standard Cedar setup
|
|
24
|
+
* where the web side proxies API requests through its own origin. Pass
|
|
25
|
+
* `null` when the host can't be determined.
|
|
26
|
+
*/
|
|
27
|
+
host?: string | null;
|
|
28
|
+
/**
|
|
29
|
+
* Scheme (`http`/`https`) the request is considered to have arrived
|
|
30
|
+
* over, resolved with {@link resolveRequestProtocol}, when it can be
|
|
31
|
+
* determined reliably. Used as an extra check alongside `host` when
|
|
32
|
+
* deciding whether the `Origin` header matches the request's own
|
|
33
|
+
* origin. Pass `null`/`undefined` when it can't be determined -- in that
|
|
34
|
+
* case only the host is compared, unless `proxied` narrows that further
|
|
35
|
+
* (see {@link isProxiedRequest}).
|
|
36
|
+
*/
|
|
37
|
+
protocol?: string | null;
|
|
38
|
+
/**
|
|
39
|
+
* Whether the request declares itself as arriving through a forwarding
|
|
40
|
+
* proxy, resolved with {@link isProxiedRequest}. Scopes the
|
|
41
|
+
* unknown-scheme same-host restriction to requests a proxy actually
|
|
42
|
+
* touched -- see `isSameOrigin`.
|
|
43
|
+
*/
|
|
44
|
+
proxied?: boolean;
|
|
45
|
+
}
|
|
46
|
+
export interface OriginValidationConfig {
|
|
47
|
+
/**
|
|
48
|
+
* Extra origins that are always trusted for state-changing requests, on
|
|
49
|
+
* top of the request's own host and any origins already listed in
|
|
50
|
+
* `cors.origin`.
|
|
51
|
+
*/
|
|
52
|
+
trustedOrigins?: string | string[];
|
|
53
|
+
/**
|
|
54
|
+
* The `origin` value from a handler's `cors` config (same shape as passed
|
|
55
|
+
* to `createCorsContext`). String and array values are treated as
|
|
56
|
+
* trusted origins.
|
|
57
|
+
*
|
|
58
|
+
* `true` (reflect any request origin back in
|
|
59
|
+
* `Access-Control-Allow-Origin`) is intentionally NOT treated as trust --
|
|
60
|
+
* combined with `credentials: true` that is the exact misconfiguration
|
|
61
|
+
* this check protects against. Apps that need to accept requests from an
|
|
62
|
+
* arbitrary set of origins must list them explicitly in `trustedOrigins`
|
|
63
|
+
* instead.
|
|
64
|
+
*/
|
|
65
|
+
corsOrigin?: CorsConfig['origin'];
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* HTTP methods that can change server-side state and therefore need their
|
|
69
|
+
* `Origin` validated. `GET` and `HEAD` requests are read-only and are never
|
|
70
|
+
* checked.
|
|
71
|
+
*/
|
|
72
|
+
export declare function requiresOriginValidation(method: string): boolean;
|
|
73
|
+
/**
|
|
74
|
+
* Resolves the host a request should be considered as having arrived at,
|
|
75
|
+
* preferring a forwarding proxy's advertised host over the connection-level
|
|
76
|
+
* one.
|
|
77
|
+
*
|
|
78
|
+
* A proxy in front of the api side (this framework's own `cedar serve
|
|
79
|
+
* --ud`, or an external nginx/CDN) terminates the browser's connection and
|
|
80
|
+
* opens a new one to the api server, so the request's own URL/`Host` header
|
|
81
|
+
* reflects that internal hop rather than the origin-facing host the
|
|
82
|
+
* browser actually targeted. `X-Forwarded-Host` carries the origin-facing
|
|
83
|
+
* host instead.
|
|
84
|
+
*
|
|
85
|
+
* Trusting `X-Forwarded-Host` here is safe for CSRF purposes: a cross-site
|
|
86
|
+
* HTML form can't set arbitrary request headers, and a cross-origin
|
|
87
|
+
* `fetch()` that sets a custom header triggers a CORS preflight, so an
|
|
88
|
+
* attacker can't forge it without already having crossed the browser's
|
|
89
|
+
* CORS checks.
|
|
90
|
+
*
|
|
91
|
+
* @param url - The request's own URL (Fetch API requests only), used as a
|
|
92
|
+
* fallback when neither `X-Forwarded-Host` nor `Host` is present.
|
|
93
|
+
*/
|
|
94
|
+
export declare function resolveRequestHost(headers: Headers, url?: string | null): string | null;
|
|
95
|
+
/**
|
|
96
|
+
* Resolves the scheme (`http`/`https`) a request should be considered as
|
|
97
|
+
* having arrived over, mirroring {@link resolveRequestHost}.
|
|
98
|
+
*
|
|
99
|
+
* The request's own URL is only used as a fallback when no
|
|
100
|
+
* `X-Forwarded-Host` is present -- once a proxy has declared itself by
|
|
101
|
+
* setting that header, the request's own URL reflects the internal hop to
|
|
102
|
+
* the proxy, and its scheme can't be assumed to match what the browser
|
|
103
|
+
* actually used unless the proxy also set `X-Forwarded-Proto`.
|
|
104
|
+
*
|
|
105
|
+
* @param url - The request's own URL (Fetch API requests only).
|
|
106
|
+
*/
|
|
107
|
+
export declare function resolveRequestProtocol(headers: Headers, url?: string | null): string | null;
|
|
108
|
+
/**
|
|
109
|
+
* Whether a request declares itself as arriving through a forwarding
|
|
110
|
+
* proxy, i.e. carries an `X-Forwarded-Host` header.
|
|
111
|
+
*
|
|
112
|
+
* Used to scope the unknown-scheme same-host restriction (see
|
|
113
|
+
* `isSameOrigin`) to requests a proxy actually touched. A request with no
|
|
114
|
+
* forwarding headers at all is a direct connection -- the normal case for
|
|
115
|
+
* dev servers, classic `cedar serve`, and most self-hosts -- not evidence
|
|
116
|
+
* of a proxy.
|
|
117
|
+
*/
|
|
118
|
+
export declare function isProxiedRequest(headers: Headers): boolean;
|
|
119
|
+
/**
|
|
120
|
+
* Decides whether a request's `Origin` header should be trusted.
|
|
121
|
+
*
|
|
122
|
+
* Requests with no `Origin` header are always trusted: non-browser clients
|
|
123
|
+
* (server-to-server calls, CLIs, mobile apps) don't send one and don't carry
|
|
124
|
+
* ambient browser cookies, so they aren't CSRF vectors.
|
|
125
|
+
*
|
|
126
|
+
* When an `Origin` header is present, it's trusted when it matches the
|
|
127
|
+
* request's own host, is listed in `trustedOrigins`, or is listed in
|
|
128
|
+
* `corsOrigin`. Everything else is untrusted.
|
|
129
|
+
*/
|
|
130
|
+
export declare function isRequestOriginTrusted(request: OriginValidationRequest, config: OriginValidationConfig): boolean;
|
|
131
|
+
//# sourceMappingURL=originValidation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"originValidation.d.ts","sourceRoot":"","sources":["../src/originValidation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAE9C;;;;;;GAMG;AACH,MAAM,WAAW,uBAAuB;IACtC;;;;OAIG;IACH,MAAM,EAAE,MAAM,CAAA;IACd;;OAEG;IACH,OAAO,EAAE,OAAO,CAAA;IAChB;;;;;;OAMG;IACH,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB;;;;;;;;OAQG;IACH,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB;;;;;OAKG;IACH,OAAO,CAAC,EAAE,OAAO,CAAA;CAClB;AAED,MAAM,WAAW,sBAAsB;IACrC;;;;OAIG;IACH,cAAc,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAA;IAClC;;;;;;;;;;;OAWG;IACH,UAAU,CAAC,EAAE,UAAU,CAAC,QAAQ,CAAC,CAAA;CAClC;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAGhE;AAgBD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,OAAO,EAChB,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,GAClB,MAAM,GAAG,IAAI,CAoBf;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,OAAO,EAChB,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,GAClB,MAAM,GAAG,IAAI,CAef;AAED;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAE1D;AA+CD;;;;;;;;;;GAUG;AACH,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,uBAAuB,EAChC,MAAM,EAAE,sBAAsB,GAC7B,OAAO,CA6BT"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
function requiresOriginValidation(method) {
|
|
2
|
+
const upperMethod = method.toUpperCase();
|
|
3
|
+
return upperMethod !== "GET" && upperMethod !== "HEAD";
|
|
4
|
+
}
|
|
5
|
+
function toOriginList(value) {
|
|
6
|
+
if (!value) {
|
|
7
|
+
return [];
|
|
8
|
+
}
|
|
9
|
+
return Array.isArray(value) ? value : [value];
|
|
10
|
+
}
|
|
11
|
+
function firstForwardedValue(headerValue) {
|
|
12
|
+
return headerValue.split(",")[0].trim();
|
|
13
|
+
}
|
|
14
|
+
function resolveRequestHost(headers, url) {
|
|
15
|
+
const forwardedHost = headers.get("x-forwarded-host");
|
|
16
|
+
if (forwardedHost) {
|
|
17
|
+
return firstForwardedValue(forwardedHost);
|
|
18
|
+
}
|
|
19
|
+
const host = headers.get("host");
|
|
20
|
+
if (host) {
|
|
21
|
+
return host;
|
|
22
|
+
}
|
|
23
|
+
if (url) {
|
|
24
|
+
try {
|
|
25
|
+
return new URL(url).host;
|
|
26
|
+
} catch {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
function resolveRequestProtocol(headers, url) {
|
|
33
|
+
const forwardedProto = headers.get("x-forwarded-proto");
|
|
34
|
+
if (forwardedProto) {
|
|
35
|
+
return firstForwardedValue(forwardedProto).toLowerCase();
|
|
36
|
+
}
|
|
37
|
+
if (!headers.get("x-forwarded-host") && url) {
|
|
38
|
+
try {
|
|
39
|
+
return new URL(url).protocol.replace(":", "");
|
|
40
|
+
} catch {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
function isProxiedRequest(headers) {
|
|
47
|
+
return headers.get("x-forwarded-host") !== null;
|
|
48
|
+
}
|
|
49
|
+
function isSameOrigin(origin, host, protocol, proxied) {
|
|
50
|
+
try {
|
|
51
|
+
const originUrl = new URL(origin);
|
|
52
|
+
if (originUrl.host !== host) {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
if (protocol) {
|
|
56
|
+
return originUrl.protocol === `${protocol}:`;
|
|
57
|
+
}
|
|
58
|
+
if (proxied) {
|
|
59
|
+
return originUrl.protocol === "https:";
|
|
60
|
+
}
|
|
61
|
+
return true;
|
|
62
|
+
} catch {
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
function isRequestOriginTrusted(request, config) {
|
|
67
|
+
const origin = request.headers.get("origin");
|
|
68
|
+
if (!origin) {
|
|
69
|
+
return true;
|
|
70
|
+
}
|
|
71
|
+
if (request.host && isSameOrigin(origin, request.host, request.protocol, request.proxied)) {
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
const trustedOrigins = toOriginList(config.trustedOrigins);
|
|
75
|
+
if (trustedOrigins.includes(origin)) {
|
|
76
|
+
return true;
|
|
77
|
+
}
|
|
78
|
+
const corsOrigins = toOriginList(
|
|
79
|
+
typeof config.corsOrigin === "string" || Array.isArray(config.corsOrigin) ? config.corsOrigin : void 0
|
|
80
|
+
);
|
|
81
|
+
if (corsOrigins.includes(origin)) {
|
|
82
|
+
return true;
|
|
83
|
+
}
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
export {
|
|
87
|
+
isProxiedRequest,
|
|
88
|
+
isRequestOriginTrusted,
|
|
89
|
+
requiresOriginValidation,
|
|
90
|
+
resolveRequestHost,
|
|
91
|
+
resolveRequestProtocol
|
|
92
|
+
};
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cookie attributes that can be set on a dbAuth cookie, either as
|
|
3
|
+
* `cookie.attributes` or (deprecated) directly on `cookie`.
|
|
4
|
+
*/
|
|
5
|
+
export interface DbAuthCookieAttributes {
|
|
6
|
+
Path?: string;
|
|
7
|
+
HttpOnly?: boolean;
|
|
8
|
+
Secure?: boolean;
|
|
9
|
+
SameSite?: string;
|
|
10
|
+
Domain?: string;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Configuration for the cookies dbAuth sets: the session cookie's name, and
|
|
14
|
+
* the attributes applied to every cookie dbAuth writes (the session cookie
|
|
15
|
+
* and the `auth-provider` cookie).
|
|
16
|
+
*/
|
|
17
|
+
export interface DbAuthCookieConfig extends DbAuthCookieAttributes {
|
|
18
|
+
attributes?: DbAuthCookieAttributes;
|
|
19
|
+
/**
|
|
20
|
+
* The name of the cookie that dbAuth sets
|
|
21
|
+
*
|
|
22
|
+
* %port% will be replaced with the port the api server is running on.
|
|
23
|
+
* If you have multiple RW apps running on the same host, you'll need to
|
|
24
|
+
* make sure they all use unique cookie names
|
|
25
|
+
*/
|
|
26
|
+
name?: string;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Returns a UTC date string `expiresInSeconds` seconds in the future. Used as
|
|
30
|
+
* the `Expires` cookie attribute for a session that should stay valid for
|
|
31
|
+
* that long from now.
|
|
32
|
+
*/
|
|
33
|
+
export declare function createExpiresAtDate(expiresInSeconds: number): string;
|
|
34
|
+
/**
|
|
35
|
+
* Generates a new CSRF token (a standard UUID) to pair with a session.
|
|
36
|
+
*/
|
|
37
|
+
export declare function createCsrfToken(): string;
|
|
38
|
+
/**
|
|
39
|
+
* Removes any fields not explicitly allowed to be sent to the client before
|
|
40
|
+
* sending a user record over the wire.
|
|
41
|
+
*/
|
|
42
|
+
export declare function sanitizeUser(user: Record<string, unknown>, allowedUserFields: string[]): Record<string, unknown>;
|
|
43
|
+
/**
|
|
44
|
+
* Builds the Set-Cookie attribute strings (`Path`, `HttpOnly`, `SameSite`,
|
|
45
|
+
* `Secure`, `Domain`, `Expires`) for a dbAuth cookie.
|
|
46
|
+
*
|
|
47
|
+
* Pass `expires: 'now'` (the default) to get the attributes needed to expire
|
|
48
|
+
* the cookie, or a UTC date string (e.g. from `createExpiresAtDate`) to set a
|
|
49
|
+
* future expiration.
|
|
50
|
+
*/
|
|
51
|
+
export declare function buildCookieAttributes({ cookieConfig, expires, overrideAttributes, }: {
|
|
52
|
+
cookieConfig?: DbAuthCookieConfig;
|
|
53
|
+
expires?: 'now' | string;
|
|
54
|
+
overrideAttributes?: DbAuthCookieAttributes;
|
|
55
|
+
}): string[];
|
|
56
|
+
/**
|
|
57
|
+
* Builds the Set-Cookie header string for the `auth-provider` cookie dbAuth
|
|
58
|
+
* sets alongside the session cookie.
|
|
59
|
+
*/
|
|
60
|
+
export declare function createAuthProviderCookieString({ cookieConfig, expiresAt, }: {
|
|
61
|
+
cookieConfig?: DbAuthCookieConfig;
|
|
62
|
+
expiresAt: string;
|
|
63
|
+
}): string;
|
|
64
|
+
/**
|
|
65
|
+
* Builds the Set-Cookie header string for the dbAuth session cookie: the
|
|
66
|
+
* `data;csrfToken` payload, encrypted, under the configured cookie name and
|
|
67
|
+
* attributes.
|
|
68
|
+
*/
|
|
69
|
+
export declare function createSessionCookieString<TIdType = unknown>({ data, csrfToken, cookieConfig, expiresAt, }: {
|
|
70
|
+
data: Record<string, TIdType>;
|
|
71
|
+
csrfToken: string;
|
|
72
|
+
cookieConfig?: DbAuthCookieConfig;
|
|
73
|
+
expiresAt: string;
|
|
74
|
+
}): string;
|
|
75
|
+
/** Fields returned from the user record when none are explicitly allowed */
|
|
76
|
+
export declare const DEFAULT_ALLOWED_USER_FIELDS: string[];
|
|
77
|
+
export interface CreateLoginResponseOptions {
|
|
78
|
+
/**
|
|
79
|
+
* Object containing cookie config options: the cookie name (with %port%
|
|
80
|
+
* support) and the attributes applied to it (`Path`, `HttpOnly`,
|
|
81
|
+
* `SameSite`, `Secure`, `Domain`).
|
|
82
|
+
*/
|
|
83
|
+
cookie?: DbAuthCookieConfig;
|
|
84
|
+
/**
|
|
85
|
+
* The fields that are allowed to be returned from the user record.
|
|
86
|
+
* Defaults to `id` and `email`.
|
|
87
|
+
*/
|
|
88
|
+
allowedUserFields?: string[];
|
|
89
|
+
/**
|
|
90
|
+
* The `Expires` cookie attribute value for the session and `auth-provider`
|
|
91
|
+
* cookies: a UTC date string in the future. Build one with
|
|
92
|
+
* `createExpiresAtDate(expiresInSeconds)`.
|
|
93
|
+
*/
|
|
94
|
+
expiresAt: string;
|
|
95
|
+
/**
|
|
96
|
+
* The HTTP status code to return in the response tuple. Defaults to 200.
|
|
97
|
+
*/
|
|
98
|
+
statusCode?: number;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Mints a dbAuth session for the given user: a CSRF token, a session cookie
|
|
102
|
+
* (the sanitized user data paired with the CSRF token, encrypted), and the
|
|
103
|
+
* `auth-provider` cookie, in the same `[body, headers, { statusCode }]` shape
|
|
104
|
+
* every dbAuth auth method returns.
|
|
105
|
+
*
|
|
106
|
+
* Callable without a `DbAuthHandler` instance, so any code that has already
|
|
107
|
+
* verified a user's identity — a WebAuthn assertion, an OAuth callback, a
|
|
108
|
+
* magic link — can issue a session the same way the built-in login flow
|
|
109
|
+
* does.
|
|
110
|
+
*/
|
|
111
|
+
export declare function createLoginResponse(user: Record<string, unknown>, options: CreateLoginResponseOptions): [Record<string, unknown>, Headers, {
|
|
112
|
+
statusCode: number;
|
|
113
|
+
}];
|
|
114
|
+
//# sourceMappingURL=session.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"AAIA;;;GAGG;AACH,MAAM,WAAW,sBAAsB;IACrC,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAED;;;;GAIG;AACH,MAAM,WAAW,kBAAmB,SAAQ,sBAAsB;IAChE,UAAU,CAAC,EAAE,sBAAsB,CAAA;IACnC;;;;;;OAMG;IACH,IAAI,CAAC,EAAE,MAAM,CAAA;CACd;AAOD;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,gBAAgB,EAAE,MAAM,GAAG,MAAM,CAKpE;AAED;;GAEG;AACH,wBAAgB,eAAe,IAAI,MAAM,CAExC;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAC1B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,iBAAiB,EAAE,MAAM,EAAE,GAC1B,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAUzB;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,EACpC,YAAY,EACZ,OAAe,EACf,kBAAuB,GACxB,EAAE;IACD,YAAY,CAAC,EAAE,kBAAkB,CAAA;IACjC,OAAO,CAAC,EAAE,KAAK,GAAG,MAAM,CAAA;IACxB,kBAAkB,CAAC,EAAE,sBAAsB,CAAA;CAC5C,GAAG,MAAM,EAAE,CAiCX;AAED;;;GAGG;AACH,wBAAgB,8BAA8B,CAAC,EAC7C,YAAY,EACZ,SAAS,GACV,EAAE;IACD,YAAY,CAAC,EAAE,kBAAkB,CAAA;IACjC,SAAS,EAAE,MAAM,CAAA;CAClB,GAAG,MAAM,CAKT;AAED;;;;GAIG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,GAAG,OAAO,EAAE,EAC3D,IAAI,EACJ,SAAS,EACT,YAAY,EACZ,SAAS,GACV,EAAE;IACD,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC7B,SAAS,EAAE,MAAM,CAAA;IACjB,YAAY,CAAC,EAAE,kBAAkB,CAAA;IACjC,SAAS,EAAE,MAAM,CAAA;CAClB,GAAG,MAAM,CAQT;AAED,4EAA4E;AAC5E,eAAO,MAAM,2BAA2B,UAAkB,CAAA;AAE1D,MAAM,WAAW,0BAA0B;IACzC;;;;OAIG;IACH,MAAM,CAAC,EAAE,kBAAkB,CAAA;IAC3B;;;OAGG;IACH,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAA;IAC5B;;;;OAIG;IACH,SAAS,EAAE,MAAM,CAAA;IACjB;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,OAAO,EAAE,0BAA0B,GAClC,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,EAAE;IAAE,UAAU,EAAE,MAAM,CAAA;CAAE,CAAC,CA+B5D"}
|
package/dist/session.js
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { v4 as uuidv4 } from "uuid";
|
|
2
|
+
import { encryptSession, generateCookieName } from "./shared.js";
|
|
3
|
+
const PAST_EXPIRES_DATE = (/* @__PURE__ */ new Date(
|
|
4
|
+
"1970-01-01T00:00:00.000+00:00"
|
|
5
|
+
)).toUTCString();
|
|
6
|
+
function createExpiresAtDate(expiresInSeconds) {
|
|
7
|
+
const expiresAt = /* @__PURE__ */ new Date();
|
|
8
|
+
expiresAt.setSeconds(expiresAt.getSeconds() + expiresInSeconds);
|
|
9
|
+
return expiresAt.toUTCString();
|
|
10
|
+
}
|
|
11
|
+
function createCsrfToken() {
|
|
12
|
+
return uuidv4();
|
|
13
|
+
}
|
|
14
|
+
function sanitizeUser(user, allowedUserFields) {
|
|
15
|
+
const sanitized = JSON.parse(JSON.stringify(user));
|
|
16
|
+
Object.keys(sanitized).forEach((key) => {
|
|
17
|
+
if (!allowedUserFields.includes(key)) {
|
|
18
|
+
delete sanitized[key];
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
return sanitized;
|
|
22
|
+
}
|
|
23
|
+
function buildCookieAttributes({
|
|
24
|
+
cookieConfig,
|
|
25
|
+
expires = "now",
|
|
26
|
+
overrideAttributes = {}
|
|
27
|
+
}) {
|
|
28
|
+
const userCookieAttributes = cookieConfig?.attributes ? { ...cookieConfig.attributes } : { ...cookieConfig };
|
|
29
|
+
if (!cookieConfig?.attributes) {
|
|
30
|
+
delete userCookieAttributes.name;
|
|
31
|
+
}
|
|
32
|
+
const cookieOptions = { ...userCookieAttributes, ...overrideAttributes };
|
|
33
|
+
const meta = Object.keys(cookieOptions).map((key) => {
|
|
34
|
+
const optionValue = cookieOptions[key];
|
|
35
|
+
if (optionValue === true) {
|
|
36
|
+
return key;
|
|
37
|
+
} else if (optionValue === false) {
|
|
38
|
+
return null;
|
|
39
|
+
} else {
|
|
40
|
+
return `${key}=${optionValue}`;
|
|
41
|
+
}
|
|
42
|
+
}).filter((v) => v !== null);
|
|
43
|
+
const expiresAt = expires === "now" ? PAST_EXPIRES_DATE : expires;
|
|
44
|
+
meta.push(`Expires=${expiresAt}`);
|
|
45
|
+
return meta;
|
|
46
|
+
}
|
|
47
|
+
function createAuthProviderCookieString({
|
|
48
|
+
cookieConfig,
|
|
49
|
+
expiresAt
|
|
50
|
+
}) {
|
|
51
|
+
return [
|
|
52
|
+
`auth-provider=dbAuth`,
|
|
53
|
+
...buildCookieAttributes({ cookieConfig, expires: expiresAt })
|
|
54
|
+
].join(";");
|
|
55
|
+
}
|
|
56
|
+
function createSessionCookieString({
|
|
57
|
+
data,
|
|
58
|
+
csrfToken,
|
|
59
|
+
cookieConfig,
|
|
60
|
+
expiresAt
|
|
61
|
+
}) {
|
|
62
|
+
const session = JSON.stringify(data) + ";" + csrfToken;
|
|
63
|
+
const encrypted = encryptSession(session);
|
|
64
|
+
return [
|
|
65
|
+
`${generateCookieName(cookieConfig?.name)}=${encrypted}`,
|
|
66
|
+
...buildCookieAttributes({ cookieConfig, expires: expiresAt })
|
|
67
|
+
].join(";");
|
|
68
|
+
}
|
|
69
|
+
const DEFAULT_ALLOWED_USER_FIELDS = ["id", "email"];
|
|
70
|
+
function createLoginResponse(user, options) {
|
|
71
|
+
const allowedUserFields = options.allowedUserFields ?? DEFAULT_ALLOWED_USER_FIELDS;
|
|
72
|
+
const sessionData = sanitizeUser(user, allowedUserFields);
|
|
73
|
+
const csrfToken = createCsrfToken();
|
|
74
|
+
const headers = new Headers();
|
|
75
|
+
headers.append("csrf-token", csrfToken);
|
|
76
|
+
headers.append(
|
|
77
|
+
"set-cookie",
|
|
78
|
+
createAuthProviderCookieString({
|
|
79
|
+
cookieConfig: options.cookie,
|
|
80
|
+
expiresAt: options.expiresAt
|
|
81
|
+
})
|
|
82
|
+
);
|
|
83
|
+
headers.append(
|
|
84
|
+
"set-cookie",
|
|
85
|
+
createSessionCookieString({
|
|
86
|
+
data: sessionData,
|
|
87
|
+
csrfToken,
|
|
88
|
+
cookieConfig: options.cookie,
|
|
89
|
+
expiresAt: options.expiresAt
|
|
90
|
+
})
|
|
91
|
+
);
|
|
92
|
+
return [sessionData, headers, { statusCode: options.statusCode ?? 200 }];
|
|
93
|
+
}
|
|
94
|
+
export {
|
|
95
|
+
DEFAULT_ALLOWED_USER_FIELDS,
|
|
96
|
+
buildCookieAttributes,
|
|
97
|
+
createAuthProviderCookieString,
|
|
98
|
+
createCsrfToken,
|
|
99
|
+
createExpiresAtDate,
|
|
100
|
+
createLoginResponse,
|
|
101
|
+
createSessionCookieString,
|
|
102
|
+
sanitizeUser
|
|
103
|
+
};
|
package/dist/shared.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../src/shared.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAA;AAEtD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAM/C,KAAK,aAAa,GAAG;IACnB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,CAAC,CAAC,EAAE,MAAM,CAAA;IACV,CAAC,CAAC,EAAE,MAAM,CAAA;IACV,CAAC,CAAC,EAAE,MAAM,CAAA;IACV,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB,CAAA;
|
|
1
|
+
{"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../src/shared.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAA;AAEtD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAM/C,KAAK,aAAa,GAAG;IACnB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,CAAC,CAAC,EAAE,MAAM,CAAA;IACV,CAAC,CAAC,EAAE,MAAM,CAAA;IACV,CAAC,CAAC,EAAE,MAAM,CAAA;IACV,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB,CAAA;AA6ED,eAAO,MAAM,aAAa,GAAI,OAAO,oBAAoB,GAAG,OAAO,QAElE,CAAA;AAGD,eAAO,MAAM,eAAe,GAAI,MAAM,MAAM,GAAG,SAAS,YAOvD,CAAA;AAGD,eAAO,MAAM,cAAc,GAAI,MAAM,MAAM,GAAG,IAAI,UAiCjD,CAAA;AAED,eAAO,MAAM,cAAc,GAAI,YAAY,MAAM,WAWhD,CAAA;AAGD,eAAO,MAAM,UAAU,GACrB,MAAM,MAAM,GAAG,SAAS,EACxB,oBAAoB,MAAM,GAAG,SAAS,kBAkBvC,CAAA;AAKD,eAAO,MAAM,aAAa,GACxB,OAAO,oBAAoB,GAAG,OAAO,EACrC,oBAAoB,MAAM,GAAG,SAAS,QAavC,CAAA;AAED,eAAO,MAAM,eAAe,GAAI,OAAO,oBAAoB,GAAG,OAAO,QAgBpE,CAAA;AAED,eAAO,MAAM,SAAS,GAAI,OAAO,MAAM,WAEtC,CAAA;AAMD,eAAO,MAAM,YAAY,GACvB,MAAM,MAAM,EACZ,qBAGG;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,aAAa,CAAA;CAAO,aAWnD,CAAA;AAID,eAAO,MAAM,kBAAkB,GAAI,MAAM,MAAM,EAAE,OAAO,MAAM,aAM7D,CAAA;AAED,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,UAK9D;AAED;;;;;;;GAOG;AACH,wBAAgB,wBAAwB,CACtC,KAAK,EAAE,oBAAoB,GAAG,OAAO,IAGnC,UAAU;IACR,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,UAAU,EAAE,MAAM,CAAA;IAClB,OAAO,CAAC,EAAE,OAAO,CAAA;CAClB,EACD,aAAa,WAAW;gBAQV,MAAM;aACT,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;wBACtB,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC;WACrC,MAAM;EA2BlB;AAYD,eAAO,MAAM,qBAAqB,GAAI,MAAM,MAAM,KAAG,aAYpD,CAAA"}
|