@ingram-tech/nk-auth 0.19.0 → 0.19.2

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 CHANGED
@@ -159,6 +159,20 @@ database applies 0002 and 0003 back to back, and where 0002 refuses a provider,
159
159
  set `issuer` to any placeholder: the value is never read again. Dropping the
160
160
  column is a later, optional delta, possible once no 1.7.2 site remains.
161
161
 
162
+ **From 1.7.5, Better Auth checks the schema before every endpoint.** The first
163
+ call into any endpoint, `getSession` included, compares the database with what
164
+ the configuration writes, and while it fails every auth call throws "Database
165
+ schema mismatch". It fails on a missing table or column, and also on a column
166
+ Better Auth does not write that is NOT NULL with no default. So a site that
167
+ owns its auth tables in its own chain (not this shipped one) must make sure no
168
+ such column survives: a 0002-style NOT NULL `account.issuer` with no 0003-style
169
+ relax breaks sign-in outright, not just sign-up. The check runs once per
170
+ process and caches a clean result. It needs a database: a unit test that
171
+ reaches a real `getSession` without one now throws instead of returning
172
+ `null`, so mock the session seam there. `advanced.database.validateSchema:
173
+ false` turns the check off, but it is the guard that turns this drift into a
174
+ loud error instead of failed inserts, so keep it on in production.
175
+
162
176
  > **Adopting from the old copy-in model?** Earlier versions told you to `cp` the
163
177
  > baseline into your own `drizzle/` chain. If you already did, keep that file
164
178
  > (deleting an applied migration causes journal drift) and just add the auth-chain
