@ingram-tech/nk-auth 0.12.2 → 0.13.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 +123 -11
- package/dist/middleware.d.ts.map +1 -1
- package/dist/middleware.js +8 -5
- package/dist/middleware.js.map +1 -1
- package/dist/options.d.ts +68 -15
- package/dist/options.d.ts.map +1 -1
- package/dist/options.js +59 -6
- package/dist/options.js.map +1 -1
- package/migrations/0001_better_auth.sql +36 -12
- package/migrations/meta/_journal.json +13 -0
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -70,16 +70,64 @@ the app: the security-sensitive default then lives in exactly one place.
|
|
|
70
70
|
|
|
71
71
|
## 1. Apply the schema
|
|
72
72
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
73
|
+
nk-auth **owns its auth tables as its own migration chain** — the versioned SQL
|
|
74
|
+
files it ships in `migrations/`, journaled separately from your app's `drizzle/`
|
|
75
|
+
migrations. You don't copy anything in; you point the runner at the shipped
|
|
76
|
+
folder. It creates Better Auth's tables (`user`, `session`, `account`,
|
|
77
|
+
`verification`, `jwks`, `passkey`), defaults new user ids to UUIDs, and puts
|
|
78
|
+
**deny-all RLS** on all of them (Better Auth reaches them through its own
|
|
79
|
+
privileged connection). See [db-package.md § "The nk-auth migration
|
|
80
|
+
chain"](../../docs/db-package.md#the-nk-auth-migration-chain) for the full model.
|
|
81
|
+
|
|
82
|
+
The auth chain runs **before** your app chain, because your app tables FK to
|
|
83
|
+
`user`.
|
|
84
|
+
|
|
85
|
+
**Production / deploy** — add the auth chain to your `db:migrate` script,
|
|
86
|
+
auth-first, on its own journal table:
|
|
87
|
+
|
|
88
|
+
```jsonc
|
|
89
|
+
// package.json
|
|
90
|
+
"scripts": {
|
|
91
|
+
"db:migrate": "nk-pg-migrate --migrations node_modules/@ingram-tech/nk-auth/migrations --table __nkauth_migrations && nk-pg-migrate"
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
The first invocation applies (and journals) nk-auth's chain; the second applies
|
|
96
|
+
your app's `drizzle/` chain. Both are idempotent and drift-checked, so re-running
|
|
97
|
+
is a no-op — see the [`nk-pg-migrate` runner](../../docs/db-package.md#migrations).
|
|
98
|
+
|
|
99
|
+
**Local dev** — nothing to do. `nk dev` resolves nk-auth and applies its chain to
|
|
100
|
+
the local PGlite database automatically, before your `drizzle/` migrations.
|
|
101
|
+
|
|
102
|
+
**Tests** — when a test needs the auth tables, pass the shipped folder to
|
|
103
|
+
`createTestDb` as a dependency chain (applied first, own journal table):
|
|
104
|
+
|
|
105
|
+
```ts
|
|
106
|
+
import { createRequire } from "node:module";
|
|
107
|
+
import { dirname, join } from "node:path";
|
|
108
|
+
|
|
109
|
+
const authMigrations = join(
|
|
110
|
+
dirname(createRequire(import.meta.url).resolve("@ingram-tech/nk-auth/package.json")),
|
|
111
|
+
"migrations",
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
const db = await createTestDb({
|
|
115
|
+
dependencyMigrations: [{ folder: authMigrations, table: "__nkauth_migrations" }],
|
|
116
|
+
});
|
|
76
117
|
```
|
|
77
118
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
119
|
+
**Upgrading better-auth** never means hand-writing a migration in your site: a
|
|
120
|
+
schema-changing upgrade ships as a new file in nk-auth's chain, so you bump the
|
|
121
|
+
dependency and your next migrate applies it. Reconcile the shipped schema against
|
|
122
|
+
a pinned `better-auth` with `npx @better-auth/cli generate` (a nextkit-maintainer
|
|
123
|
+
task, done once here — not per site).
|
|
124
|
+
|
|
125
|
+
> **Adopting from the old copy-in model?** Earlier docs told sites to `cp` the
|
|
126
|
+
> baseline into their own `drizzle/` chain. If you already did, keep that file
|
|
127
|
+
> (deleting an applied migration causes journal drift) and just add the auth-chain
|
|
128
|
+
> invocation above: its DDL is all `… if not exists`, so it no-ops against your
|
|
129
|
+
> existing tables and simply records the nk-auth journal. New auth migrations then
|
|
130
|
+
> flow through the shipped chain from here on.
|
|
83
131
|
|
|
84
132
|
## 2. Configure the server
|
|
85
133
|
|
|
@@ -103,9 +151,13 @@ import { betterAuth } from "better-auth";
|
|
|
103
151
|
import { pool } from "@/lib/db"; // the ONE shared createPool() from @ingram-tech/nk-db
|
|
104
152
|
|
|
105
153
|
const env = authEnv();
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
154
|
+
// Render a real template per `kind` — these are the first mails a user ever
|
|
155
|
+
// gets from you. `text: url, html: url` ships a bare link that reads as
|
|
156
|
+
// phishing; see "Auth emails" below.
|
|
157
|
+
const email = makeEmailSenders(async ({ kind, to, url, user, newEmail }) => {
|
|
158
|
+
const { subject, html, text } = await renderAuthEmail({ kind, url, user, newEmail });
|
|
159
|
+
sendEmail({ to, from: fromAddress("Example", "no-reply"), subject, html, text });
|
|
160
|
+
});
|
|
109
161
|
|
|
110
162
|
export const auth = betterAuth({
|
|
111
163
|
database: pool, // inject the shared pool — exactly one pool per process
|
|
@@ -125,6 +177,15 @@ export const auth = betterAuth({
|
|
|
125
177
|
sendResetPassword: email.sendResetPassword,
|
|
126
178
|
},
|
|
127
179
|
emailVerification: { sendVerificationEmail: email.sendVerificationEmail },
|
|
180
|
+
user: {
|
|
181
|
+
// Confirms the move from the CURRENT address. Note the name: it is
|
|
182
|
+
// `sendChangeEmailConfirmation`, and betterAuth() does not
|
|
183
|
+
// excess-property-check, so a wrong name here silently never fires.
|
|
184
|
+
changeEmail: {
|
|
185
|
+
enabled: true,
|
|
186
|
+
sendChangeEmailConfirmation: email.sendChangeEmailConfirmation,
|
|
187
|
+
},
|
|
188
|
+
},
|
|
128
189
|
socialProviders: {
|
|
129
190
|
google: {
|
|
130
191
|
clientId: process.env.GOOGLE_CLIENT_ID ?? "",
|
|
@@ -149,6 +210,57 @@ import { auth } from "@/lib/auth";
|
|
|
149
210
|
export const { GET, POST } = toNextJsHandler(auth);
|
|
150
211
|
```
|
|
151
212
|
|
|
213
|
+
## Auth emails
|
|
214
|
+
|
|
215
|
+
`makeEmailSenders(send)` returns the three callbacks Better Auth needs. Every
|
|
216
|
+
message reaches your `send` with a **`kind`** discriminator — switch on that to
|
|
217
|
+
pick a template. Never switch on `subject`: it is default English copy that a
|
|
218
|
+
localized site is expected to throw away.
|
|
219
|
+
|
|
220
|
+
| `kind` | Better Auth option | Goes to |
|
|
221
|
+
| ---------------- | --------------------------------------------- | --------------------- |
|
|
222
|
+
| `verify-email` | `emailVerification.sendVerificationEmail` | the new signup |
|
|
223
|
+
| `reset-password` | `emailAndPassword.sendResetPassword` | the account address |
|
|
224
|
+
| `change-email` | `user.changeEmail.sendChangeEmailConfirmation` | the **current** address |
|
|
225
|
+
|
|
226
|
+
Each message also carries `user` (`id` for a locale/preferences lookup, `name`
|
|
227
|
+
to personalize), `token`, and the originating `request` — whose `Accept-Language`
|
|
228
|
+
is the only locale signal available for a signup verification, before the user
|
|
229
|
+
has any stored preference.
|
|
230
|
+
|
|
231
|
+
**Render a real template.** Verification, reset and change-email are the first
|
|
232
|
+
mail a user ever gets from you; a bare `<a href=url>url</a>` reads as phishing
|
|
233
|
+
and trains people to distrust your domain. Take the
|
|
234
|
+
[`registry`](https://github.com/ingram-technologies/registry) email components
|
|
235
|
+
(`shadcn add email-verification email-password-reset`) — they accept `heading` /
|
|
236
|
+
`body` / `ctaLabel` / `preview` overrides precisely so you can pass translated
|
|
237
|
+
copy — or write your own. Send auth links from the `no-reply` local part
|
|
238
|
+
(`fromAddress("Example", "no-reply")`), per
|
|
239
|
+
[transactional-email.md](../../docs/transactional-email.md).
|
|
240
|
+
|
|
241
|
+
**Spread all three in; do not hand-write them.** `betterAuth()` takes its
|
|
242
|
+
options through a generic, which switches **off** excess-property checking on
|
|
243
|
+
that object literal. A callback under a wrong-but-plausible name compiles
|
|
244
|
+
perfectly and then never fires:
|
|
245
|
+
|
|
246
|
+
```ts
|
|
247
|
+
user: {
|
|
248
|
+
changeEmail: {
|
|
249
|
+
enabled: true,
|
|
250
|
+
// WRONG NAME. No type error — and no email, ever. The real option is
|
|
251
|
+
// `sendChangeEmailConfirmation`.
|
|
252
|
+
sendChangeEmailVerification: async ({ user, url }) => { /* dead code */ },
|
|
253
|
+
},
|
|
254
|
+
},
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
That is not hypothetical — it shipped, and the failure is silent in both
|
|
258
|
+
directions: Better Auth falls through to sending the *verification* mail to the
|
|
259
|
+
**new** address instead, so the current address is never told its account is
|
|
260
|
+
moving. `makeEmailSenders` exists to keep you off that path; `options.ts` pins
|
|
261
|
+
all three names to the real Better Auth option types, so an upstream rename
|
|
262
|
+
fails nk-auth's build rather than quietly disabling your mail.
|
|
263
|
+
|
|
152
264
|
## 3. Query data with RLS intact
|
|
153
265
|
|
|
154
266
|
Data access lives in [`@ingram-tech/nk-db`](../nk-db), not here. Query over the
|
package/dist/middleware.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":"AA4BA,OAAO,EAAE,KAAK,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAG7D,MAAM,WAAW,oBAAoB;IACpC;;;OAGG;IACH,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,8DAA8D;IAC9D,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;OAIG;IACH,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,sFAAsF;IACtF,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;;;OAIG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,oBAAoB,
|
|
1
|
+
{"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":"AA4BA,OAAO,EAAE,KAAK,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAG7D,MAAM,WAAW,oBAAoB;IACpC;;;OAGG;IACH,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,8DAA8D;IAC9D,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;OAIG;IACH,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,sFAAsF;IACtF,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;;;OAIG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,oBAAoB,aAiC5B,WAAW,KAAG,YAAY,CA6D9D"}
|
package/dist/middleware.js
CHANGED
|
@@ -32,11 +32,17 @@ export function createAuthMiddleware(config) {
|
|
|
32
32
|
const signInPath = config.signInPath ?? "/login";
|
|
33
33
|
const frontDoorPaths = config.frontDoorPaths ?? [];
|
|
34
34
|
const cookiePrefix = config.sessionCookiePrefix ?? "better-auth";
|
|
35
|
+
// Segment-boundary match: "/app" gates "/app" and "/app/x" but not
|
|
36
|
+
// "/application". Used for both the construction-time loop check and the
|
|
37
|
+
// per-request gate, so the two can't drift (a broader construction check
|
|
38
|
+
// would reject safe configs like protectedPaths ["/log"] + signInPath
|
|
39
|
+
// "/login", where "/login" is never actually gated).
|
|
40
|
+
const isProtected = (path) => config.protectedPaths.some((p) => path === p || path.startsWith(`${p}/`));
|
|
35
41
|
// Loop-safety, enforced once at construction rather than hoped-for per
|
|
36
42
|
// request. The sign-in path is where the validated guard parks an
|
|
37
43
|
// invalid-but-present session; it must never be the target of an optimistic
|
|
38
44
|
// redirect, or a stale cookie loops.
|
|
39
|
-
if (
|
|
45
|
+
if (isProtected(signInPath)) {
|
|
40
46
|
throw new Error(`@ingram-tech/nk-auth: signInPath "${signInPath}" must not fall under protectedPaths — a cookie-less visit would redirect to itself forever.`);
|
|
41
47
|
}
|
|
42
48
|
if (frontDoorPaths.includes(signInPath)) {
|
|
@@ -74,10 +80,7 @@ export function createAuthMiddleware(config) {
|
|
|
74
80
|
const hasSessionCookie = !!getSessionCookie(request);
|
|
75
81
|
// 2. Unauthenticated (no cookie) on a protected path -> sign in, and
|
|
76
82
|
// remember where they were going.
|
|
77
|
-
if (!hasSessionCookie &&
|
|
78
|
-
// Segment-boundary match: "/app" must gate "/app" and "/app/x" but not
|
|
79
|
-
// "/application".
|
|
80
|
-
config.protectedPaths.some((p) => path === p || path.startsWith(`${p}/`))) {
|
|
83
|
+
if (!hasSessionCookie && isProtected(path)) {
|
|
81
84
|
const original = request.nextUrl.pathname + request.nextUrl.search;
|
|
82
85
|
const to = request.nextUrl.clone();
|
|
83
86
|
to.pathname = signInPath;
|
package/dist/middleware.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"middleware.js","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,EAAoB,YAAY,EAAE,MAAM,aAAa,CAAC;AAC7D,OAAO,EAAE,mBAAmB,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AA0B3E,MAAM,UAAU,oBAAoB,CAAC,MAA4B;IAChE,MAAM,UAAU,GAAG,MAAM,CAAC,UAAU,IAAI,QAAQ,CAAC;IACjD,MAAM,cAAc,GAAG,MAAM,CAAC,cAAc,IAAI,EAAE,CAAC;IACnD,MAAM,YAAY,GAAG,MAAM,CAAC,mBAAmB,IAAI,aAAa,CAAC;IAEjE,
|
|
1
|
+
{"version":3,"file":"middleware.js","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":"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"}
|
package/dist/options.d.ts
CHANGED
|
@@ -17,6 +17,14 @@ export { uuidGenerateId } from "./id.js";
|
|
|
17
17
|
* Better Auth's default is scrypt — new sites should omit this and use that
|
|
18
18
|
* default. Sites still on bcrypt should migrate hashes to scrypt and drop this;
|
|
19
19
|
* see the nk-auth README (§"Migrating bcrypt passwords to scrypt") for the path.
|
|
20
|
+
*
|
|
21
|
+
* NOTE: bcrypt silently truncates the password at **72 bytes**, so two passwords
|
|
22
|
+
* sharing their first 72 bytes verify as equal — even though the password policy
|
|
23
|
+
* allows up to {@link DEFAULT_MAX_PASSWORD_LENGTH} (128) characters. This is a
|
|
24
|
+
* property of bcrypt, not a bug here, and it's deliberately not length-guarded:
|
|
25
|
+
* a guard would break verification of the legacy long-password hashes this
|
|
26
|
+
* preset exists to support (bcrypt already truncated them at hash time). It is
|
|
27
|
+
* one more reason to migrate off bcrypt — scrypt has no such ceiling.
|
|
20
28
|
*/
|
|
21
29
|
export declare const bcryptPassword: {
|
|
22
30
|
hash: (password: string) => Promise<string>;
|
|
@@ -49,28 +57,73 @@ export declare const makePasskeyOptions: (cfg: PasskeyConfig) => PasskeyOptions;
|
|
|
49
57
|
* for an "app.example.com" origin).
|
|
50
58
|
*/
|
|
51
59
|
export declare const passkeyOptionsForBaseUrl: (baseURL: string, rpName: string) => PasskeyOptions;
|
|
52
|
-
/**
|
|
53
|
-
|
|
60
|
+
/**
|
|
61
|
+
* Which auth mail is being sent. Switch on this to pick a template — never on
|
|
62
|
+
* `subject`, which is English default copy a site is expected to replace.
|
|
63
|
+
*/
|
|
64
|
+
export type AuthEmailKind = "verify-email" | "reset-password" | "change-email";
|
|
65
|
+
/** The subset of the Better Auth user these callbacks pass through. */
|
|
66
|
+
export interface AuthEmailUser {
|
|
67
|
+
id: string;
|
|
68
|
+
email: string;
|
|
69
|
+
name?: string;
|
|
70
|
+
}
|
|
71
|
+
/** One outgoing auth mail, handed to the site's sender. */
|
|
72
|
+
export interface AuthEmailMessage {
|
|
73
|
+
/** Discriminator — pick the template off this. */
|
|
74
|
+
kind: AuthEmailKind;
|
|
75
|
+
/**
|
|
76
|
+
* Recipient. For `change-email` this is deliberately the user's CURRENT
|
|
77
|
+
* address: confirming the move from the address that already owns the
|
|
78
|
+
* account is what stops a hijacked session from walking off with it.
|
|
79
|
+
*/
|
|
54
80
|
to: string;
|
|
81
|
+
/**
|
|
82
|
+
* Default English subject, for sites that don't localize. Prefer your own
|
|
83
|
+
* translated copy — see the README.
|
|
84
|
+
*/
|
|
55
85
|
subject: string;
|
|
86
|
+
/** The one-time action link. */
|
|
56
87
|
url: string;
|
|
57
|
-
|
|
88
|
+
/** The raw token behind `url`, if you need to build your own link. */
|
|
89
|
+
token: string;
|
|
90
|
+
/** `id` lets you look up a locale/preferences; `name` personalizes copy. */
|
|
91
|
+
user: AuthEmailUser;
|
|
92
|
+
/** `change-email` only: the address the user is moving to. */
|
|
93
|
+
newEmail?: string;
|
|
94
|
+
/** The originating request — `Accept-Language` is a locale source. */
|
|
95
|
+
request?: Request;
|
|
96
|
+
}
|
|
97
|
+
/** Send one transactional email (wire to `@ingram-tech/nk-email`'s `sendEmail`). */
|
|
98
|
+
export type SendEmail = (message: AuthEmailMessage) => Promise<unknown>;
|
|
58
99
|
/**
|
|
59
|
-
* Email callbacks for `emailAndPassword.sendResetPassword
|
|
60
|
-
* `emailVerification.sendVerificationEmail
|
|
100
|
+
* Email callbacks for `emailAndPassword.sendResetPassword`,
|
|
101
|
+
* `emailVerification.sendVerificationEmail` and
|
|
102
|
+
* `user.changeEmail.sendChangeEmailConfirmation`, routed through your sender.
|
|
103
|
+
*
|
|
104
|
+
* Spread all three in. Hand-writing them is a trap: `betterAuth()` infers its
|
|
105
|
+
* options generically, so TypeScript does NOT excess-property-check that object
|
|
106
|
+
* literal — a callback under a misremembered name (`sendChangeEmailVerification`
|
|
107
|
+
* is the one people reach for) compiles clean and simply never fires. The
|
|
108
|
+
* `PinnedEmailSenders` assertion below ties these three names to the real Better
|
|
109
|
+
* Auth options, so a rename upstream breaks this build instead of your site.
|
|
61
110
|
*/
|
|
62
111
|
export declare const makeEmailSenders: (send: SendEmail) => {
|
|
63
|
-
sendResetPassword: ({ user, url, }: {
|
|
64
|
-
user:
|
|
65
|
-
|
|
66
|
-
|
|
112
|
+
sendResetPassword: ({ user, url, token }: {
|
|
113
|
+
user: AuthEmailUser;
|
|
114
|
+
url: string;
|
|
115
|
+
token: string;
|
|
116
|
+
}, request?: Request) => Promise<void>;
|
|
117
|
+
sendVerificationEmail: ({ user, url, token }: {
|
|
118
|
+
user: AuthEmailUser;
|
|
67
119
|
url: string;
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
120
|
+
token: string;
|
|
121
|
+
}, request?: Request) => Promise<void>;
|
|
122
|
+
sendChangeEmailConfirmation: ({ user, newEmail, url, token, }: {
|
|
123
|
+
user: AuthEmailUser;
|
|
124
|
+
newEmail: string;
|
|
73
125
|
url: string;
|
|
74
|
-
|
|
126
|
+
token: string;
|
|
127
|
+
}, request?: Request) => Promise<void>;
|
|
75
128
|
};
|
|
76
129
|
//# sourceMappingURL=options.d.ts.map
|
package/dist/options.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"options.d.ts","sourceRoot":"","sources":["../src/options.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"options.d.ts","sourceRoot":"","sources":["../src/options.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAO3D,OAAO,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAEzC;;;;;;;;GAQG;AAEH;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,cAAc;IAC1B,IAAI,aAAa,MAAM,KAAG,OAAO,CAAC,MAAM,CAAC;IACzC,MAAM,wBAGH;QACF,IAAI,EAAE,MAAM,CAAC;QACb,QAAQ,EAAE,MAAM,CAAC;KACjB,KAAG,OAAO,CAAC,OAAO,CAAC;CACpB,CAAC;AAEF,MAAM,WAAW,aAAa;IAC7B,oEAAoE;IACpE,IAAI,EAAE,MAAM,CAAC;IACb,kCAAkC;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,sDAAsD;IACtD,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;CAC1B;AAED,iFAAiF;AACjF,eAAO,MAAM,kBAAkB,QAAS,aAAa,KAAG,cAItD,CAAC;AAEH;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,wBAAwB,YAC3B,MAAM,UACP,MAAM,KACZ,cAKA,CAAC;AAEJ;;;GAGG;AACH,MAAM,MAAM,aAAa,GAAG,cAAc,GAAG,gBAAgB,GAAG,cAAc,CAAC;AAE/E,uEAAuE;AACvE,MAAM,WAAW,aAAa;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;CACd;AAED,2DAA2D;AAC3D,MAAM,WAAW,gBAAgB;IAChC,kDAAkD;IAClD,IAAI,EAAE,aAAa,CAAC;IACpB;;;;OAIG;IACH,EAAE,EAAE,MAAM,CAAC;IACX;;;OAGG;IACH,OAAO,EAAE,MAAM,CAAC;IAChB,gCAAgC;IAChC,GAAG,EAAE,MAAM,CAAC;IACZ,sEAAsE;IACtE,KAAK,EAAE,MAAM,CAAC;IACd,4EAA4E;IAC5E,IAAI,EAAE,aAAa,CAAC;IACpB,8DAA8D;IAC9D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,sEAAsE;IACtE,OAAO,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,oFAAoF;AACpF,MAAM,MAAM,SAAS,GAAG,CAAC,OAAO,EAAE,gBAAgB,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;AAExE;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,gBAAgB,SAAU,SAAS;IAC/C,iBAAiB,yBACM;QAAE,IAAI,EAAE,aAAa,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,YAC/D,OAAO,KACf,OAAO,CAAC,IAAI,CAAC;IAWhB,qBAAqB,yBACE;QAAE,IAAI,EAAE,aAAa,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,YAC/D,OAAO,KACf,OAAO,CAAC,IAAI,CAAC;IAWhB,2BAA2B,oCAMvB;QACF,IAAI,EAAE,aAAa,CAAC;QACpB,QAAQ,EAAE,MAAM,CAAC;QACjB,GAAG,EAAE,MAAM,CAAC;QACZ,KAAK,EAAE,MAAM,CAAC;KACd,YACS,OAAO,KACf,OAAO,CAAC,IAAI,CAAC;CAYf,CAAC"}
|
package/dist/options.js
CHANGED
|
@@ -20,6 +20,14 @@ export { uuidGenerateId } from "./id.js";
|
|
|
20
20
|
* Better Auth's default is scrypt — new sites should omit this and use that
|
|
21
21
|
* default. Sites still on bcrypt should migrate hashes to scrypt and drop this;
|
|
22
22
|
* see the nk-auth README (§"Migrating bcrypt passwords to scrypt") for the path.
|
|
23
|
+
*
|
|
24
|
+
* NOTE: bcrypt silently truncates the password at **72 bytes**, so two passwords
|
|
25
|
+
* sharing their first 72 bytes verify as equal — even though the password policy
|
|
26
|
+
* allows up to {@link DEFAULT_MAX_PASSWORD_LENGTH} (128) characters. This is a
|
|
27
|
+
* property of bcrypt, not a bug here, and it's deliberately not length-guarded:
|
|
28
|
+
* a guard would break verification of the legacy long-password hashes this
|
|
29
|
+
* preset exists to support (bcrypt already truncated them at hash time). It is
|
|
30
|
+
* one more reason to migrate off bcrypt — scrypt has no such ceiling.
|
|
23
31
|
*/
|
|
24
32
|
export const bcryptPassword = {
|
|
25
33
|
hash: (password) => bcrypt.hash(password, 10),
|
|
@@ -50,15 +58,60 @@ export const passkeyOptionsForBaseUrl = (baseURL, rpName) => makePasskeyOptions(
|
|
|
50
58
|
origin: baseURL,
|
|
51
59
|
});
|
|
52
60
|
/**
|
|
53
|
-
* Email callbacks for `emailAndPassword.sendResetPassword
|
|
54
|
-
* `emailVerification.sendVerificationEmail
|
|
61
|
+
* Email callbacks for `emailAndPassword.sendResetPassword`,
|
|
62
|
+
* `emailVerification.sendVerificationEmail` and
|
|
63
|
+
* `user.changeEmail.sendChangeEmailConfirmation`, routed through your sender.
|
|
64
|
+
*
|
|
65
|
+
* Spread all three in. Hand-writing them is a trap: `betterAuth()` infers its
|
|
66
|
+
* options generically, so TypeScript does NOT excess-property-check that object
|
|
67
|
+
* literal — a callback under a misremembered name (`sendChangeEmailVerification`
|
|
68
|
+
* is the one people reach for) compiles clean and simply never fires. The
|
|
69
|
+
* `PinnedEmailSenders` assertion below ties these three names to the real Better
|
|
70
|
+
* Auth options, so a rename upstream breaks this build instead of your site.
|
|
55
71
|
*/
|
|
56
72
|
export const makeEmailSenders = (send) => ({
|
|
57
|
-
sendResetPassword: async ({ user, url, }) => {
|
|
58
|
-
await send({
|
|
73
|
+
sendResetPassword: async ({ user, url, token }, request) => {
|
|
74
|
+
await send({
|
|
75
|
+
kind: "reset-password",
|
|
76
|
+
to: user.email,
|
|
77
|
+
subject: "Reset your password",
|
|
78
|
+
url,
|
|
79
|
+
token,
|
|
80
|
+
user,
|
|
81
|
+
request,
|
|
82
|
+
});
|
|
59
83
|
},
|
|
60
|
-
sendVerificationEmail: async ({ user, url, }) => {
|
|
61
|
-
await send({
|
|
84
|
+
sendVerificationEmail: async ({ user, url, token }, request) => {
|
|
85
|
+
await send({
|
|
86
|
+
kind: "verify-email",
|
|
87
|
+
to: user.email,
|
|
88
|
+
subject: "Verify your email",
|
|
89
|
+
url,
|
|
90
|
+
token,
|
|
91
|
+
user,
|
|
92
|
+
request,
|
|
93
|
+
});
|
|
94
|
+
},
|
|
95
|
+
sendChangeEmailConfirmation: async ({ user, newEmail, url, token, }, request) => {
|
|
96
|
+
await send({
|
|
97
|
+
kind: "change-email",
|
|
98
|
+
to: user.email, // the CURRENT address — see AuthEmailMessage.to
|
|
99
|
+
subject: "Confirm your email change",
|
|
100
|
+
url,
|
|
101
|
+
token,
|
|
102
|
+
user,
|
|
103
|
+
newEmail,
|
|
104
|
+
request,
|
|
105
|
+
});
|
|
62
106
|
},
|
|
63
107
|
});
|
|
108
|
+
const _pinEmailSenders = (send) => {
|
|
109
|
+
const senders = makeEmailSenders(send);
|
|
110
|
+
return {
|
|
111
|
+
reset: senders.sendResetPassword,
|
|
112
|
+
verify: senders.sendVerificationEmail,
|
|
113
|
+
change: senders.sendChangeEmailConfirmation,
|
|
114
|
+
};
|
|
115
|
+
};
|
|
116
|
+
void _pinEmailSenders;
|
|
64
117
|
//# sourceMappingURL=options.js.map
|
package/dist/options.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"options.js","sourceRoot":"","sources":["../src/options.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"options.js","sourceRoot":"","sources":["../src/options.ts"],"names":[],"mappings":"AAEA,OAAO,MAAM,MAAM,QAAQ,CAAC;AAE5B,iFAAiF;AACjF,kFAAkF;AAClF,iEAAiE;AACjE,OAAO,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAEzC;;;;;;;;GAQG;AAEH;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG;IAC7B,IAAI,EAAE,CAAC,QAAgB,EAAmB,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,EAAE,CAAC;IACtE,MAAM,EAAE,CAAC,EACR,IAAI,EACJ,QAAQ,GAIR,EAAoB,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,QAAQ,EAAE,IAAI,CAAC;CACtD,CAAC;AAWF,iFAAiF;AACjF,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,GAAkB,EAAkB,EAAE,CAAC,CAAC;IAC1E,IAAI,EAAE,GAAG,CAAC,IAAI;IACd,MAAM,EAAE,GAAG,CAAC,MAAM;IAClB,MAAM,EAAE,GAAG,CAAC,MAAM;CAClB,CAAC,CAAC;AAEH;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG,CACvC,OAAe,EACf,MAAc,EACG,EAAE,CACnB,kBAAkB,CAAC;IAClB,IAAI,EAAE,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC,QAAQ;IAC/B,MAAM;IACN,MAAM,EAAE,OAAO;CACf,CAAC,CAAC;AA6CJ;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,IAAe,EAAE,EAAE,CAAC,CAAC;IACrD,iBAAiB,EAAE,KAAK,EACvB,EAAE,IAAI,EAAE,GAAG,EAAE,KAAK,EAAuD,EACzE,OAAiB,EACD,EAAE;QAClB,MAAM,IAAI,CAAC;YACV,IAAI,EAAE,gBAAgB;YACtB,EAAE,EAAE,IAAI,CAAC,KAAK;YACd,OAAO,EAAE,qBAAqB;YAC9B,GAAG;YACH,KAAK;YACL,IAAI;YACJ,OAAO;SACP,CAAC,CAAC;IACJ,CAAC;IACD,qBAAqB,EAAE,KAAK,EAC3B,EAAE,IAAI,EAAE,GAAG,EAAE,KAAK,EAAuD,EACzE,OAAiB,EACD,EAAE;QAClB,MAAM,IAAI,CAAC;YACV,IAAI,EAAE,cAAc;YACpB,EAAE,EAAE,IAAI,CAAC,KAAK;YACd,OAAO,EAAE,mBAAmB;YAC5B,GAAG;YACH,KAAK;YACL,IAAI;YACJ,OAAO;SACP,CAAC,CAAC;IACJ,CAAC;IACD,2BAA2B,EAAE,KAAK,EACjC,EACC,IAAI,EACJ,QAAQ,EACR,GAAG,EACH,KAAK,GAML,EACD,OAAiB,EACD,EAAE;QAClB,MAAM,IAAI,CAAC;YACV,IAAI,EAAE,cAAc;YACpB,EAAE,EAAE,IAAI,CAAC,KAAK,EAAE,gDAAgD;YAChE,OAAO,EAAE,2BAA2B;YACpC,GAAG;YACH,KAAK;YACL,IAAI;YACJ,QAAQ;YACR,OAAO;SACP,CAAC,CAAC;IACJ,CAAC;CACD,CAAC,CAAC;AAgCH,MAAM,gBAAgB,GAAG,CAAC,IAAe,EAAsB,EAAE;IAChE,MAAM,OAAO,GAAG,gBAAgB,CAAC,IAAI,CAAC,CAAC;IACvC,OAAO;QACN,KAAK,EAAE,OAAO,CAAC,iBAAiB;QAChC,MAAM,EAAE,OAAO,CAAC,qBAAqB;QACrC,MAAM,EAAE,OAAO,CAAC,2BAA2B;KAC3C,CAAC;AACH,CAAC,CAAC;AACF,KAAK,gBAAgB,CAAC"}
|
|
@@ -1,11 +1,20 @@
|
|
|
1
1
|
-- @ingram-tech/nk-auth — Better Auth schema, hardened for RLS.
|
|
2
2
|
--
|
|
3
|
-
-- This
|
|
4
|
-
--
|
|
5
|
-
-- `
|
|
6
|
-
--
|
|
3
|
+
-- This is the BASELINE (0001) of nk-auth's own migration chain — an append-only
|
|
4
|
+
-- set of SQL files this package ships and versions, journaled independently of a
|
|
5
|
+
-- consuming site's `drizzle/` chain (see docs/db-package.md § "The nk-auth
|
|
6
|
+
-- migration chain"). It mirrors Better Auth's core tables (user / session /
|
|
7
|
+
-- account / verification), the `jwt` plugin's `jwks` table, and the `passkey`
|
|
8
|
+
-- plugin's `passkey` table, for the version of `better-auth` this package pins.
|
|
9
|
+
--
|
|
10
|
+
-- Upgrading better-auth: this file is APPEND-ONLY. Once shipped it must never be
|
|
11
|
+
-- rewritten — the runner hashes each file and a changed baseline drifts every
|
|
12
|
+
-- site that already applied it. When a better-auth upgrade changes the schema,
|
|
13
|
+
-- diff it against the pinned version with:
|
|
7
14
|
-- npx @better-auth/cli generate
|
|
8
|
-
-- and
|
|
15
|
+
-- and land the delta as a NEW file (0002_*.sql, …) plus a `meta/_journal.json`
|
|
16
|
+
-- entry, written as idempotent `... if not exists` DDL and re-applying the two
|
|
17
|
+
-- hardening steps below to any new table. Never edit 0001.
|
|
9
18
|
--
|
|
10
19
|
-- TWO hardening steps the generator does NOT produce, both required:
|
|
11
20
|
-- 1. New users default to a UUID id, because `auth.uid()`-style RLS policies
|
|
@@ -16,6 +25,13 @@
|
|
|
16
25
|
-- which bypasses RLS, so denying the app's RLS role any access here costs
|
|
17
26
|
-- nothing and keeps the auth tables off-limits to user-facing queries.
|
|
18
27
|
|
|
28
|
+
-- Statements below are separated by drizzle's per-statement breakpoint marker
|
|
29
|
+
-- (a plain SQL line comment). It is REQUIRED: the PGlite dev/test migrator parses
|
|
30
|
+
-- one statement per command and rejects a multi-statement string, so the runner
|
|
31
|
+
-- splits the file on that marker. Keep one statement per segment when appending
|
|
32
|
+
-- deltas — and never write the literal marker token inside prose like this, or
|
|
33
|
+
-- the splitter will cut here.
|
|
34
|
+
|
|
19
35
|
create table if not exists "public"."user" (
|
|
20
36
|
"id" text primary key default gen_random_uuid()::text, -- hardening (1)
|
|
21
37
|
"name" text not null,
|
|
@@ -25,7 +41,7 @@ create table if not exists "public"."user" (
|
|
|
25
41
|
"createdAt" timestamptz not null default now(),
|
|
26
42
|
"updatedAt" timestamptz not null default now()
|
|
27
43
|
);
|
|
28
|
-
|
|
44
|
+
--> statement-breakpoint
|
|
29
45
|
create table if not exists "public"."session" (
|
|
30
46
|
"id" text primary key,
|
|
31
47
|
"expiresAt" timestamptz not null,
|
|
@@ -36,7 +52,7 @@ create table if not exists "public"."session" (
|
|
|
36
52
|
"createdAt" timestamptz not null default now(),
|
|
37
53
|
"updatedAt" timestamptz not null default now()
|
|
38
54
|
);
|
|
39
|
-
|
|
55
|
+
--> statement-breakpoint
|
|
40
56
|
create table if not exists "public"."account" (
|
|
41
57
|
"id" text primary key,
|
|
42
58
|
"accountId" text not null,
|
|
@@ -52,7 +68,7 @@ create table if not exists "public"."account" (
|
|
|
52
68
|
"createdAt" timestamptz not null default now(),
|
|
53
69
|
"updatedAt" timestamptz not null default now()
|
|
54
70
|
);
|
|
55
|
-
|
|
71
|
+
--> statement-breakpoint
|
|
56
72
|
create table if not exists "public"."verification" (
|
|
57
73
|
"id" text primary key,
|
|
58
74
|
"identifier" text not null,
|
|
@@ -61,7 +77,7 @@ create table if not exists "public"."verification" (
|
|
|
61
77
|
"createdAt" timestamptz not null default now(),
|
|
62
78
|
"updatedAt" timestamptz not null default now()
|
|
63
79
|
);
|
|
64
|
-
|
|
80
|
+
--> statement-breakpoint
|
|
65
81
|
-- `jwt` plugin: holds the asymmetric keypair used to sign session JWTs.
|
|
66
82
|
create table if not exists "public"."jwks" (
|
|
67
83
|
"id" text primary key,
|
|
@@ -69,7 +85,7 @@ create table if not exists "public"."jwks" (
|
|
|
69
85
|
"privateKey" text not null,
|
|
70
86
|
"createdAt" timestamptz not null default now()
|
|
71
87
|
);
|
|
72
|
-
|
|
88
|
+
--> statement-breakpoint
|
|
73
89
|
-- `passkey` plugin.
|
|
74
90
|
create table if not exists "public"."passkey" (
|
|
75
91
|
"id" text primary key,
|
|
@@ -84,17 +100,25 @@ create table if not exists "public"."passkey" (
|
|
|
84
100
|
"aaguid" text,
|
|
85
101
|
"createdAt" timestamptz default now()
|
|
86
102
|
);
|
|
87
|
-
|
|
103
|
+
--> statement-breakpoint
|
|
88
104
|
create index if not exists "idx_session_userId" on "public"."session" ("userId");
|
|
105
|
+
--> statement-breakpoint
|
|
89
106
|
create index if not exists "idx_account_userId" on "public"."account" ("userId");
|
|
107
|
+
--> statement-breakpoint
|
|
90
108
|
create index if not exists "idx_passkey_userId" on "public"."passkey" ("userId");
|
|
109
|
+
--> statement-breakpoint
|
|
91
110
|
create index if not exists "idx_verification_identifier" on "public"."verification" ("identifier");
|
|
92
|
-
|
|
111
|
+
--> statement-breakpoint
|
|
93
112
|
-- Hardening (2): deny-all RLS. No policies = no anon/authenticated access.
|
|
94
113
|
-- Better Auth's privileged connection bypasses RLS, so auth still works.
|
|
95
114
|
alter table "public"."user" enable row level security;
|
|
115
|
+
--> statement-breakpoint
|
|
96
116
|
alter table "public"."session" enable row level security;
|
|
117
|
+
--> statement-breakpoint
|
|
97
118
|
alter table "public"."account" enable row level security;
|
|
119
|
+
--> statement-breakpoint
|
|
98
120
|
alter table "public"."verification" enable row level security;
|
|
121
|
+
--> statement-breakpoint
|
|
99
122
|
alter table "public"."jwks" enable row level security;
|
|
123
|
+
--> statement-breakpoint
|
|
100
124
|
alter table "public"."passkey" enable row level security;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ingram-tech/nk-auth",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.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,12 +60,12 @@
|
|
|
60
60
|
"./migrations/*": "./migrations/*"
|
|
61
61
|
},
|
|
62
62
|
"scripts": {
|
|
63
|
-
"build": "rm -rf dist
|
|
63
|
+
"build": "rm -rf dist *.tsbuildinfo && tsc -p tsconfig.build.json",
|
|
64
64
|
"type-check": "tsc -p tsconfig.json --noEmit",
|
|
65
65
|
"test": "vitest run"
|
|
66
66
|
},
|
|
67
67
|
"dependencies": {
|
|
68
|
-
"@ingram-tech/nk-db": "^1.
|
|
68
|
+
"@ingram-tech/nk-db": "^1.4.2",
|
|
69
69
|
"bcrypt": "^6.0.0",
|
|
70
70
|
"jose": "^6.2.3",
|
|
71
71
|
"zod": "^4.4.3"
|
|
@@ -93,7 +93,7 @@
|
|
|
93
93
|
},
|
|
94
94
|
"devDependencies": {
|
|
95
95
|
"@better-auth/passkey": "^1.6.23",
|
|
96
|
-
"@ingram-tech/nk-dev": "0.
|
|
96
|
+
"@ingram-tech/nk-dev": "0.8.0",
|
|
97
97
|
"@types/bcrypt": "^6.0.0",
|
|
98
98
|
"@types/node": "^26.1.1",
|
|
99
99
|
"@types/pg": "^8.20.0",
|