@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 +19 -5
- package/dist/keys.d.ts.map +1 -1
- package/dist/keys.js +7 -3
- package/dist/keys.js.map +1 -1
- package/dist/options.d.ts +3 -1
- package/dist/options.d.ts.map +1 -1
- package/dist/options.js +7 -6
- package/dist/options.js.map +1 -1
- package/package.json +4 -4
- package/src/keys.ts +7 -3
- package/src/options.ts +7 -7
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 />;
|
package/dist/keys.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"keys.d.ts","sourceRoot":"","sources":["../src/keys.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;
|
|
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.
|
|
33
|
-
*
|
|
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
|
|
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
|
|
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
|
|
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
|
*
|
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;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
|
|
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
|
|
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) =>
|
|
56
|
-
|
|
57
|
-
rpName,
|
|
58
|
-
|
|
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
|
package/dist/options.js.map
CHANGED
|
@@ -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
|
|
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.
|
|
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.
|
|
100
|
+
"@ingram-tech/nk-dev": "0.20.0",
|
|
101
101
|
"@types/bcrypt": "^6.0.0",
|
|
102
|
-
"@types/node": "^26.6.
|
|
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.
|
|
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.
|
|
36
|
-
*
|
|
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
|
|
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
|
|
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
|
-
|
|
82
|
-
|
|
83
|
-
|
|
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
|