@@ -237,7 +251,7 @@ export const auth = betterAuth({
237
251
  ```
238
252
 
239
253
  ```ts
240
- // app/auth/[...all]/route.ts — a standard Next.js route handler.
254
+ // src/app/auth/[...all]/route.ts — a standard Next.js route handler.
241
255
  // Lives at /auth (set via `basePath: authBasePath`), NOT /api/auth: auth is a
242
256
  // user-facing surface (sign-in, OAuth callbacks), not an internal machine API.
243
257
  import { toNextJsHandler } from "better-auth/next-js";
@@ -367,7 +381,7 @@ JWT's `sub` (and payload `id`) is the public id too. RLS needs nothing: nk-db's
367
381
  `auth.uid()` policies hold either way.
368
382
 
369
383
  ```tsx
370
- // app/dashboard/page.tsx — gate a protected page (validated, DB-backed).
384
+ // src/app/dashboard/page.tsx — gate a protected page (validated, DB-backed).
371
385
  import { requireUser } from "@/lib/auth/session";
372
386
  export default async function Dashboard() {
373
387
  // -> /login?next=/dashboard when signed out (and ?stale=1 when the cookie is
@@ -376,7 +390,7 @@ export default async function Dashboard() {
376
390
  return <main>Hi {user.email}</main>;
377
391
  }
378
392
 
379
- // app/login/page.tsx — gate the sign-in page HERE, never in middleware. Honor
393
+ // src/app/login/page.tsx — gate the sign-in page HERE, never in middleware. Honor
380
394
  // `next` so sign-in returns the user to where they were headed.
381
395
  import { redirectIfAuthenticated } from "@/lib/auth/session";
382
396
  import { safeNext } from "@ingram-tech/nk-auth/server";
@@ -442,7 +456,7 @@ cookie). To still end a sign-in eventually, add `sessionLifetime({ maxAge })`
442
456
  to the instance's `plugins`: refreshes are clamped to `createdAt + maxAge`.
443
457
 
444
458
  ```tsx
445
- // app/(app)/SessionRenewal.tsx
459
+ // src/app/(app)/SessionRenewal.tsx
446
460
  "use client";
447
461
  export const SessionRenewal = () => {
448
462
  authClient.useSession();
@@ -507,7 +521,7 @@ password credential until it sets one. `hasCredentialAccount()` drives the
507
521
  `getLinkedProviders()` when you need the full list.
508
522
 
509
523
  ```tsx
510
- // app/settings/security/page.tsx (server component)
524
+ // src/app/settings/security/page.tsx (server component)
511
525
  import { hasCredentialAccount } from "@/lib/auth/session";
512
526
  export default async function Security() {
513
527
  return (await hasCredentialAccount()) ? <ChangePassword /> : <SetPassword />;
@@ -1 +1 @@
1
- {"version":3,"file":"keys.d.ts","sourceRoot":"","sources":["../src/keys.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AA2CH;;;;;;;;;GASG;AACH,eAAO,MAAM,UAAU,QAAO,MAW7B,CAAC;AAEF,MAAM,WAAW,OAAO;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;CACpB;AAED;;;;GAIG;AACH,eAAO,MAAM,OAAO,QAAO,OAe1B,CAAC;AAEF,+EAA+E;AAC/E,eAAO,MAAM,YAAY,QAAO,OAAuD,CAAC"}
1
+ {"version":3,"file":"keys.d.ts","sourceRoot":"","sources":["../src/keys.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AA+CH;;;;;;;;;GASG;AACH,eAAO,MAAM,UAAU,QAAO,MAW7B,CAAC;AAEF,MAAM,WAAW,OAAO;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;CACpB;AAED;;;;GAIG;AACH,eAAO,MAAM,OAAO,QAAO,OAe1B,CAAC;AAEF,+EAA+E;AAC/E,eAAO,MAAM,YAAY,QAAO,OAAuD,CAAC"}
package/dist/keys.js CHANGED
@@ -29,12 +29,16 @@ import { z } from "zod";
29
29
  const DEV_SECRET_PLACEHOLDER = "nk-dev-insecure-placeholder-not-for-production";
30
30
  /**
31
31
  * In production the secret is required; everywhere else it defaults to the
32
- * insecure placeholder. Read NODE_ENV at parse time (not module load) so the
33
- * same process can be exercised under either mode.
32
+ * insecure placeholder. An empty value (a bare `BETTER_AUTH_SECRET=` line in a
33
+ * .env file) counts as unset. Read NODE_ENV at parse time (not module load) so
34
+ * the same process can be exercised under either mode.
34
35
  */
35
36
  const secretField = () => process.env.NODE_ENV === "production"
36
37
  ? z.string().min(1)
37
- : z.string().min(1).default(DEV_SECRET_PLACEHOLDER);
38
+ : z
39
+ .string()
40
+ .optional()
41
+ .transform((secret) => secret || DEV_SECRET_PLACEHOLDER);
38
42
  const buildSchema = () => z.object({
39
43
  BETTER_AUTH_SECRET: secretField(),
40
44
  BETTER_AUTH_URL: z.string().url(),
package/dist/keys.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"keys.js","sourceRoot":"","sources":["../src/keys.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;GAGG;AACH,MAAM,sBAAsB,GAAG,gDAAgD,CAAC;AAEhF;;;;GAIG;AACH,MAAM,WAAW,GAAG,GAAG,EAAE,CACxB,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,YAAY;IACpC,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IACnB,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,sBAAsB,CAAC,CAAC;AAEtD,MAAM,WAAW,GAAG,GAAG,EAAE,CACxB,CAAC,CAAC,MAAM,CAAC;IACR,kBAAkB,EAAE,WAAW,EAAE;IACjC,eAAe,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE;IACjC,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;CAC/B,CAAC,CAAC;AAEJ,IAAI,iBAAiB,GAAG,KAAK,CAAC;AAE9B;;;;GAIG;AACH,MAAM,iBAAiB,GAAG,CAAC,MAAc,EAAQ,EAAE;IAClD,IAAI,MAAM,KAAK,sBAAsB,IAAI,CAAC,iBAAiB,EAAE,CAAC;QAC7D,iBAAiB,GAAG,IAAI,CAAC;QACzB,OAAO,CAAC,IAAI,CACX,iHAAiH,CACjH,CAAC;IACH,CAAC;AACF,CAAC,CAAC;AAEF;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG,GAAW,EAAE;IACtC,MAAM,MAAM,GAAG,WAAW,EAAE,CAAC,SAAS,CAAC,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC;IACvE,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACrB,uEAAuE;QACvE,uEAAuE;QACvE,MAAM,IAAI,KAAK,CACd,2GAA2G,CAC3G,CAAC;IACH,CAAC;IACD,iBAAiB,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAC/B,OAAO,MAAM,CAAC,IAAI,CAAC;AACpB,CAAC,CAAC;AAQF;;;;GAIG;AACH,MAAM,CAAC,MAAM,OAAO,GAAG,GAAY,EAAE;IACpC,MAAM,MAAM,GAAG,WAAW,EAAE,CAAC,SAAS,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IACpD,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACrB,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM;aAChC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,KAAK,CAAC,OAAO,EAAE,CAAC;aAC3D,IAAI,CAAC,IAAI,CAAC,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,+CAA+C,MAAM,EAAE,CAAC,CAAC;IAC1E,CAAC;IACD,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC;IACxB,iBAAiB,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC;IAC1C,OAAO;QACN,MAAM,EAAE,GAAG,CAAC,kBAAkB;QAC9B,OAAO,EAAE,GAAG,CAAC,eAAe;QAC5B,WAAW,EAAE,GAAG,CAAC,YAAY;KAC7B,CAAC;AACH,CAAC,CAAC;AAEF,+EAA+E;AAC/E,MAAM,CAAC,MAAM,YAAY,GAAG,GAAY,EAAE,CAAC,WAAW,EAAE,CAAC,SAAS,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC"}
1
+ {"version":3,"file":"keys.js","sourceRoot":"","sources":["../src/keys.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;GAGG;AACH,MAAM,sBAAsB,GAAG,gDAAgD,CAAC;AAEhF;;;;;GAKG;AACH,MAAM,WAAW,GAAG,GAAG,EAAE,CACxB,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,YAAY;IACpC,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IACnB,CAAC,CAAC,CAAC;SACA,MAAM,EAAE;SACR,QAAQ,EAAE;SACV,SAAS,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,IAAI,sBAAsB,CAAC,CAAC;AAE7D,MAAM,WAAW,GAAG,GAAG,EAAE,CACxB,CAAC,CAAC,MAAM,CAAC;IACR,kBAAkB,EAAE,WAAW,EAAE;IACjC,eAAe,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE;IACjC,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;CAC/B,CAAC,CAAC;AAEJ,IAAI,iBAAiB,GAAG,KAAK,CAAC;AAE9B;;;;GAIG;AACH,MAAM,iBAAiB,GAAG,CAAC,MAAc,EAAQ,EAAE;IAClD,IAAI,MAAM,KAAK,sBAAsB,IAAI,CAAC,iBAAiB,EAAE,CAAC;QAC7D,iBAAiB,GAAG,IAAI,CAAC;QACzB,OAAO,CAAC,IAAI,CACX,iHAAiH,CACjH,CAAC;IACH,CAAC;AACF,CAAC,CAAC;AAEF;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG,GAAW,EAAE;IACtC,MAAM,MAAM,GAAG,WAAW,EAAE,CAAC,SAAS,CAAC,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC;IACvE,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACrB,uEAAuE;QACvE,uEAAuE;QACvE,MAAM,IAAI,KAAK,CACd,2GAA2G,CAC3G,CAAC;IACH,CAAC;IACD,iBAAiB,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAC/B,OAAO,MAAM,CAAC,IAAI,CAAC;AACpB,CAAC,CAAC;AAQF;;;;GAIG;AACH,MAAM,CAAC,MAAM,OAAO,GAAG,GAAY,EAAE;IACpC,MAAM,MAAM,GAAG,WAAW,EAAE,CAAC,SAAS,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IACpD,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACrB,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM;aAChC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,KAAK,CAAC,OAAO,EAAE,CAAC;aAC3D,IAAI,CAAC,IAAI,CAAC,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,+CAA+C,MAAM,EAAE,CAAC,CAAC;IAC1E,CAAC;IACD,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC;IACxB,iBAAiB,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC;IAC1C,OAAO;QACN,MAAM,EAAE,GAAG,CAAC,kBAAkB;QAC9B,OAAO,EAAE,GAAG,CAAC,eAAe;QAC5B,WAAW,EAAE,GAAG,CAAC,YAAY;KAC7B,CAAC;AACH,CAAC,CAAC;AAEF,+EAA+E;AAC/E,MAAM,CAAC,MAAM,YAAY,GAAG,GAAY,EAAE,CAAC,WAAW,EAAE,CAAC,SAAS,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC"}
package/dist/options.d.ts CHANGED
@@ -46,7 +46,9 @@ export declare const makePasskeyOptions: (cfg: PasskeyConfig) => PasskeyOptions;
46
46
  /**
47
47
  * Derive passkey options from the site's single base URL: `rpID` = the URL's
48
48
  * hostname (the WebAuthn effective domain — host only, no scheme or port) and
49
- * `origin` = the URL itself. This is the common case (one sign-in origin), and
49
+ * `origin` = the URL's origin (scheme, host and port; any path or trailing slash
50
+ * dropped, since the browser reports the bare origin and WebAuthn compares it
51
+ * exactly). This is the common case (one sign-in origin), and
50
52
  * it keeps the relying-party id and the registered origin in lockstep so they
51
53
  * can't drift. Pass the same value the instance signs sessions for, e.g.:
52
54
  *
@@ -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;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"}
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;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,wBAAwB,YAC3B,MAAM,UACP,MAAM,KACZ,cAGF,CAAC;AAEF;;;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
@@ -42,7 +42,9 @@ export const makePasskeyOptions = (cfg) => ({
42
42
  /**
43
43
  * Derive passkey options from the site's single base URL: `rpID` = the URL's
44
44
  * hostname (the WebAuthn effective domain — host only, no scheme or port) and
45
- * `origin` = the URL itself. This is the common case (one sign-in origin), and
45
+ * `origin` = the URL's origin (scheme, host and port; any path or trailing slash
46
+ * dropped, since the browser reports the bare origin and WebAuthn compares it
47
+ * exactly). This is the common case (one sign-in origin), and
46
48
  * it keeps the relying-party id and the registered origin in lockstep so they
47
49
  * can't drift. Pass the same value the instance signs sessions for, e.g.:
48
50
  *
@@ -52,11 +54,10 @@ export const makePasskeyOptions = (cfg) => ({
52
54
  * must register against a parent registrable domain (e.g. rpID "example.com"
53
55
  * for an "app.example.com" origin).
54
56
  */
55
- export const passkeyOptionsForBaseUrl = (baseURL, rpName) => makePasskeyOptions({
56
- rpId: new URL(baseURL).hostname,
57
- rpName,
58
- origin: baseURL,
59
- });
57
+ export const passkeyOptionsForBaseUrl = (baseURL, rpName) => {
58
+ const url = new URL(baseURL);
59
+ return makePasskeyOptions({ rpId: url.hostname, rpName, origin: url.origin });
60
+ };
60
61
  /**
61
62
  * Email callbacks for `emailAndPassword.sendResetPassword`,
62
63
  * `emailVerification.sendVerificationEmail` and
@@ -1 +1 @@
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
+ {"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;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG,CACvC,OAAe,EACf,MAAc,EACG,EAAE;IACnB,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC;IAC7B,OAAO,kBAAkB,CAAC,EAAE,IAAI,EAAE,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC;AAC/E,CAAC,CAAC;AA6CF;;;;;;;;;;;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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ingram-tech/nk-auth",
3
- "version": "0.19.0",
3
+ "version": "0.19.2",
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",
@@ -97,13 +97,13 @@
97
97
  },
98
98
  "devDependencies": {
99
99
  "@better-auth/passkey": "1.7.7",
100
- "@ingram-tech/nk-dev": "0.19.0",
100
+ "@ingram-tech/nk-dev": "0.20.0",
101
101
  "@types/bcrypt": "^6.0.0",
102
- "@types/node": "^26.6.4",
102
+ "@types/node": "^26.6.5",
103
103
  "@types/pg": "^8.23.1",
104
104
  "@types/react": "^19.3.0",
105
105
  "better-auth": "1.7.7",
106
- "next": "^16.3.8",
106
+ "next": "^16.4.0",
107
107
  "pg": "^8.23.1",
108
108
  "react": "^19.3.0",
109
109
  "typescript": "^7.0.2",
package/src/keys.ts CHANGED
@@ -32,13 +32,17 @@ const DEV_SECRET_PLACEHOLDER = "nk-dev-insecure-placeholder-not-for-production";
32
32
 
33
33
  /**
34
34
  * In production the secret is required; everywhere else it defaults to the
35
- * insecure placeholder. Read NODE_ENV at parse time (not module load) so the
36
- * same process can be exercised under either mode.
35
+ * insecure placeholder. An empty value (a bare `BETTER_AUTH_SECRET=` line in a
36
+ * .env file) counts as unset. Read NODE_ENV at parse time (not module load) so
37
+ * the same process can be exercised under either mode.
37
38
  */
38
39
  const secretField = () =>
39
40
  process.env.NODE_ENV === "production"
40
41
  ? z.string().min(1)
41
- : z.string().min(1).default(DEV_SECRET_PLACEHOLDER);
42
+ : z
43
+ .string()
44
+ .optional()
45
+ .transform((secret) => secret || DEV_SECRET_PLACEHOLDER);
42
46
 
43
47
  const buildSchema = () =>
44
48
  z.object({
package/src/options.ts CHANGED
@@ -64,7 +64,9 @@ export const makePasskeyOptions = (cfg: PasskeyConfig): PasskeyOptions => ({
64
64
  /**
65
65
  * Derive passkey options from the site's single base URL: `rpID` = the URL's
66
66
  * hostname (the WebAuthn effective domain — host only, no scheme or port) and
67
- * `origin` = the URL itself. This is the common case (one sign-in origin), and
67
+ * `origin` = the URL's origin (scheme, host and port; any path or trailing slash
68
+ * dropped, since the browser reports the bare origin and WebAuthn compares it
69
+ * exactly). This is the common case (one sign-in origin), and
68
70
  * it keeps the relying-party id and the registered origin in lockstep so they
69
71
  * can't drift. Pass the same value the instance signs sessions for, e.g.:
70
72
  *
@@ -77,12 +79,10 @@ export const makePasskeyOptions = (cfg: PasskeyConfig): PasskeyOptions => ({
77
79
  export const passkeyOptionsForBaseUrl = (
78
80
  baseURL: string,
79
81
  rpName: string,
80
- ): PasskeyOptions =>
81
- makePasskeyOptions({
82
- rpId: new URL(baseURL).hostname,
83
- rpName,
84
- origin: baseURL,
85
- });
82
+ ): PasskeyOptions => {
83
+ const url = new URL(baseURL);
84
+ return makePasskeyOptions({ rpId: url.hostname, rpName, origin: url.origin });
85
+ };
86
86
 
87
87
  /**
88
88
  * Which auth mail is being sent. Switch on this to pick a template — never on