@jxsuite/auth 0.4.1 → 0.5.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/package.json +6 -6
- package/src/config.ts +92 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jxsuite/auth",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Better Auth sessions, sign-in flows, and table permissions for Jx sites over /_jx/auth",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -38,13 +38,13 @@
|
|
|
38
38
|
"upgrade": "bunx npm-check-updates -u && bun install"
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"@jxsuite/connector": "^0.
|
|
42
|
-
"@jxsuite/schema": "^1.
|
|
43
|
-
"better-auth": "^1.
|
|
44
|
-
"kysely": "^0.29.
|
|
41
|
+
"@jxsuite/connector": "^0.5.0",
|
|
42
|
+
"@jxsuite/schema": "^1.6.0",
|
|
43
|
+
"better-auth": "^1.7.1",
|
|
44
|
+
"kysely": "^0.29.5"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
47
|
-
"@happy-dom/global-registrator": "^20.11.
|
|
47
|
+
"@happy-dom/global-registrator": "^20.11.2",
|
|
48
48
|
"ajv": "^8.20.0"
|
|
49
49
|
},
|
|
50
50
|
"jx": "./jx-extension.json"
|
package/src/config.ts
CHANGED
|
@@ -16,6 +16,87 @@ export const AUTH_BASE_PATH = "/_jx/auth";
|
|
|
16
16
|
/** Default NAME of the env var holding the Better Auth signing secret. */
|
|
17
17
|
export const DEFAULT_SECRET_ENV = "BETTER_AUTH_SECRET";
|
|
18
18
|
|
|
19
|
+
/**
|
|
20
|
+
* How long a session lives, and how often activity extends it (seconds).
|
|
21
|
+
*
|
|
22
|
+
* These are Better Auth's own defaults, restated so the lifetime is **this project's** decision and
|
|
23
|
+
* shows up in a diff when it changes. A session lifetime that lives only in a dependency's default
|
|
24
|
+
* is one nobody chose.
|
|
25
|
+
*/
|
|
26
|
+
export const SESSION_EXPIRES_SECONDS = 60 * 60 * 24 * 7;
|
|
27
|
+
export const SESSION_UPDATE_SECONDS = 60 * 60 * 24;
|
|
28
|
+
|
|
29
|
+
/** Auth-route rate limit: requests per IP per window (seconds). */
|
|
30
|
+
export const RATE_LIMIT_MAX = 100;
|
|
31
|
+
export const RATE_LIMIT_WINDOW_SECONDS = 10;
|
|
32
|
+
|
|
33
|
+
/** Better Auth's own cookie-name prefix. Changing it signs every existing session out. */
|
|
34
|
+
const COOKIE_PREFIX = "better-auth";
|
|
35
|
+
|
|
36
|
+
/** The cookies `createCookieGetter` mints, all of them same-host and `Path=/`. */
|
|
37
|
+
const COOKIE_IDS = ["session_token", "session_data", "dont_remember", "account_data"];
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Whether this deployment's cookies must carry `Secure` — and therefore a name prefix.
|
|
41
|
+
*
|
|
42
|
+
* **Default secure, and only step down when the origin is positively known to be plain HTTP.**
|
|
43
|
+
* Better Auth's own rule falls back to `NODE_ENV === "production"`, which is _false on Cloudflare
|
|
44
|
+
* Workers_ — where `NODE_ENV` is simply unset — so the library's default silently produced
|
|
45
|
+
* unprefixed, non-`Secure` session cookies on exactly the platform Jx deploys to. The one host that
|
|
46
|
+
* genuinely serves auth over plain HTTP is the local dev server, and it pins `BETTER_AUTH_URL` to
|
|
47
|
+
* its own origin so this reads `http:` there rather than guessing.
|
|
48
|
+
*
|
|
49
|
+
* @param {Env} env
|
|
50
|
+
* @returns {boolean}
|
|
51
|
+
*/
|
|
52
|
+
export function cookiesAreSecure(env: Env): boolean {
|
|
53
|
+
const baseURL = env["BETTER_AUTH_URL"];
|
|
54
|
+
if (typeof baseURL === "string" && baseURL !== "") {
|
|
55
|
+
return !baseURL.startsWith("http://");
|
|
56
|
+
}
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* The `advanced` block: cookie names and attributes (RFC 6265bis §4.1.3).
|
|
62
|
+
*
|
|
63
|
+
* **`__Host-`, not `__Secure-`.** `__Secure-` only promises the cookie was set with `Secure`; a
|
|
64
|
+
* page on a sibling origin — `evil.example.com` against `example.com` — can still overwrite it by
|
|
65
|
+
* setting a `Domain`. `__Host-` forbids `Domain`, pins `Path=/`, and is therefore the prefix that
|
|
66
|
+
* actually stops a session-fixation write. Better Auth's defaults already set `Path=/` and no
|
|
67
|
+
* `Domain`, so the stronger prefix costs nothing.
|
|
68
|
+
*
|
|
69
|
+
* Getting there means turning **off** `useSecureCookies` and putting `Secure` back by hand: the
|
|
70
|
+
* library prepends `__Secure-` to whatever name it is given, so leaving it on would mint
|
|
71
|
+
* `__Secure-__Host-…`, a name no browser accepts. `secure: true` in `defaultCookieAttributes`
|
|
72
|
+
* restores the attribute the flag would have set. `tests/config.test.ts` asserts the resulting
|
|
73
|
+
* names, so a library upgrade that changes this is loud rather than silent.
|
|
74
|
+
*
|
|
75
|
+
* **`Partitioned` is not set, and must not default on.** CHIPS is for cookies in a _third-party_
|
|
76
|
+
* context; Jx auth cookies are first-party to the site that serves them. Setting it would force
|
|
77
|
+
* `SameSite=None; Secure` and partition the session per top-level site — signing a visitor out
|
|
78
|
+
* whenever the embedding page changed.
|
|
79
|
+
*
|
|
80
|
+
* @param {boolean} secure
|
|
81
|
+
* @returns {Record<string, unknown>}
|
|
82
|
+
*/
|
|
83
|
+
function advancedCookieOptions(secure: boolean): Record<string, unknown> {
|
|
84
|
+
if (!secure) {
|
|
85
|
+
// Plain-HTTP dev origin: a prefixed cookie without `Secure` is rejected outright.
|
|
86
|
+
return { cookiePrefix: COOKIE_PREFIX, useSecureCookies: false };
|
|
87
|
+
}
|
|
88
|
+
const cookies: Record<string, { name: string }> = {};
|
|
89
|
+
for (const id of COOKIE_IDS) {
|
|
90
|
+
cookies[id] = { name: `__Host-${COOKIE_PREFIX}.${id}` };
|
|
91
|
+
}
|
|
92
|
+
return {
|
|
93
|
+
cookiePrefix: COOKIE_PREFIX,
|
|
94
|
+
cookies,
|
|
95
|
+
defaultCookieAttributes: { secure: true },
|
|
96
|
+
useSecureCookies: false,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
|
|
19
100
|
/** One social provider entry: env-var NAMES for the OAuth credentials, never values. */
|
|
20
101
|
export interface AuthProviderDef {
|
|
21
102
|
clientIdEnv?: string;
|
|
@@ -119,8 +200,19 @@ export function buildAuthOptions(
|
|
|
119
200
|
// Without BETTER_AUTH_URL Better Auth derives the origin from each incoming request, which
|
|
120
201
|
// Is correct for same-origin /_jx/auth mounts; setting it pins callbacks for OAuth flows.
|
|
121
202
|
...(typeof baseURL === "string" && baseURL !== "" ? { baseURL } : {}),
|
|
203
|
+
advanced: advancedCookieOptions(cookiesAreSecure(env)),
|
|
122
204
|
basePath: AUTH_BASE_PATH,
|
|
123
205
|
emailAndPassword: { enabled: section.methods?.emailPassword ?? true },
|
|
206
|
+
/*
|
|
207
|
+
* Rate limiting is ON everywhere, not just in production. Better Auth gates its own default on
|
|
208
|
+
* `NODE_ENV === "production"`, which is unset on Workers — so the limit was off in the one
|
|
209
|
+
* place it matters. It is also on in dev deliberately: 100 requests per 10 seconds is far
|
|
210
|
+
* beyond anything a person does by hand, and a limit that is disabled while you develop is a
|
|
211
|
+
* limit nobody ever finds out is broken. Storage is Better Auth's in-memory default, which on
|
|
212
|
+
* a serverless runtime is per-isolate — a speed bump for credential stuffing, not a wall.
|
|
213
|
+
*/
|
|
214
|
+
rateLimit: { enabled: true, max: RATE_LIMIT_MAX, window: RATE_LIMIT_WINDOW_SECONDS },
|
|
215
|
+
session: { expiresIn: SESSION_EXPIRES_SECONDS, updateAge: SESSION_UPDATE_SECONDS },
|
|
124
216
|
...(Object.keys(socialProviders).length > 0 ? { socialProviders } : {}),
|
|
125
217
|
...(roles.length > 0
|
|
126
218
|
? {
|