@ingram-tech/nk-auth 0.14.0 → 0.14.1
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 +36 -42
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -1,32 +1,28 @@
|
|
|
1
1
|
# @ingram-tech/nk-auth
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
only what you need from focused subpaths.
|
|
3
|
+
Composable [Better Auth](https://better-auth.com) presets for Next.js apps, spread
|
|
4
|
+
into your *own* `betterAuth()` call. This is not a `betterAuth()` wrapper: your app
|
|
5
|
+
stays plain Better Auth, so plugin type inference survives. Import only what you
|
|
6
|
+
need from focused subpaths.
|
|
8
7
|
|
|
9
|
-
`better-auth`, `pg`, `@better-auth/passkey` are
|
|
8
|
+
`better-auth`, `pg`, `@better-auth/passkey` are peer dependencies, so there is
|
|
10
9
|
exactly one Better Auth copy in the app.
|
|
11
10
|
|
|
12
11
|
| Export (subpath) | For |
|
|
13
12
|
| --- | --- |
|
|
14
|
-
| `authEnv`, `authSecret`, `isConfigured` (`./`) | env validation — `authEnv()` returns the full `{ secret, baseURL, databaseUrl }` bundle; `authSecret()` resolves just the secret (with the prod/dev rule) for
|
|
15
|
-
| `backendJwtOptions` / `verifyBackendJwt` (`./jwt`) | a JWT for
|
|
13
|
+
| `authEnv`, `authSecret`, `isConfigured` (`./`) | env validation — `authEnv()` returns the full `{ secret, baseURL, databaseUrl }` bundle; `authSecret()` resolves just the secret (with the prod/dev rule) for apps that derive their own `baseURL` / DB connection |
|
|
14
|
+
| `backendJwtOptions` / `verifyBackendJwt` (`./jwt`) | a JWT for your own backend API (custom `audience`; `ids` mints `sub` as the public user id) |
|
|
16
15
|
| `nkOrganizationDefaults`, `lastActiveOrganizationHooks`, `lastActiveOrganizationUserField` (`./organization`) | org-plugin defaults + active-org restore/persist |
|
|
17
|
-
| `createAuthPool` (`./pool`) | **deprecated** — alias of `createPool` from [`@ingram-tech/nk-db`](../nk-db); inject
|
|
16
|
+
| `createAuthPool` (`./pool`) | **deprecated** — alias of `createPool` from [`@ingram-tech/nk-db`](../nk-db); inject your app's shared pool instead |
|
|
18
17
|
| `makeEmailSenders`, `makePasskeyOptions`, `passkeyOptionsForBaseUrl`, `uuidGenerateId` (`./`) | email hooks, passkeys (`passkeyOptionsForBaseUrl` derives `rpID`/`origin` from a single base URL), UUID ids |
|
|
19
|
-
| `bcryptPassword` (`./`) | **legacy only** — bcrypt verifier for
|
|
18
|
+
| `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) |
|
|
20
19
|
| `createAuthHelpers`, `safeNext` (`./server`) | validated App Router session helpers (`getSession` / `getUser` / `requireSession` / `requireUser` / `redirectIfAuthenticated`), request-memoized via React `cache()`, with automatic `next` + stale-cookie signalling; `safeNext` validates a `?next=` param |
|
|
21
20
|
| `createAuthMiddleware` (`./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 |
|
|
22
21
|
|
|
23
|
-
> **Data access + RLS** is owned by [`@ingram-tech/nk-db`](../nk-db)
|
|
24
|
-
> direct `pg` connection and enforce per-request Row Level Security with
|
|
25
|
-
> `withRls` / `withRlsTransaction` (claims taken from the Better Auth session — no
|
|
26
|
-
> JWT minting, no REST proxy).
|
|
22
|
+
> **Data access + RLS** is owned by [`@ingram-tech/nk-db`](../nk-db); see §3.
|
|
27
23
|
>
|
|
28
|
-
> **Backend-JWT + org sites** (a backend API plus the org plugin): compose
|
|
29
|
-
>
|
|
24
|
+
> **Backend-JWT + org sites** (a backend API plus the org plugin): compose your
|
|
25
|
+
> app's shared nk-db pool, `backendJwtOptions({ audience })`,
|
|
30
26
|
> `nkOrganizationDefaults`, and `lastActiveOrganizationHooks(pool)` in your
|
|
31
27
|
> `betterAuth()`; verify backend tokens with `verifyBackendJwt`. Keep
|
|
32
28
|
> app-specific bits (SSO restrictions, permissions/roles, connectors) in the app.
|
|
@@ -49,7 +45,7 @@ Outside production, `BETTER_AUTH_SECRET` falls back to a well-known insecure
|
|
|
49
45
|
placeholder, so local dev and tests run without setting it (a warning is logged).
|
|
50
46
|
In production it stays required — a missing secret throws at startup.
|
|
51
47
|
|
|
52
|
-
If your
|
|
48
|
+
If your app derives its own `baseURL` and opens its own database connection, it
|
|
53
49
|
may not want `authEnv()`'s all-or-nothing bundle (which also requires
|
|
54
50
|
`BETTER_AUTH_URL` and `DATABASE_URL`). Take just the secret — same prod/dev rule —
|
|
55
51
|
with `authSecret()`:
|
|
@@ -116,14 +112,14 @@ const db = await createTestDb({
|
|
|
116
112
|
});
|
|
117
113
|
```
|
|
118
114
|
|
|
119
|
-
**Upgrading better-auth** never means hand-writing a migration in your
|
|
115
|
+
**Upgrading better-auth** never means hand-writing a migration in your app: a
|
|
120
116
|
schema-changing upgrade ships as a new file in nk-auth's chain, so you bump the
|
|
121
117
|
dependency and your next migrate applies it. Reconcile the shipped schema against
|
|
122
|
-
a pinned `better-auth` with `npx @better-auth/cli generate` (a
|
|
123
|
-
|
|
118
|
+
a pinned `better-auth` with `npx @better-auth/cli generate` (a maintainer task,
|
|
119
|
+
done once in this package).
|
|
124
120
|
|
|
125
|
-
> **Adopting from the old copy-in model?** Earlier
|
|
126
|
-
> baseline into
|
|
121
|
+
> **Adopting from the old copy-in model?** Earlier versions told you to `cp` the
|
|
122
|
+
> baseline into your own `drizzle/` chain. If you already did, keep that file
|
|
127
123
|
> (deleting an applied migration causes journal drift) and just add the auth-chain
|
|
128
124
|
> invocation above: its DDL is all `… if not exists`, so it no-ops against your
|
|
129
125
|
> existing tables and simply records the nk-auth journal. New auth migrations then
|
|
@@ -131,10 +127,8 @@ task, done once here — not per site).
|
|
|
131
127
|
|
|
132
128
|
## 2. Configure the server
|
|
133
129
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
the call site (so `auth.api.*` stays typed) and keeps the site plain Better Auth,
|
|
137
|
-
per the prime directive.
|
|
130
|
+
Your app calls `betterAuth` itself and spreads in the presets, which keeps full
|
|
131
|
+
Better Auth type inference at the call site, so `auth.api.*` stays typed.
|
|
138
132
|
|
|
139
133
|
```ts
|
|
140
134
|
// lib/auth.ts
|
|
@@ -166,13 +160,13 @@ export const auth = betterAuth({
|
|
|
166
160
|
basePath: authBasePath, // mount at /auth, not the framework default /api/auth
|
|
167
161
|
advanced: { database: { generateId: uuidGenerateId } }, // UUIDv7 ids
|
|
168
162
|
// ^ stored as hyphenated UUIDv7 (uuid columns / RLS policies stay valid).
|
|
169
|
-
// To show those same ids as prefixed base58 on the wire/UI — `team_…`,
|
|
170
|
-
//
|
|
163
|
+
// To show those same ids as prefixed base58 on the wire/UI — `team_…`, the
|
|
164
|
+
// way an API exposes prefixed ids like `agt_`/`smt_` — skin them with
|
|
171
165
|
// `toPrefixedId(uuid, "team")` / recover with `fromPrefixedId`. `base58Id`
|
|
172
|
-
// mints a fresh one directly for text-id
|
|
166
|
+
// mints a fresh one directly for text-id apps. All from `@ingram-tech/nk-auth`.
|
|
173
167
|
emailAndPassword: {
|
|
174
168
|
enabled: true,
|
|
175
|
-
// Better Auth hashes with scrypt by default. Only
|
|
169
|
+
// Better Auth hashes with scrypt by default. Only apps carrying
|
|
176
170
|
// pre-existing bcrypt hashes set `password: bcryptPassword` (legacy).
|
|
177
171
|
sendResetPassword: email.sendResetPassword,
|
|
178
172
|
},
|
|
@@ -232,11 +226,11 @@ has any stored preference.
|
|
|
232
226
|
mail a user ever gets from you; a bare `<a href=url>url</a>` reads as phishing
|
|
233
227
|
and trains people to distrust your domain. Take the
|
|
234
228
|
[`registry`](https://github.com/ingram-technologies/registry) email components
|
|
235
|
-
(`shadcn add email-verification email-password-reset`)
|
|
236
|
-
`body` / `ctaLabel` / `preview` overrides
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
229
|
+
(`shadcn add email-verification email-password-reset`), which accept `heading` /
|
|
230
|
+
`body` / `ctaLabel` / `preview` overrides so you can pass translated copy, or
|
|
231
|
+
write your own. Send auth links from the default `notifications` local part
|
|
232
|
+
(`fromAddress("Example")`), **not** `no-reply`: this is the mail users are most
|
|
233
|
+
likely to reply to, and dropping that reply is hostile. See
|
|
240
234
|
[transactional-email.md](../../docs/transactional-email.md).
|
|
241
235
|
|
|
242
236
|
**Spread all three in; do not hand-write them.** `betterAuth()` takes its
|
|
@@ -274,8 +268,8 @@ claims GUC, so a policy's `auth.uid()` still compares to a `uuid` column.
|
|
|
274
268
|
|
|
275
269
|
## 4. Client
|
|
276
270
|
|
|
277
|
-
Assemble the client in a `"use client"` module (
|
|
278
|
-
|
|
271
|
+
Assemble the client in a `"use client"` module (plugin inference is preserved
|
|
272
|
+
here too):
|
|
279
273
|
|
|
280
274
|
```tsx
|
|
281
275
|
"use client";
|
|
@@ -394,13 +388,13 @@ marker and bounces to a clean `/login?next=…`; signing in returns them to
|
|
|
394
388
|
`next`. `next` for the cookie-less case is filled in by middleware directly; for
|
|
395
389
|
the cookie-present case the guard reads it from the `x-nk-auth-path` header
|
|
396
390
|
middleware injects — so the self-heal (next + clearing) needs the middleware.
|
|
397
|
-
|
|
391
|
+
An app that skips middleware still gets validated gating from the server helpers,
|
|
398
392
|
just without automatic `next`/clearing.
|
|
399
393
|
|
|
400
394
|
## 6. Passwords: change, set, and reset
|
|
401
395
|
|
|
402
396
|
nk-auth owns the reset **sender** (`makeEmailSenders.sendResetPassword`, §2) and
|
|
403
|
-
|
|
397
|
+
closes the loop so your app never touches Better Auth's `account` table or
|
|
404
398
|
endpoint names directly. Three pieces:
|
|
405
399
|
|
|
406
400
|
**Detect what login the user has.** A social-only account (Google, …) has no
|
|
@@ -478,11 +472,11 @@ async redirects() {
|
|
|
478
472
|
## Migrating bcrypt passwords to scrypt
|
|
479
473
|
|
|
480
474
|
`bcryptPassword` is **legacy support only** (see its `@deprecated` note). It
|
|
481
|
-
exists so
|
|
475
|
+
exists so apps whose `account.password` hashes are bcrypt keep verifying:
|
|
482
476
|
that is all nk-auth ships for the bcrypt case.
|
|
483
477
|
|
|
484
|
-
The path to move
|
|
485
|
-
|
|
478
|
+
The path to move fully onto scrypt is a **dual-format verifier**, wired in your
|
|
479
|
+
app (nk-auth does not ship it). Better Auth's default hasher is scrypt
|
|
486
480
|
(`<salt-hex>:<key-hex>`) and bcrypt hashes are trivially distinguishable (they
|
|
487
481
|
start with `$2a$`/`$2b$`/`$2y$`), so override only
|
|
488
482
|
`emailAndPassword.password.verify` to branch on `hash.startsWith("$2")` →
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ingram-tech/nk-auth",
|
|
3
|
-
"version": "0.14.
|
|
3
|
+
"version": "0.14.1",
|
|
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",
|
|
@@ -68,7 +68,7 @@
|
|
|
68
68
|
"test": "vitest run"
|
|
69
69
|
},
|
|
70
70
|
"dependencies": {
|
|
71
|
-
"@ingram-tech/nk-db": "^2.1.
|
|
71
|
+
"@ingram-tech/nk-db": "^2.1.1",
|
|
72
72
|
"bcrypt": "^6.0.0",
|
|
73
73
|
"jose": "^6.2.9",
|
|
74
74
|
"zod": "^4.4.3"
|
|
@@ -96,7 +96,7 @@
|
|
|
96
96
|
},
|
|
97
97
|
"devDependencies": {
|
|
98
98
|
"@better-auth/passkey": "^1.7.0",
|
|
99
|
-
"@ingram-tech/nk-dev": "0.
|
|
99
|
+
"@ingram-tech/nk-dev": "0.13.1",
|
|
100
100
|
"@types/bcrypt": "^6.0.0",
|
|
101
101
|
"@types/node": "^26.2.0",
|
|
102
102
|
"@types/pg": "^8.23.1",
|