@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.
Files changed (2) hide show
  1. package/README.md +36 -42
  2. package/package.json +3 -3
package/README.md CHANGED
@@ -1,32 +1,28 @@
1
1
  # @ingram-tech/nk-auth
2
2
 
3
- The Ingram **[Better Auth](https://better-auth.com) foundation**: a toolkit of
4
- composable presets each Ingram Next.js site spreads into its *own*
5
- `betterAuth()` call. It is **not** a `betterAuth()` wrapper — the site stays
6
- plain Better Auth (prime directive), keeping full plugin type inference. Import
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 **peer dependencies** so there's
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 sites that derive their own `baseURL` / DB connection |
15
- | `backendJwtOptions` / `verifyBackendJwt` (`./jwt`) | a JWT for the site's own backend API (custom `audience`; `ids` mints `sub` as the public user id) |
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 the site's shared pool instead |
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 sites with pre-existing bcrypt hashes. New sites omit it (Better Auth defaults to scrypt). See [Migrating bcrypt passwords to scrypt](#migrating-bcrypt-passwords-to-scrypt) |
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): query over a
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 the
29
- > site's shared nk-db pool, `backendJwtOptions({ audience })`,
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 site derives its own `baseURL` and opens its own database connection, it
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 site: a
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 nextkit-maintainer
123
- task, done once here — not per site).
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 docs told sites to `cp` the
126
- > baseline into their own `drizzle/` chain. If you already did, keep that file
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
- This package is **not** a `betterAuth()` wrapper — your site calls `betterAuth`
135
- itself and spreads in our presets. That keeps full Better Auth type inference at
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
- // matching the Ingram Cloud API's `agt_`/`smt_` ids — skin them with
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 sites. All from `@ingram-tech/nk-auth`.
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 sites carrying
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`) — 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 default `notifications` local
238
- part (`fromAddress("Example")`) — **not** `no-reply`: this is the mail users are
239
- most likely to reply to, and dropping that reply is hostile. See
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 (full plugin inference is
278
- preserved here too):
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
- Sites that skip middleware still get validated gating from the server helpers,
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
- now closes the loop so a site never touches Better Auth's `account` table or
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 sites whose `account.password` hashes are bcrypt keep verifying —
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 a site fully onto scrypt is a **dual-format verifier**, wired
485
- in the site (nk-auth does not ship it). Better Auth's default hasher is scrypt
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.0",
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.0",
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.12.2",
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",