@ingram-tech/nk-auth 0.17.1 → 0.18.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 +21 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/server.d.ts +3 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +8 -1
- package/dist/server.js.map +1 -1
- package/dist/session-lifetime.d.ts +18 -0
- package/dist/session-lifetime.d.ts.map +1 -0
- package/dist/session-lifetime.js +39 -0
- package/dist/session-lifetime.js.map +1 -0
- package/package.json +3 -3
- package/src/index.ts +1 -0
- package/src/server.ts +12 -2
- package/src/session-lifetime.ts +46 -0
package/README.md
CHANGED
|
@@ -15,6 +15,7 @@ exactly one Better Auth copy in the app.
|
|
|
15
15
|
| `nkOrganizationDefaults`, `lastActiveOrganizationHooks`, `lastActiveOrganizationUserField` (`./organization`) | org-plugin defaults + active-org restore/persist |
|
|
16
16
|
| `createAuthPool` (`./pool`) | **deprecated** — alias of `createPool` from [`@ingram-tech/nk-db`](../nk-db); inject your app's shared pool instead |
|
|
17
17
|
| `makeEmailSenders`, `makePasskeyOptions`, `passkeyOptionsForBaseUrl`, `uuidGenerateId` (`./`) | email hooks, passkeys (`passkeyOptionsForBaseUrl` derives `rpID`/`origin` from a single base URL), UUID ids |
|
|
18
|
+
| `sessionLifetime` (`./`) | Better Auth plugin capping a sliding session at `createdAt + maxAge`; see [Gate routes](#5-gate-routes-without-the-redirect-loop), "Session lifetime" |
|
|
18
19
|
| `bcryptPassword` (`./`) | **legacy only** — bcrypt verifier for apps with pre-existing bcrypt hashes. New apps omit it (Better Auth defaults to scrypt). See [Migrating bcrypt passwords to scrypt](#migrating-bcrypt-passwords-to-scrypt) |
|
|
19
20
|
| `createAuthHelpers`, `safeNext` (`./server`) | validated App Router session helpers (`getSession` / `getUser` / `requireSession` / `requireUser` / `signInTarget` / `redirectIfAuthenticated`), request-memoized via React `cache()`, with automatic `next` + stale-cookie signalling; `safeNext` validates a `?next=` param |
|
|
20
21
|
| `createAuthMiddleware`, `withAuthPathHeader`, `clearStaleSession` (`./middleware`) | loop-safe edge middleware: gates unauthenticated users off protected paths, preserves `next`, and clears a stale session cookie so a bad session self-heals; the last two are its `next`-header and stale-cookie halves, for a site with its own proxy |
|
|
@@ -429,6 +430,26 @@ marker and bounces to a clean `/login?next=…`; signing in returns them to
|
|
|
429
430
|
the cookie-present case the guard reads it from the `x-nk-auth-path` header
|
|
430
431
|
middleware injects.
|
|
431
432
|
|
|
433
|
+
**Session lifetime.** Better Auth refreshes a session on read (at most once
|
|
434
|
+
per `session.updateAge`), moving the row's `expiresAt` and re-issuing the cookie
|
|
435
|
+
with a fresh max-age. A server component cannot set cookies, so the server
|
|
436
|
+
helpers read with `disableRefresh`: refreshing there would extend the row while
|
|
437
|
+
the cookie still dies on its sign-in max-age, and users get signed out
|
|
438
|
+
`expiresIn` after sign-in however active they are. The refresh belongs to the
|
|
439
|
+
browser: mount `authClient.useSession()` in the signed-in shell (it hits
|
|
440
|
+
`/auth/get-session` on load and on tab focus, and that route handler can set the
|
|
441
|
+
cookie). To still end a sign-in eventually, add `sessionLifetime({ maxAge })`
|
|
442
|
+
to the instance's `plugins`: refreshes are clamped to `createdAt + maxAge`.
|
|
443
|
+
|
|
444
|
+
```tsx
|
|
445
|
+
// app/(app)/SessionRenewal.tsx
|
|
446
|
+
"use client";
|
|
447
|
+
export const SessionRenewal = () => {
|
|
448
|
+
authClient.useSession();
|
|
449
|
+
return null;
|
|
450
|
+
};
|
|
451
|
+
```
|
|
452
|
+
|
|
432
453
|
**Your own proxy.** An app that composes its own proxy (locale routing, tenant
|
|
433
454
|
pinning, …) keeps validated gating from the server helpers, but the guards can
|
|
434
455
|
only preserve `next` if something sets that header. Both halves of the
|
package/dist/index.d.ts
CHANGED
|
@@ -7,4 +7,5 @@ export { lastActiveOrganizationHooks, lastActiveOrganizationUserField, nkOrganiz
|
|
|
7
7
|
export { CREDENTIAL_PROVIDER_ID, DEFAULT_MAX_PASSWORD_LENGTH, DEFAULT_MIN_PASSWORD_LENGTH, type PasswordPolicy, passwordSchema, type ResetPasswordError, type ResetPasswordErrorCode, validateNewPassword, } from "./password.js";
|
|
8
8
|
export { authBasePath } from "./paths.js";
|
|
9
9
|
export { createAuthPool } from "./pool.js";
|
|
10
|
+
export { sessionLifetime } from "./session-lifetime.js";
|
|
10
11
|
//# 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":"AAKA,OAAO,EACN,sBAAsB,EACtB,oBAAoB,EACpB,qBAAqB,EACrB,KAAK,qBAAqB,EAC1B,wBAAwB,GACxB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,KAAK,gBAAgB,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AACtF,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AACjF,OAAO,EAAE,KAAK,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAC5E,OAAO,EACN,KAAK,aAAa,EAClB,KAAK,gBAAgB,EACrB,KAAK,aAAa,EAClB,cAAc,EACd,gBAAgB,EAChB,kBAAkB,EAClB,KAAK,aAAa,EAClB,wBAAwB,EACxB,KAAK,SAAS,GACd,MAAM,cAAc,CAAC;AACtB,OAAO,EACN,2BAA2B,EAC3B,+BAA+B,EAC/B,sBAAsB,GACtB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACN,sBAAsB,EACtB,2BAA2B,EAC3B,2BAA2B,EAC3B,KAAK,cAAc,EACnB,cAAc,EACd,KAAK,kBAAkB,EACvB,KAAK,sBAAsB,EAC3B,mBAAmB,GACnB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAKA,OAAO,EACN,sBAAsB,EACtB,oBAAoB,EACpB,qBAAqB,EACrB,KAAK,qBAAqB,EAC1B,wBAAwB,GACxB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,KAAK,gBAAgB,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AACtF,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AACjF,OAAO,EAAE,KAAK,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAC5E,OAAO,EACN,KAAK,aAAa,EAClB,KAAK,gBAAgB,EACrB,KAAK,aAAa,EAClB,cAAc,EACd,gBAAgB,EAChB,kBAAkB,EAClB,KAAK,aAAa,EAClB,wBAAwB,EACxB,KAAK,SAAS,GACd,MAAM,cAAc,CAAC;AACtB,OAAO,EACN,2BAA2B,EAC3B,+BAA+B,EAC/B,sBAAsB,GACtB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACN,sBAAsB,EACtB,2BAA2B,EAC3B,2BAA2B,EAC3B,KAAK,cAAc,EACnB,cAAc,EACd,KAAK,kBAAkB,EACvB,KAAK,sBAAsB,EAC3B,mBAAmB,GACnB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -11,4 +11,5 @@ export { lastActiveOrganizationHooks, lastActiveOrganizationUserField, nkOrganiz
|
|
|
11
11
|
export { CREDENTIAL_PROVIDER_ID, DEFAULT_MAX_PASSWORD_LENGTH, DEFAULT_MIN_PASSWORD_LENGTH, passwordSchema, validateNewPassword, } from "./password.js";
|
|
12
12
|
export { authBasePath } from "./paths.js";
|
|
13
13
|
export { createAuthPool } from "./pool.js";
|
|
14
|
+
export { sessionLifetime } from "./session-lifetime.js";
|
|
14
15
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,+EAA+E;AAC/E,4EAA4E;AAC5E,wEAAwE;AAExE,OAAO,EACN,sBAAsB,EACtB,oBAAoB,EACpB,qBAAqB,EAErB,wBAAwB,GACxB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAyB,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AACtF,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AACjF,OAAO,EAAgB,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAC5E,OAAO,EAIN,cAAc,EACd,gBAAgB,EAChB,kBAAkB,EAElB,wBAAwB,GAExB,MAAM,cAAc,CAAC;AACtB,OAAO,EACN,2BAA2B,EAC3B,+BAA+B,EAC/B,sBAAsB,GACtB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACN,sBAAsB,EACtB,2BAA2B,EAC3B,2BAA2B,EAE3B,cAAc,EAGd,mBAAmB,GACnB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,+EAA+E;AAC/E,4EAA4E;AAC5E,wEAAwE;AAExE,OAAO,EACN,sBAAsB,EACtB,oBAAoB,EACpB,qBAAqB,EAErB,wBAAwB,GACxB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAyB,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AACtF,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AACjF,OAAO,EAAgB,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAC5E,OAAO,EAIN,cAAc,EACd,gBAAgB,EAChB,kBAAkB,EAElB,wBAAwB,GAExB,MAAM,cAAc,CAAC;AACtB,OAAO,EACN,2BAA2B,EAC3B,+BAA+B,EAC/B,sBAAsB,GACtB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACN,sBAAsB,EACtB,2BAA2B,EAC3B,2BAA2B,EAE3B,cAAc,EAGd,mBAAmB,GACnB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC"}
|
package/dist/server.d.ts
CHANGED
|
@@ -54,6 +54,9 @@ interface AuthLike<S extends SessionLike> {
|
|
|
54
54
|
api: {
|
|
55
55
|
getSession: (input: {
|
|
56
56
|
headers: Headers;
|
|
57
|
+
query?: {
|
|
58
|
+
disableRefresh?: boolean;
|
|
59
|
+
};
|
|
57
60
|
}) => Promise<S | null>;
|
|
58
61
|
/**
|
|
59
62
|
* Better Auth's `/list-accounts`: the auth methods linked to the current
|
package/dist/server.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,OAAO,EAAE,KAAK,qBAAqB,EAAkB,MAAM,YAAY,CAAC;AACxE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAItD,OAAO,EAEN,KAAK,gBAAgB,EAErB,MAAM,uBAAuB,CAAC;AAG/B;;;;GAIG;AACH,OAAO,EAAE,aAAa,IAAI,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAClE,YAAY,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAE9D,oEAAoE;AACpE,UAAU,WAAW;IACpB,IAAI,EAAE,OAAO,CAAC;CACd;AAED;;;;GAIG;AACH,UAAU,QAAQ,CAAC,CAAC,SAAS,WAAW;IACvC;;;OAGG;IACH,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;IACjC,GAAG,EAAE;QACJ,UAAU,EAAE,CAAC,KAAK,EAAE;
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,OAAO,EAAE,KAAK,qBAAqB,EAAkB,MAAM,YAAY,CAAC;AACxE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAItD,OAAO,EAEN,KAAK,gBAAgB,EAErB,MAAM,uBAAuB,CAAC;AAG/B;;;;GAIG;AACH,OAAO,EAAE,aAAa,IAAI,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAClE,YAAY,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAE9D,oEAAoE;AACpE,UAAU,WAAW;IACpB,IAAI,EAAE,OAAO,CAAC;CACd;AAED;;;;GAIG;AACH,UAAU,QAAQ,CAAC,CAAC,SAAS,WAAW;IACvC;;;OAGG;IACH,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;IACjC,GAAG,EAAE;QACJ,UAAU,EAAE,CAAC,KAAK,EAAE;YACnB,OAAO,EAAE,OAAO,CAAC;YACjB,KAAK,CAAC,EAAE;gBAAE,cAAc,CAAC,EAAE,OAAO,CAAA;aAAE,CAAC;SACrC,KAAK,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;QACxB;;;;;WAKG;QACH,gBAAgB,EAAE,CAAC,KAAK,EAAE;YACzB,OAAO,EAAE,OAAO,CAAC;SACjB,KAAK,OAAO,CAAC,KAAK,CAAC;YAAE,UAAU,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC,CAAC;KAC7C,CAAC;CACF;AAED;;;;;;GAMG;AACH,MAAM,WAAW,UAAU;IAC1B,IAAI,EAAE,QAAQ,CAAC;IACf,YAAY,CAAC,EAAE,QAAQ,CAAC;CACxB;AAED,MAAM,WAAW,kBAAmB,SAAQ,gBAAgB;IAC3D,iEAAiE;IACjE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;OAIG;IACH,GAAG,CAAC,EAAE,UAAU,CAAC;IACjB;;;;OAIG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B;;;;;;OAMG;IACH,UAAU,CAAC,EAAE,KAAK,GAAG,qBAAqB,CAAC;CAC3C;AAKD;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,SAAS,WAAW,EACrD,OAAO,EAAE,CAAC,EACV,GAAG,EAAE,UAAU,GACb,CAAC,CAyBH;AAED,wBAAgB,iBAAiB,CAAC,CAAC,SAAS,WAAW,EACtD,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,EACjB,OAAO,GAAE,kBAAuB;sBAqBG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC;mBAgB1B,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC;0BA+DlB,OAAO,CAAC,CAAC,CAAC;uBAXb,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;wBA3BjB,OAAO,CAAC,MAAM,CAAC;kCAkDH,MAAM,KAAG,OAAO,CAAC,IAAI,CAAC;8BAU5B,OAAO,CAAC,MAAM,EAAE,CAAC;gCAcf,OAAO,CAAC,OAAO,CAAC;EAevD"}
|
package/dist/server.js
CHANGED
|
@@ -86,7 +86,14 @@ export function createAuthHelpers(auth, options = {}) {
|
|
|
86
86
|
: authChainCheck(auth.options?.database, options.chainCheck);
|
|
87
87
|
const getSession = cache(async () => {
|
|
88
88
|
await ensureChain();
|
|
89
|
-
|
|
89
|
+
// Never refresh here: a refresh extends the row and re-issues the cookie,
|
|
90
|
+
// and a server component cannot set cookies, so the row would outlive a
|
|
91
|
+
// cookie that still expires on its sign-in max-age. Refreshes belong to
|
|
92
|
+
// the client's `/get-session` (see README, "Session lifetime").
|
|
93
|
+
const session = await auth.api.getSession({
|
|
94
|
+
headers: await headers(),
|
|
95
|
+
query: { disableRefresh: true },
|
|
96
|
+
});
|
|
90
97
|
return session && options.ids
|
|
91
98
|
? encodeSessionIds(session, options.ids)
|
|
92
99
|
: session;
|
package/dist/server.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,OAAO,EAA8B,cAAc,EAAE,MAAM,YAAY,CAAC;AAExE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAC3C,OAAO,EAAE,KAAK,EAAE,MAAM,OAAO,CAAC;AAC9B,OAAO,EACN,mBAAmB,EAEnB,SAAS,GACT,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,sBAAsB,EAAE,MAAM,eAAe,CAAC;AAEvD;;;;GAIG;AACH,OAAO,EAAE,aAAa,IAAI,QAAQ,EAAE,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { BetterAuthPlugin } from "better-auth";
|
|
2
|
+
/**
|
|
3
|
+
* A hard ceiling on how long one sign-in lasts, however active the user stays.
|
|
4
|
+
* Better Auth's own session is sliding: each refresh (at most once per
|
|
5
|
+
* `session.updateAge`) moves `expiresAt` to now + `session.expiresIn`, forever.
|
|
6
|
+
* This clamps every refresh to `createdAt + maxAge`, so a session idles out
|
|
7
|
+
* after `expiresIn` and ends outright `maxAge` after sign-in.
|
|
8
|
+
*
|
|
9
|
+
* plugins: [sessionLifetime({ maxAge: 60 * 60 * 24 * 90 })],
|
|
10
|
+
*
|
|
11
|
+
* Suite apps sharing one session table must pass the same `maxAge`: whichever
|
|
12
|
+
* instance refreshes the row applies its own.
|
|
13
|
+
*/
|
|
14
|
+
export declare function sessionLifetime(options: {
|
|
15
|
+
/** Seconds from sign-in after which the session can no longer be refreshed. */
|
|
16
|
+
maxAge: number;
|
|
17
|
+
}): BetterAuthPlugin;
|
|
18
|
+
//# sourceMappingURL=session-lifetime.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-lifetime.d.ts","sourceRoot":"","sources":["../src/session-lifetime.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAEpD;;;;;;;;;;;GAWG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE;IACxC,+EAA+E;IAC/E,MAAM,EAAE,MAAM,CAAC;CACf,GAAG,gBAAgB,CA4BnB"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A hard ceiling on how long one sign-in lasts, however active the user stays.
|
|
3
|
+
* Better Auth's own session is sliding: each refresh (at most once per
|
|
4
|
+
* `session.updateAge`) moves `expiresAt` to now + `session.expiresIn`, forever.
|
|
5
|
+
* This clamps every refresh to `createdAt + maxAge`, so a session idles out
|
|
6
|
+
* after `expiresIn` and ends outright `maxAge` after sign-in.
|
|
7
|
+
*
|
|
8
|
+
* plugins: [sessionLifetime({ maxAge: 60 * 60 * 24 * 90 })],
|
|
9
|
+
*
|
|
10
|
+
* Suite apps sharing one session table must pass the same `maxAge`: whichever
|
|
11
|
+
* instance refreshes the row applies its own.
|
|
12
|
+
*/
|
|
13
|
+
export function sessionLifetime(options) {
|
|
14
|
+
return {
|
|
15
|
+
id: "nk-session-lifetime",
|
|
16
|
+
init: () => ({
|
|
17
|
+
options: {
|
|
18
|
+
databaseHooks: {
|
|
19
|
+
session: {
|
|
20
|
+
update: {
|
|
21
|
+
before: async (data, ctx) => {
|
|
22
|
+
const createdAt = ctx?.context.session?.session.createdAt;
|
|
23
|
+
if (!(data.expiresAt instanceof Date) || !createdAt) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
const ceiling = new Date(new Date(createdAt).getTime() +
|
|
27
|
+
options.maxAge * 1000);
|
|
28
|
+
if (data.expiresAt > ceiling) {
|
|
29
|
+
return { data: { ...data, expiresAt: ceiling } };
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
}),
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=session-lifetime.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-lifetime.js","sourceRoot":"","sources":["../src/session-lifetime.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,eAAe,CAAC,OAG/B;IACA,OAAO;QACN,EAAE,EAAE,qBAAqB;QACzB,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC;YACZ,OAAO,EAAE;gBACR,aAAa,EAAE;oBACd,OAAO,EAAE;wBACR,MAAM,EAAE;4BACP,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,EAAE;gCAC3B,MAAM,SAAS,GACd,GAAG,EAAE,OAAO,CAAC,OAAO,EAAE,OAAO,CAAC,SAAS,CAAC;gCACzC,IAAI,CAAC,CAAC,IAAI,CAAC,SAAS,YAAY,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;oCACrD,OAAO;gCACR,CAAC;gCACD,MAAM,OAAO,GAAG,IAAI,IAAI,CACvB,IAAI,IAAI,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE;oCAC5B,OAAO,CAAC,MAAM,GAAG,IAAI,CACtB,CAAC;gCACF,IAAI,IAAI,CAAC,SAAS,GAAG,OAAO,EAAE,CAAC;oCAC9B,OAAO,EAAE,IAAI,EAAE,EAAE,GAAG,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,EAAE,CAAC;gCAClD,CAAC;4BACF,CAAC;yBACD;qBACD;iBACD;aACD;SACD,CAAC;KACF,CAAC;AACH,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ingram-tech/nk-auth",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.18.0",
|
|
4
4
|
"description": "The Ingram Better Auth foundation: composable presets (org, dual-shape JWT, active-org hooks, pg pool) for Next.js sites.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -69,7 +69,7 @@
|
|
|
69
69
|
"test": "vitest run"
|
|
70
70
|
},
|
|
71
71
|
"dependencies": {
|
|
72
|
-
"@ingram-tech/nk-db": "^2.
|
|
72
|
+
"@ingram-tech/nk-db": "^2.2.0",
|
|
73
73
|
"bcrypt": "^6.0.0",
|
|
74
74
|
"jose": "^6.2.12",
|
|
75
75
|
"zod": "^4.6.4"
|
|
@@ -97,7 +97,7 @@
|
|
|
97
97
|
},
|
|
98
98
|
"devDependencies": {
|
|
99
99
|
"@better-auth/passkey": "1.7.4",
|
|
100
|
-
"@ingram-tech/nk-dev": "0.
|
|
100
|
+
"@ingram-tech/nk-dev": "0.18.0",
|
|
101
101
|
"@types/bcrypt": "^6.0.0",
|
|
102
102
|
"@types/node": "^26.5.1",
|
|
103
103
|
"@types/pg": "^8.23.1",
|
package/src/index.ts
CHANGED
package/src/server.ts
CHANGED
|
@@ -61,7 +61,10 @@ interface AuthLike<S extends SessionLike> {
|
|
|
61
61
|
*/
|
|
62
62
|
options?: { database?: unknown };
|
|
63
63
|
api: {
|
|
64
|
-
getSession: (input: {
|
|
64
|
+
getSession: (input: {
|
|
65
|
+
headers: Headers;
|
|
66
|
+
query?: { disableRefresh?: boolean };
|
|
67
|
+
}) => Promise<S | null>;
|
|
65
68
|
/**
|
|
66
69
|
* Better Auth's `/list-accounts`: the auth methods linked to the current
|
|
67
70
|
* session's user, each `{ providerId }` ("credential" for email/password,
|
|
@@ -175,7 +178,14 @@ export function createAuthHelpers<S extends SessionLike>(
|
|
|
175
178
|
: authChainCheck(auth.options?.database, options.chainCheck);
|
|
176
179
|
const getSession = cache(async (): Promise<S | null> => {
|
|
177
180
|
await ensureChain();
|
|
178
|
-
|
|
181
|
+
// Never refresh here: a refresh extends the row and re-issues the cookie,
|
|
182
|
+
// and a server component cannot set cookies, so the row would outlive a
|
|
183
|
+
// cookie that still expires on its sign-in max-age. Refreshes belong to
|
|
184
|
+
// the client's `/get-session` (see README, "Session lifetime").
|
|
185
|
+
const session = await auth.api.getSession({
|
|
186
|
+
headers: await headers(),
|
|
187
|
+
query: { disableRefresh: true },
|
|
188
|
+
});
|
|
179
189
|
return session && options.ids
|
|
180
190
|
? encodeSessionIds(session, options.ids)
|
|
181
191
|
: session;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { BetterAuthPlugin } from "better-auth";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* A hard ceiling on how long one sign-in lasts, however active the user stays.
|
|
5
|
+
* Better Auth's own session is sliding: each refresh (at most once per
|
|
6
|
+
* `session.updateAge`) moves `expiresAt` to now + `session.expiresIn`, forever.
|
|
7
|
+
* This clamps every refresh to `createdAt + maxAge`, so a session idles out
|
|
8
|
+
* after `expiresIn` and ends outright `maxAge` after sign-in.
|
|
9
|
+
*
|
|
10
|
+
* plugins: [sessionLifetime({ maxAge: 60 * 60 * 24 * 90 })],
|
|
11
|
+
*
|
|
12
|
+
* Suite apps sharing one session table must pass the same `maxAge`: whichever
|
|
13
|
+
* instance refreshes the row applies its own.
|
|
14
|
+
*/
|
|
15
|
+
export function sessionLifetime(options: {
|
|
16
|
+
/** Seconds from sign-in after which the session can no longer be refreshed. */
|
|
17
|
+
maxAge: number;
|
|
18
|
+
}): BetterAuthPlugin {
|
|
19
|
+
return {
|
|
20
|
+
id: "nk-session-lifetime",
|
|
21
|
+
init: () => ({
|
|
22
|
+
options: {
|
|
23
|
+
databaseHooks: {
|
|
24
|
+
session: {
|
|
25
|
+
update: {
|
|
26
|
+
before: async (data, ctx) => {
|
|
27
|
+
const createdAt =
|
|
28
|
+
ctx?.context.session?.session.createdAt;
|
|
29
|
+
if (!(data.expiresAt instanceof Date) || !createdAt) {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
const ceiling = new Date(
|
|
33
|
+
new Date(createdAt).getTime() +
|
|
34
|
+
options.maxAge * 1000,
|
|
35
|
+
);
|
|
36
|
+
if (data.expiresAt > ceiling) {
|
|
37
|
+
return { data: { ...data, expiresAt: ceiling } };
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
}),
|
|
45
|
+
};
|
|
46
|
+
}
|