@ingram-tech/nk-auth 0.17.0 → 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/chain.d.ts +7 -2
- package/dist/chain.d.ts.map +1 -1
- package/dist/chain.js +10 -4
- package/dist/chain.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- 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 +5 -4
- package/src/chain.ts +17 -6
- package/src/index.ts +2 -1
- 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/chain.d.ts
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import { type MigrationStatus } from "@ingram-tech/nk-db/migrate";
|
|
2
2
|
import type { Pool } from "pg";
|
|
3
|
-
/**
|
|
4
|
-
|
|
3
|
+
/**
|
|
4
|
+
* The migration chain this package ships: `migrations/` in the installed
|
|
5
|
+
* package. Resolved through the package manifest, at call time, so a bundler
|
|
6
|
+
* that rewrites `import.meta.url` (Next's server build) still lands on the
|
|
7
|
+
* folder under `node_modules`, and never sees a URL it would try to inline.
|
|
8
|
+
*/
|
|
9
|
+
export declare const authMigrationsFolder: () => string;
|
|
5
10
|
/** The journal table the README tells a site to record the chain in. */
|
|
6
11
|
export declare const AUTH_MIGRATIONS_TABLE = "__nkauth_migrations";
|
|
7
12
|
/**
|
package/dist/chain.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chain.d.ts","sourceRoot":"","sources":["../src/chain.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqB,KAAK,eAAe,EAAE,MAAM,4BAA4B,CAAC;
|
|
1
|
+
{"version":3,"file":"chain.d.ts","sourceRoot":"","sources":["../src/chain.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqB,KAAK,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAGrF,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,IAAI,CAAC;AAE/B;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB,QAAO,MAOtC,CAAC;AAEH,wEAAwE;AACxE,eAAO,MAAM,qBAAqB,wBAAwB,CAAC;AAS3D;;;;;;GAMG;AACH,qBAAa,wBAAyB,SAAQ,KAAK;IAClD,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,YAAY,OAAO,EAAE,SAAS,MAAM,EAAE,EAMrC;CACD;AAED,MAAM,WAAW,qBAAqB;IACrC,sFAAsF;IACtF,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,qDAAqD;IACrD,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,sBAAsB,SAC5B,IAAI,YACD,qBAAqB,KAC5B,OAAO,CAAC,eAAe,GAAG,IAAI,CAkBhC,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,cAAc,aAChB,OAAO,YACR,qBAAqB,KAC5B,CAAC,MAAM,OAAO,CAAC,IAAI,CAAC,CAOtB,CAAC"}
|
package/dist/chain.js
CHANGED
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
import { inspectMigrations } from "@ingram-tech/nk-db/migrate";
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
import { createRequire } from "node:module";
|
|
3
|
+
import { dirname } from "node:path";
|
|
4
|
+
/**
|
|
5
|
+
* The migration chain this package ships: `migrations/` in the installed
|
|
6
|
+
* package. Resolved through the package manifest, at call time, so a bundler
|
|
7
|
+
* that rewrites `import.meta.url` (Next's server build) still lands on the
|
|
8
|
+
* folder under `node_modules`, and never sees a URL it would try to inline.
|
|
9
|
+
*/
|
|
10
|
+
export const authMigrationsFolder = () => dirname(dirname(createRequire(import.meta.url).resolve("@ingram-tech/nk-auth/migrations/meta/_journal.json")));
|
|
5
11
|
/** The journal table the README tells a site to record the chain in. */
|
|
6
12
|
export const AUTH_MIGRATIONS_TABLE = "__nkauth_migrations";
|
|
7
13
|
/** The shape of a `pg` Pool as far as the chain check needs it. */
|
|
@@ -45,7 +51,7 @@ export const assertAuthChainApplied = async (pool, options = {}) => {
|
|
|
45
51
|
return null;
|
|
46
52
|
const status = await inspectMigrations({
|
|
47
53
|
pool,
|
|
48
|
-
migrationsFolder:
|
|
54
|
+
migrationsFolder: authMigrationsFolder(),
|
|
49
55
|
migrationsSchema: schema,
|
|
50
56
|
migrationsTable: table,
|
|
51
57
|
});
|
package/dist/chain.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chain.js","sourceRoot":"","sources":["../src/chain.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAwB,MAAM,4BAA4B,CAAC;AACrF,OAAO,EAAE,aAAa,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"chain.js","sourceRoot":"","sources":["../src/chain.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAwB,MAAM,4BAA4B,CAAC;AACrF,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAGpC;;;;;GAKG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,GAAW,EAAE,CAChD,OAAO,CACN,OAAO,CACN,aAAa,CAAC,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,OAAO,CACrC,oDAAoD,CACpD,CACD,CACD,CAAC;AAEH,wEAAwE;AACxE,MAAM,CAAC,MAAM,qBAAqB,GAAG,qBAAqB,CAAC;AAE3D,mEAAmE;AACnE,MAAM,MAAM,GAAG,CAAC,KAAc,EAAiB,EAAE,CAChD,OAAO,KAAK,KAAK,QAAQ;IACzB,KAAK,KAAK,IAAI;IACd,OAAQ,KAA6B,CAAC,KAAK,KAAK,UAAU;IAC1D,OAAQ,KAA+B,CAAC,OAAO,KAAK,UAAU,CAAC;AAEhE;;;;;;GAMG;AACH,MAAM,OAAO,wBAAyB,SAAQ,KAAK;IACzC,OAAO,CAAoB;IACpC,YAAY,OAA0B;QACrC,KAAK,CACJ,yBAAyB,OAAO,CAAC,MAAM,6EAA6E,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,0GAA0G,qBAAqB,kCAAkC,CACvS,CAAC;QACF,IAAI,CAAC,IAAI,GAAG,0BAA0B,CAAC;QACvC,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IACxB,CAAC;CACD;AASD;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG,KAAK,EAC1C,IAAU,EACV,OAAO,GAA0B,EAAE,EACD,EAAE;IACpC,MAAM,MAAM,GAAG,OAAO,CAAC,gBAAgB,IAAI,SAAS,CAAC;IACrD,MAAM,KAAK,GAAG,OAAO,CAAC,eAAe,IAAI,qBAAqB,CAAC;IAC/D,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,IAAI,CAAC,KAAK,CAChC,mCAAmC,EACnC,CAAC,IAAI,MAAM,MAAM,KAAK,GAAG,CAAC,CAC1B,CAAC;IACF,IAAI,IAAI,CAAC,CAAC,CAAC,EAAE,OAAO,KAAK,IAAI,IAAI,IAAI,CAAC,CAAC,CAAC,EAAE,OAAO,KAAK,SAAS;QAAE,OAAO,IAAI,CAAC;IAC7E,MAAM,MAAM,GAAG,MAAM,iBAAiB,CAAC;QACtC,IAAI;QACJ,gBAAgB,EAAE,oBAAoB,EAAE;QACxC,gBAAgB,EAAE,MAAM;QACxB,eAAe,EAAE,KAAK;KACtB,CAAC,CAAC;IACH,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC/B,MAAM,IAAI,wBAAwB,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IAC5E,CAAC;IACD,OAAO,MAAM,CAAC;AACf,CAAC,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,CAC7B,QAAiB,EACjB,OAAO,GAA0B,EAAE,EACX,EAAE;IAC1B,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC;QAAE,OAAO,KAAK,IAAI,EAAE,GAAE,CAAC,CAAC;IAC7C,IAAI,OAAkC,CAAC;IACvC,OAAO,GAAG,EAAE;QACX,OAAO,KAAK,sBAAsB,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;QAC5E,OAAO,OAAO,CAAC;IAChB,CAAC,CAAC;AACH,CAAC,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { assertAuthChainApplied,
|
|
1
|
+
export { assertAuthChainApplied, authMigrationsFolder, AUTH_MIGRATIONS_TABLE, type AuthChainCheckOptions, AuthChainNotAppliedError, } from "./chain.js";
|
|
2
2
|
export { type BackendJwtConfig, backendJwtOptions, verifyBackendJwt } from "./jwt.js";
|
|
3
3
|
export { base58Id, fromPrefixedId, toPrefixedId, uuidGenerateId } from "./id.js";
|
|
4
4
|
export { type AuthEnv, authEnv, authSecret, isConfigured } from "./keys.js";
|
|
@@ -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,
|
|
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
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// "@ingram-tech/nk-auth/client" so importing the server presets never pulls in
|
|
3
3
|
// React. Focused subpaths (./jwt, ./organization, ./pool) let a site import
|
|
4
4
|
// only what it needs (e.g. avoid bcrypt when it doesn't use passwords).
|
|
5
|
-
export { assertAuthChainApplied,
|
|
5
|
+
export { assertAuthChainApplied, authMigrationsFolder, AUTH_MIGRATIONS_TABLE, AuthChainNotAppliedError, } from "./chain.js";
|
|
6
6
|
export { backendJwtOptions, verifyBackendJwt } from "./jwt.js";
|
|
7
7
|
export { base58Id, fromPrefixedId, toPrefixedId, uuidGenerateId } from "./id.js";
|
|
8
8
|
export { authEnv, authSecret, isConfigured } from "./keys.js";
|
|
@@ -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,
|
|
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",
|
|
@@ -60,7 +60,8 @@
|
|
|
60
60
|
"types": "./dist/client.d.ts",
|
|
61
61
|
"import": "./dist/client.js"
|
|
62
62
|
},
|
|
63
|
-
"./migrations/*": "./migrations/*"
|
|
63
|
+
"./migrations/*": "./migrations/*",
|
|
64
|
+
"./package.json": "./package.json"
|
|
64
65
|
},
|
|
65
66
|
"scripts": {
|
|
66
67
|
"build": "rm -rf dist *.tsbuildinfo && tsc -p tsconfig.build.json",
|
|
@@ -68,7 +69,7 @@
|
|
|
68
69
|
"test": "vitest run"
|
|
69
70
|
},
|
|
70
71
|
"dependencies": {
|
|
71
|
-
"@ingram-tech/nk-db": "^2.
|
|
72
|
+
"@ingram-tech/nk-db": "^2.2.0",
|
|
72
73
|
"bcrypt": "^6.0.0",
|
|
73
74
|
"jose": "^6.2.12",
|
|
74
75
|
"zod": "^4.6.4"
|
|
@@ -96,7 +97,7 @@
|
|
|
96
97
|
},
|
|
97
98
|
"devDependencies": {
|
|
98
99
|
"@better-auth/passkey": "1.7.4",
|
|
99
|
-
"@ingram-tech/nk-dev": "0.
|
|
100
|
+
"@ingram-tech/nk-dev": "0.18.0",
|
|
100
101
|
"@types/bcrypt": "^6.0.0",
|
|
101
102
|
"@types/node": "^26.5.1",
|
|
102
103
|
"@types/pg": "^8.23.1",
|
package/src/chain.ts
CHANGED
|
@@ -1,11 +1,22 @@
|
|
|
1
1
|
import { inspectMigrations, type MigrationStatus } from "@ingram-tech/nk-db/migrate";
|
|
2
|
-
import {
|
|
2
|
+
import { createRequire } from "node:module";
|
|
3
|
+
import { dirname } from "node:path";
|
|
3
4
|
import type { Pool } from "pg";
|
|
4
5
|
|
|
5
|
-
/**
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
)
|
|
6
|
+
/**
|
|
7
|
+
* The migration chain this package ships: `migrations/` in the installed
|
|
8
|
+
* package. Resolved through the package manifest, at call time, so a bundler
|
|
9
|
+
* that rewrites `import.meta.url` (Next's server build) still lands on the
|
|
10
|
+
* folder under `node_modules`, and never sees a URL it would try to inline.
|
|
11
|
+
*/
|
|
12
|
+
export const authMigrationsFolder = (): string =>
|
|
13
|
+
dirname(
|
|
14
|
+
dirname(
|
|
15
|
+
createRequire(import.meta.url).resolve(
|
|
16
|
+
"@ingram-tech/nk-auth/migrations/meta/_journal.json",
|
|
17
|
+
),
|
|
18
|
+
),
|
|
19
|
+
);
|
|
9
20
|
|
|
10
21
|
/** The journal table the README tells a site to record the chain in. */
|
|
11
22
|
export const AUTH_MIGRATIONS_TABLE = "__nkauth_migrations";
|
|
@@ -68,7 +79,7 @@ export const assertAuthChainApplied = async (
|
|
|
68
79
|
if (rows[0]?.present === null || rows[0]?.present === undefined) return null;
|
|
69
80
|
const status = await inspectMigrations({
|
|
70
81
|
pool,
|
|
71
|
-
migrationsFolder:
|
|
82
|
+
migrationsFolder: authMigrationsFolder(),
|
|
72
83
|
migrationsSchema: schema,
|
|
73
84
|
migrationsTable: table,
|
|
74
85
|
});
|
package/src/index.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
export {
|
|
7
7
|
assertAuthChainApplied,
|
|
8
|
-
|
|
8
|
+
authMigrationsFolder,
|
|
9
9
|
AUTH_MIGRATIONS_TABLE,
|
|
10
10
|
type AuthChainCheckOptions,
|
|
11
11
|
AuthChainNotAppliedError,
|
|
@@ -41,3 +41,4 @@ export {
|
|
|
41
41
|
} from "./password.js";
|
|
42
42
|
export { authBasePath } from "./paths.js";
|
|
43
43
|
export { createAuthPool } from "./pool.js";
|
|
44
|
+
export { sessionLifetime } from "./session-lifetime.js";
|
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
|
+
}
|