@ingram-tech/nk-auth 0.7.5 → 0.8.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 +22 -55
- package/dist/index.d.ts +1 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -3
- package/dist/index.js.map +1 -1
- package/dist/jwt.d.ts +4 -12
- package/dist/jwt.d.ts.map +1 -1
- package/dist/jwt.js +0 -23
- package/dist/jwt.js.map +1 -1
- package/dist/keys.d.ts +4 -9
- package/dist/keys.d.ts.map +1 -1
- package/dist/keys.js +4 -11
- package/dist/keys.js.map +1 -1
- package/dist/options.d.ts +1 -1
- package/dist/options.d.ts.map +1 -1
- package/package.json +3 -3
- package/dist/supabase.d.ts +0 -26
- package/dist/supabase.d.ts.map +0 -1
- package/dist/supabase.js +0 -8
- package/dist/supabase.js.map +0 -1
package/README.md
CHANGED
|
@@ -6,23 +6,22 @@ composable presets each Ingram Next.js site spreads into its *own*
|
|
|
6
6
|
plain Better Auth (prime directive), keeping full plugin type inference. Import
|
|
7
7
|
only what you need from focused subpaths.
|
|
8
8
|
|
|
9
|
-
`better-auth`, `pg`, `@better-auth/passkey
|
|
10
|
-
|
|
9
|
+
`better-auth`, `pg`, `@better-auth/passkey` are **peer dependencies** so there's
|
|
10
|
+
exactly one Better Auth copy in the app.
|
|
11
11
|
|
|
12
12
|
| Export (subpath) | For |
|
|
13
13
|
| --- | --- |
|
|
14
|
-
| `rlsJwtOptions` (`./jwt`) | Supabase RLS bridge token (`role:"authenticated"`) |
|
|
15
14
|
| `backendJwtOptions` / `verifyBackendJwt` (`./jwt`) | a JWT for the site's own backend API (custom `audience`) |
|
|
16
15
|
| `nkOrganizationDefaults`, `lastActiveOrganizationHooks`, `lastActiveOrganizationUserField` (`./organization`) | org-plugin defaults + active-org restore/persist |
|
|
17
16
|
| `createAuthPool` (`./pool`) | `pg` Pool with optional SSL CA verification |
|
|
18
|
-
| `bcryptPassword`, `makeEmailSenders`, `makePasskeyOptions`, `uuidGenerateId` (`./`) | password
|
|
19
|
-
| `createServerSupabase` (`./`) | RLS-aware supabase-js client (attaches the session JWT) |
|
|
17
|
+
| `bcryptPassword`, `makeEmailSenders`, `makePasskeyOptions`, `uuidGenerateId` (`./`) | password verification, email hooks, passkeys, UUID ids |
|
|
20
18
|
| `createAuthHelpers`, `safeNext` (`./server`) | validated App Router session helpers (`getSession` / `getUser` / `requireSession` / `requireUser` / `redirectIfAuthenticated`) with automatic `next` + stale-cookie signalling; `safeNext` validates a `?next=` param |
|
|
21
19
|
| `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
20
|
|
|
23
|
-
> **
|
|
24
|
-
>
|
|
25
|
-
>
|
|
21
|
+
> **Data access + RLS** is owned by [`@ingram-tech/nk-db`](../nk-db): query over a
|
|
22
|
+
> direct `pg` connection and enforce per-request Row Level Security with
|
|
23
|
+
> `withRls` / `withRlsTransaction` (claims taken from the Better Auth session — no
|
|
24
|
+
> JWT minting, no PostgREST). nk-auth no longer ships a Supabase data client.
|
|
26
25
|
>
|
|
27
26
|
> **Backend-JWT + org sites** (a backend API plus the org plugin): compose `createAuthPool`,
|
|
28
27
|
> `backendJwtOptions({ audience })`, `nkOrganizationDefaults`, and
|
|
@@ -37,7 +36,7 @@ only what you need from focused subpaths.
|
|
|
37
36
|
## Install
|
|
38
37
|
|
|
39
38
|
```bash
|
|
40
|
-
bun add @ingram-tech/nk-auth
|
|
39
|
+
bun add @ingram-tech/nk-auth better-auth @better-auth/passkey pg bcrypt
|
|
41
40
|
```
|
|
42
41
|
|
|
43
42
|
Set the env contract (validated by `keys.ts`):
|
|
@@ -45,46 +44,42 @@ Set the env contract (validated by `keys.ts`):
|
|
|
45
44
|
```dotenv
|
|
46
45
|
BETTER_AUTH_SECRET=… # openssl rand -hex 32
|
|
47
46
|
BETTER_AUTH_URL=https://example.com
|
|
48
|
-
DATABASE_URL=… #
|
|
49
|
-
NEXT_PUBLIC_SUPABASE_URL=…
|
|
50
|
-
NEXT_PUBLIC_SUPABASE_ANON_KEY=…
|
|
47
|
+
DATABASE_URL=… # direct Postgres connection (:5432)
|
|
51
48
|
```
|
|
52
49
|
|
|
53
50
|
## 1. Apply the schema
|
|
54
51
|
|
|
55
52
|
```bash
|
|
56
53
|
cp node_modules/@ingram-tech/nk-auth/migrations/0001_better_auth.sql \
|
|
57
|
-
|
|
54
|
+
migrations/$(date +%Y%m%d%H%M%S)_better_auth.sql
|
|
58
55
|
```
|
|
59
56
|
|
|
60
57
|
It creates Better Auth's tables (`user`, `session`, `account`, `verification`,
|
|
61
58
|
`jwks`, `passkey`), defaults new user ids to UUIDs, and puts **deny-all RLS** on
|
|
62
|
-
all of them (
|
|
63
|
-
|
|
64
|
-
|
|
59
|
+
all of them (Better Auth reaches them through its own privileged connection).
|
|
60
|
+
Reconcile against your pinned `better-auth` with `npx @better-auth/cli generate`
|
|
61
|
+
after upgrades.
|
|
65
62
|
|
|
66
63
|
## 2. Configure the server
|
|
67
64
|
|
|
68
65
|
This package is **not** a `betterAuth()` wrapper — your site calls `betterAuth`
|
|
69
66
|
itself and spreads in our presets. That keeps full Better Auth type inference at
|
|
70
67
|
the call site (so `auth.api.*` stays typed) and keeps the site plain Better Auth,
|
|
71
|
-
per the prime directive.
|
|
68
|
+
per the prime directive.
|
|
72
69
|
|
|
73
70
|
```ts
|
|
74
71
|
// lib/auth.ts
|
|
75
72
|
import { passkey } from "@better-auth/passkey";
|
|
76
|
-
import { fromAddress, sendEmail } from "@ingram-tech/email";
|
|
73
|
+
import { fromAddress, sendEmail } from "@ingram-tech/nk-email";
|
|
77
74
|
import {
|
|
78
75
|
authBasePath,
|
|
79
76
|
authEnv,
|
|
80
77
|
bcryptPassword,
|
|
81
78
|
makeEmailSenders,
|
|
82
79
|
makePasskeyOptions,
|
|
83
|
-
rlsJwtOptions,
|
|
84
80
|
uuidGenerateId,
|
|
85
81
|
} from "@ingram-tech/nk-auth";
|
|
86
82
|
import { betterAuth } from "better-auth";
|
|
87
|
-
import { jwt } from "better-auth/plugins/jwt";
|
|
88
83
|
import { Pool } from "pg";
|
|
89
84
|
|
|
90
85
|
const env = authEnv();
|
|
@@ -98,14 +93,14 @@ export const auth = betterAuth({
|
|
|
98
93
|
baseURL: env.baseURL,
|
|
99
94
|
basePath: authBasePath, // mount at /auth, not the framework default /api/auth
|
|
100
95
|
advanced: { database: { generateId: uuidGenerateId } }, // UUIDv7 ids
|
|
101
|
-
// ^ stored as hyphenated UUIDv7 (uuid columns /
|
|
96
|
+
// ^ stored as hyphenated UUIDv7 (uuid columns / RLS policies stay valid).
|
|
102
97
|
// To show those same ids as prefixed base58 on the wire/UI — `team_…`,
|
|
103
98
|
// matching the Ingram Cloud API's `agt_`/`smt_` ids — skin them with
|
|
104
99
|
// `toPrefixedId(uuid, "team")` / recover with `fromPrefixedId`. `base58Id`
|
|
105
100
|
// mints a fresh one directly for text-id sites. All from `@ingram-tech/nk-auth`.
|
|
106
101
|
emailAndPassword: {
|
|
107
102
|
enabled: true,
|
|
108
|
-
password: bcryptPassword, //
|
|
103
|
+
password: bcryptPassword, // bcrypt verifier (Better Auth defaults to scrypt)
|
|
109
104
|
sendResetPassword: email.sendResetPassword,
|
|
110
105
|
},
|
|
111
106
|
emailVerification: { sendVerificationEmail: email.sendVerificationEmail },
|
|
@@ -116,7 +111,6 @@ export const auth = betterAuth({
|
|
|
116
111
|
},
|
|
117
112
|
},
|
|
118
113
|
plugins: [
|
|
119
|
-
jwt(rlsJwtOptions), // the RLS bridge
|
|
120
114
|
passkey(makePasskeyOptions({ rpId: "example.com", rpName: "Example", origin: env.baseURL })),
|
|
121
115
|
],
|
|
122
116
|
});
|
|
@@ -132,29 +126,11 @@ export const { GET, POST } = auth.handler;
|
|
|
132
126
|
|
|
133
127
|
## 3. Query data with RLS intact
|
|
134
128
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
`
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
```ts
|
|
141
|
-
import { authEnv, createServerSupabase } from "@ingram-tech/nk-auth";
|
|
142
|
-
import { auth } from "@/lib/auth";
|
|
143
|
-
|
|
144
|
-
export async function GET(request: Request) {
|
|
145
|
-
const env = authEnv();
|
|
146
|
-
const supabase = createServerSupabase({
|
|
147
|
-
getToken: async () =>
|
|
148
|
-
(await auth.api.getToken({ headers: request.headers }))?.token ?? null,
|
|
149
|
-
supabaseUrl: env.supabaseUrl,
|
|
150
|
-
supabaseAnonKey: env.supabaseAnonKey,
|
|
151
|
-
});
|
|
152
|
-
// Reads/writes run as the signed-in user; existing RLS policies apply.
|
|
153
|
-
const { data, error } = await supabase.from("notes").select("*");
|
|
154
|
-
if (error) throw new Error(error.message);
|
|
155
|
-
return Response.json(data);
|
|
156
|
-
}
|
|
157
|
-
```
|
|
129
|
+
Data access lives in [`@ingram-tech/nk-db`](../nk-db), not here. Query over the
|
|
130
|
+
direct `pg` connection and wrap reads/writes in `withRls` / `withRlsTransaction`,
|
|
131
|
+
which set `request.jwt.claims` + `SET LOCAL ROLE` per transaction from the Better
|
|
132
|
+
Auth session — so `auth.uid()` policies fire unchanged, with no JWT minting and
|
|
133
|
+
no PostgREST. See its README for the pattern.
|
|
158
134
|
|
|
159
135
|
## 4. Client
|
|
160
136
|
|
|
@@ -263,12 +239,3 @@ the cookie-present case the guard reads it from the `x-nk-auth-path` header
|
|
|
263
239
|
middleware injects — so the self-heal (next + clearing) needs the middleware.
|
|
264
240
|
Sites that skip middleware still get validated gating from the server helpers,
|
|
265
241
|
just without automatic `next`/clearing.
|
|
266
|
-
|
|
267
|
-
## RLS bridge (the important part)
|
|
268
|
-
|
|
269
|
-
`auth.uid()` reads the `sub` claim of the JWT PostgREST receives. The `jwt`
|
|
270
|
-
plugin (configured here) mints an asymmetric token with `sub` = the user's UUID
|
|
271
|
-
and `role: "authenticated"`, exposed at `/auth/jwks`. Register that JWKS URL
|
|
272
|
-
as a Supabase **third-party auth** issuer, and every existing policy works
|
|
273
|
-
unchanged. Full rationale and the HS256 fallback:
|
|
274
|
-
[`docs/better-auth-migration.md`](../../docs/better-auth-migration.md).
|
package/dist/index.d.ts
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
export { type BackendJwtConfig, backendJwtOptions,
|
|
1
|
+
export { type BackendJwtConfig, backendJwtOptions, verifyBackendJwt } from "./jwt.js";
|
|
2
2
|
export { base58Id, fromPrefixedId, toPrefixedId, uuidGenerateId } from "./id.js";
|
|
3
3
|
export { type AuthEnv, authEnv, isConfigured } from "./keys.js";
|
|
4
4
|
export { bcryptPassword, makeEmailSenders, makePasskeyOptions, type PasskeyConfig, type SendEmail, } from "./options.js";
|
|
5
5
|
export { lastActiveOrganizationHooks, lastActiveOrganizationUserField, nkOrganizationDefaults, } from "./organization.js";
|
|
6
6
|
export { authBasePath } from "./paths.js";
|
|
7
7
|
export { createAuthPool } from "./pool.js";
|
|
8
|
-
export { createServerSupabase, type ServerSupabaseConfig } from "./supabase.js";
|
|
9
8
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAKA,OAAO,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,KAAK,gBAAgB,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AACtF,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AACjF,OAAO,EAAE,KAAK,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAChE,OAAO,EACN,cAAc,EACd,gBAAgB,EAChB,kBAAkB,EAClB,KAAK,aAAa,EAClB,KAAK,SAAS,GACd,MAAM,cAAc,CAAC;AACtB,OAAO,EACN,2BAA2B,EAC3B,+BAA+B,EAC/B,sBAAsB,GACtB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
// Server entry — the Ingram Better Auth foundation. Browser re-exports live at
|
|
2
2
|
// "@ingram-tech/nk-auth/client" so importing the server presets never pulls in
|
|
3
3
|
// React. Focused subpaths (./jwt, ./organization, ./pool) let a site import
|
|
4
|
-
// only what it needs (e.g. avoid bcrypt
|
|
5
|
-
export { backendJwtOptions,
|
|
4
|
+
// only what it needs (e.g. avoid bcrypt when it doesn't use passwords).
|
|
5
|
+
export { backendJwtOptions, verifyBackendJwt } from "./jwt.js";
|
|
6
6
|
export { base58Id, fromPrefixedId, toPrefixedId, uuidGenerateId } from "./id.js";
|
|
7
7
|
export { authEnv, isConfigured } from "./keys.js";
|
|
8
8
|
export { bcryptPassword, makeEmailSenders, makePasskeyOptions, } from "./options.js";
|
|
9
9
|
export { lastActiveOrganizationHooks, lastActiveOrganizationUserField, nkOrganizationDefaults, } from "./organization.js";
|
|
10
10
|
export { authBasePath } from "./paths.js";
|
|
11
11
|
export { createAuthPool } from "./pool.js";
|
|
12
|
-
export { createServerSupabase } from "./supabase.js";
|
|
13
12
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,+EAA+E;AAC/E,4EAA4E;AAC5E,wEAAwE;AAExE,OAAO,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,+EAA+E;AAC/E,4EAA4E;AAC5E,wEAAwE;AAExE,OAAO,EAAyB,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AACtF,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AACjF,OAAO,EAAgB,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAChE,OAAO,EACN,cAAc,EACd,gBAAgB,EAChB,kBAAkB,GAGlB,MAAM,cAAc,CAAC;AACtB,OAAO,EACN,2BAA2B,EAC3B,+BAA+B,EAC/B,sBAAsB,GACtB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC"}
|
package/dist/jwt.d.ts
CHANGED
|
@@ -1,19 +1,11 @@
|
|
|
1
1
|
import type { JwtOptions } from "better-auth/plugins/jwt";
|
|
2
2
|
import { type JWTPayload } from "jose";
|
|
3
3
|
/**
|
|
4
|
-
* `jwt` plugin
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* so PostgREST's `auth.uid()` keeps working. See docs/better-auth-migration.md.
|
|
9
|
-
* - `backendJwtOptions` — a short-lived token for a site's OWN backend API
|
|
10
|
-
* (a specific `audience`), typically carrying extra claims the site adds via
|
|
11
|
-
* `auth.api.signJWT`.
|
|
12
|
-
*
|
|
13
|
-
* Both default to EdDSA (Better Auth's default), exposed at `/api/auth/jwks`.
|
|
4
|
+
* `jwt` plugin preset for a site's OWN backend API: a short-lived token with a
|
|
5
|
+
* specific `audience`, typically carrying extra claims the site adds via
|
|
6
|
+
* `auth.api.signJWT`. Defaults to EdDSA (Better Auth's default), with the JWKS
|
|
7
|
+
* exposed at `/api/auth/jwks` so the backend can verify it (`verifyBackendJwt`).
|
|
14
8
|
*/
|
|
15
|
-
/** Supabase RLS bridge: mints `sub` = user id, `role`/`aud` = "authenticated". */
|
|
16
|
-
export declare const rlsJwtOptions: JwtOptions;
|
|
17
9
|
export interface BackendJwtConfig {
|
|
18
10
|
/** Expected audience of the site's backend, e.g. "ingram-wiki-backend". */
|
|
19
11
|
audience: string;
|
package/dist/jwt.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"jwt.d.ts","sourceRoot":"","sources":["../src/jwt.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,EAAsB,KAAK,UAAU,EAAa,MAAM,MAAM,CAAC;AAEtE
|
|
1
|
+
{"version":3,"file":"jwt.d.ts","sourceRoot":"","sources":["../src/jwt.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,EAAsB,KAAK,UAAU,EAAa,MAAM,MAAM,CAAC;AAEtE;;;;;GAKG;AAEH,MAAM,WAAW,gBAAgB;IAChC,2EAA2E;IAC3E,QAAQ,EAAE,MAAM,CAAC;IACjB,kCAAkC;IAClC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;OAGG;IACH,uBAAuB,CAAC,EAAE,OAAO,CAAC;CAClC;AAED,sEAAsE;AACtE,eAAO,MAAM,iBAAiB,GAAI,QAAQ,gBAAgB,KAAG,UAM3D,CAAC;AAIH;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,GAAU,QAAQ;IAC9C,KAAK,EAAE,MAAM,CAAC;IACd,2EAA2E;IAC3E,OAAO,EAAE,MAAM,GAAG,GAAG,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;CACf,KAAG,OAAO,CAAC,UAAU,CAerB,CAAC"}
|
package/dist/jwt.js
CHANGED
|
@@ -1,27 +1,4 @@
|
|
|
1
1
|
import { createRemoteJWKSet, jwtVerify } from "jose";
|
|
2
|
-
/**
|
|
3
|
-
* `jwt` plugin presets. Two shapes, because Ingram sites mint JWTs for two
|
|
4
|
-
* different audiences:
|
|
5
|
-
*
|
|
6
|
-
* - `rlsJwtOptions` — a Supabase RLS bridge token (`role: "authenticated"`),
|
|
7
|
-
* so PostgREST's `auth.uid()` keeps working. See docs/better-auth-migration.md.
|
|
8
|
-
* - `backendJwtOptions` — a short-lived token for a site's OWN backend API
|
|
9
|
-
* (a specific `audience`), typically carrying extra claims the site adds via
|
|
10
|
-
* `auth.api.signJWT`.
|
|
11
|
-
*
|
|
12
|
-
* Both default to EdDSA (Better Auth's default), exposed at `/api/auth/jwks`.
|
|
13
|
-
*/
|
|
14
|
-
/** Supabase RLS bridge: mints `sub` = user id, `role`/`aud` = "authenticated". */
|
|
15
|
-
export const rlsJwtOptions = {
|
|
16
|
-
jwks: { keyPairConfig: { alg: "EdDSA" } },
|
|
17
|
-
jwt: {
|
|
18
|
-
definePayload: ({ user }) => ({
|
|
19
|
-
sub: user.id,
|
|
20
|
-
role: "authenticated",
|
|
21
|
-
aud: "authenticated",
|
|
22
|
-
}),
|
|
23
|
-
},
|
|
24
|
-
};
|
|
25
2
|
/** `jwt` plugin options for a backend-API token (custom audience). */
|
|
26
3
|
export const backendJwtOptions = (config) => ({
|
|
27
4
|
disableSettingJwtHeader: config.disableSettingJwtHeader,
|
package/dist/jwt.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"jwt.js","sourceRoot":"","sources":["../src/jwt.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAmB,SAAS,EAAE,MAAM,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"jwt.js","sourceRoot":"","sources":["../src/jwt.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAmB,SAAS,EAAE,MAAM,MAAM,CAAC;AAqBtE,sEAAsE;AACtE,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,MAAwB,EAAc,EAAE,CAAC,CAAC;IAC3E,uBAAuB,EAAE,MAAM,CAAC,uBAAuB;IACvD,GAAG,EAAE;QACJ,QAAQ,EAAE,MAAM,CAAC,QAAQ;QACzB,cAAc,EAAE,MAAM,CAAC,cAAc;KACrC;CACD,CAAC,CAAC;AAEH,MAAM,SAAS,GAAG,IAAI,GAAG,EAAiD,CAAC;AAE3E;;;;GAIG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,KAAK,EAAE,MAMtC,EAAuB,EAAE;IACzB,MAAM,GAAG,GACR,OAAO,MAAM,CAAC,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC;IAC/E,MAAM,QAAQ,GAAG,GAAG,CAAC,QAAQ,EAAE,CAAC;IAChC,IAAI,IAAI,GAAG,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IACnC,IAAI,CAAC,IAAI,EAAE,CAAC;QACX,IAAI,GAAG,kBAAkB,CAAC,GAAG,CAAC,CAAC;QAC/B,SAAS,CAAC,GAAG,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;IAC/B,CAAC;IACD,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,SAAS,CAAC,MAAM,CAAC,KAAK,EAAE,IAAI,EAAE;QACvD,QAAQ,EAAE,MAAM,CAAC,QAAQ;QACzB,MAAM,EAAE,MAAM,CAAC,MAAM;QACrB,UAAU,EAAE,CAAC,OAAO,CAAC;KACrB,CAAC,CAAC;IACH,OAAO,OAAO,CAAC;AAChB,CAAC,CAAC"}
|
package/dist/keys.d.ts
CHANGED
|
@@ -4,22 +4,17 @@
|
|
|
4
4
|
* Following the "each package owns its own env validation" pattern. Env vars are
|
|
5
5
|
* external input, so we parse them with Zod (per docs/code-style.md) rather than
|
|
6
6
|
* reading `process.env` ad hoc. `authEnv()` returns a validated, config-shaped
|
|
7
|
-
* object you can spread straight into `
|
|
7
|
+
* object you can spread straight into your `betterAuth()` call.
|
|
8
8
|
*
|
|
9
9
|
* Required:
|
|
10
|
-
* BETTER_AUTH_SECRET
|
|
11
|
-
* BETTER_AUTH_URL
|
|
12
|
-
* DATABASE_URL
|
|
13
|
-
* (session-mode pooler or :5432, NOT PostgREST)
|
|
14
|
-
* NEXT_PUBLIC_SUPABASE_URL — Supabase project URL (for the data client)
|
|
15
|
-
* NEXT_PUBLIC_SUPABASE_ANON_KEY — Supabase anon key (RLS still enforced)
|
|
10
|
+
* BETTER_AUTH_SECRET — session/CSRF signing key (`openssl rand -hex 32`)
|
|
11
|
+
* BETTER_AUTH_URL — canonical site origin, e.g. "https://example.com"
|
|
12
|
+
* DATABASE_URL — direct Postgres connection for Better Auth (:5432)
|
|
16
13
|
*/
|
|
17
14
|
export interface AuthEnv {
|
|
18
15
|
secret: string;
|
|
19
16
|
baseURL: string;
|
|
20
17
|
databaseUrl: string;
|
|
21
|
-
supabaseUrl: string;
|
|
22
|
-
supabaseAnonKey: string;
|
|
23
18
|
}
|
|
24
19
|
/**
|
|
25
20
|
* Read and validate all nk-auth env vars at once. Throws a single error listing
|
package/dist/keys.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"keys.d.ts","sourceRoot":"","sources":["../src/keys.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"keys.d.ts","sourceRoot":"","sources":["../src/keys.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAUH,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,OAc1B,CAAC;AAEF,+EAA+E;AAC/E,eAAO,MAAM,YAAY,QAAO,OAAgD,CAAC"}
|
package/dist/keys.js
CHANGED
|
@@ -4,23 +4,18 @@
|
|
|
4
4
|
* Following the "each package owns its own env validation" pattern. Env vars are
|
|
5
5
|
* external input, so we parse them with Zod (per docs/code-style.md) rather than
|
|
6
6
|
* reading `process.env` ad hoc. `authEnv()` returns a validated, config-shaped
|
|
7
|
-
* object you can spread straight into `
|
|
7
|
+
* object you can spread straight into your `betterAuth()` call.
|
|
8
8
|
*
|
|
9
9
|
* Required:
|
|
10
|
-
* BETTER_AUTH_SECRET
|
|
11
|
-
* BETTER_AUTH_URL
|
|
12
|
-
* DATABASE_URL
|
|
13
|
-
* (session-mode pooler or :5432, NOT PostgREST)
|
|
14
|
-
* NEXT_PUBLIC_SUPABASE_URL — Supabase project URL (for the data client)
|
|
15
|
-
* NEXT_PUBLIC_SUPABASE_ANON_KEY — Supabase anon key (RLS still enforced)
|
|
10
|
+
* BETTER_AUTH_SECRET — session/CSRF signing key (`openssl rand -hex 32`)
|
|
11
|
+
* BETTER_AUTH_URL — canonical site origin, e.g. "https://example.com"
|
|
12
|
+
* DATABASE_URL — direct Postgres connection for Better Auth (:5432)
|
|
16
13
|
*/
|
|
17
14
|
import { z } from "zod";
|
|
18
15
|
const schema = z.object({
|
|
19
16
|
BETTER_AUTH_SECRET: z.string().min(1),
|
|
20
17
|
BETTER_AUTH_URL: z.string().url(),
|
|
21
18
|
DATABASE_URL: z.string().min(1),
|
|
22
|
-
NEXT_PUBLIC_SUPABASE_URL: z.string().url(),
|
|
23
|
-
NEXT_PUBLIC_SUPABASE_ANON_KEY: z.string().min(1),
|
|
24
19
|
});
|
|
25
20
|
/**
|
|
26
21
|
* Read and validate all nk-auth env vars at once. Throws a single error listing
|
|
@@ -40,8 +35,6 @@ export const authEnv = () => {
|
|
|
40
35
|
secret: env.BETTER_AUTH_SECRET,
|
|
41
36
|
baseURL: env.BETTER_AUTH_URL,
|
|
42
37
|
databaseUrl: env.DATABASE_URL,
|
|
43
|
-
supabaseUrl: env.NEXT_PUBLIC_SUPABASE_URL,
|
|
44
|
-
supabaseAnonKey: env.NEXT_PUBLIC_SUPABASE_ANON_KEY,
|
|
45
38
|
};
|
|
46
39
|
};
|
|
47
40
|
/** Whether nk-auth is fully configured (lets callers degrade in local/dev). */
|
package/dist/keys.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"keys.js","sourceRoot":"","sources":["../src/keys.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"keys.js","sourceRoot":"","sources":["../src/keys.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,MAAM,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC;IACvB,kBAAkB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IACrC,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;AAQH;;;;GAIG;AACH,MAAM,CAAC,MAAM,OAAO,GAAG,GAAY,EAAE;IACpC,MAAM,MAAM,GAAG,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAC7C,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,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,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC"}
|
package/dist/options.d.ts
CHANGED
|
@@ -30,7 +30,7 @@ export interface PasskeyConfig {
|
|
|
30
30
|
}
|
|
31
31
|
/** Build `passkey` plugin options. Use as `passkey(makePasskeyOptions(cfg))`. */
|
|
32
32
|
export declare const makePasskeyOptions: (cfg: PasskeyConfig) => PasskeyOptions;
|
|
33
|
-
/** Send one transactional email (wire to `@ingram-tech/email`'s `sendEmail`). */
|
|
33
|
+
/** Send one transactional email (wire to `@ingram-tech/nk-email`'s `sendEmail`). */
|
|
34
34
|
export type SendEmail = (message: {
|
|
35
35
|
to: string;
|
|
36
36
|
subject: string;
|
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;AAM3D,OAAO,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAEzC;;;;;;;;GAQG;AAEH;;;GAGG;AACH,eAAO,MAAM,cAAc;qBACT,MAAM,KAAG,OAAO,CAAC,MAAM,CAAC;kCAItC;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,GAAI,KAAK,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;AAM3D,OAAO,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAEzC;;;;;;;;GAQG;AAEH;;;GAGG;AACH,eAAO,MAAM,cAAc;qBACT,MAAM,KAAG,OAAO,CAAC,MAAM,CAAC;kCAItC;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,GAAI,KAAK,aAAa,KAAG,cAItD,CAAC;AAEH,oFAAoF;AACpF,MAAM,MAAM,SAAS,GAAG,CAAC,OAAO,EAAE;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;CACZ,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;AAEvB;;;GAGG;AACH,eAAO,MAAM,gBAAgB,GAAI,MAAM,SAAS;wCAI5C;QACF,IAAI,EAAE;YAAE,KAAK,EAAE,MAAM,CAAA;SAAE,CAAC;QACxB,GAAG,EAAE,MAAM,CAAC;KACZ,KAAG,OAAO,CAAC,IAAI,CAAC;4CAMd;QACF,IAAI,EAAE;YAAE,KAAK,EAAE,MAAM,CAAA;SAAE,CAAC;QACxB,GAAG,EAAE,MAAM,CAAC;KACZ,KAAG,OAAO,CAAC,IAAI,CAAC;CAGhB,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ingram-tech/nk-auth",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "The Ingram Better Auth foundation: composable presets (org, dual-shape JWT, Supabase RLS bridge, active-org hooks, pg pool) for Next.js sites.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -61,7 +61,7 @@
|
|
|
61
61
|
"test": "vitest run"
|
|
62
62
|
},
|
|
63
63
|
"dependencies": {
|
|
64
|
-
"@ingram-tech/nk-db": "^0.
|
|
64
|
+
"@ingram-tech/nk-db": "^0.9.0",
|
|
65
65
|
"bcrypt": "^6.0.0",
|
|
66
66
|
"jose": "^6.0.0",
|
|
67
67
|
"zod": "^4.0.0"
|
|
@@ -93,7 +93,7 @@
|
|
|
93
93
|
},
|
|
94
94
|
"devDependencies": {
|
|
95
95
|
"@better-auth/passkey": "^1.6.0",
|
|
96
|
-
"@ingram-tech/nk-dev": "
|
|
96
|
+
"@ingram-tech/nk-dev": "0.2.3",
|
|
97
97
|
"@supabase/supabase-js": "^2.45.0",
|
|
98
98
|
"@types/bcrypt": "^6.0.0",
|
|
99
99
|
"@types/node": "^25.0.0",
|
package/dist/supabase.d.ts
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import { type SupabaseClient } from "@supabase/supabase-js";
|
|
2
|
-
/**
|
|
3
|
-
* The RLS-aware Supabase data client — the single chokepoint that keeps Row
|
|
4
|
-
* Level Security working after the move off Supabase Auth.
|
|
5
|
-
*
|
|
6
|
-
* Instead of `supabase.auth`, it pulls the Better Auth session JWT (via the
|
|
7
|
-
* injected `getToken`) and hands it to supabase-js's v2 `accessToken` callback.
|
|
8
|
-
* PostgREST then sets `request.jwt.claims`, so `auth.uid()` (and every existing
|
|
9
|
-
* policy) keeps returning the right user. With no session, `getToken` yields
|
|
10
|
-
* `null` and the request runs as `anon` — RLS is still enforced, never bypassed.
|
|
11
|
-
*
|
|
12
|
-
* Sites should import THIS rather than constructing supabase-js directly, so the
|
|
13
|
-
* token bridge can't be forgotten. (Enforce with a Biome/GritQL rule banning raw
|
|
14
|
-
* `createClient` for data — see docs/better-auth-migration.md.)
|
|
15
|
-
*
|
|
16
|
-
* Wire `getToken` to the site's own Better Auth instance, e.g.:
|
|
17
|
-
* getToken: async () => (await auth.api.getToken({ headers }))?.token ?? null
|
|
18
|
-
*/
|
|
19
|
-
export interface ServerSupabaseConfig {
|
|
20
|
-
/** Returns the current request's Better Auth JWT, or null when signed out. */
|
|
21
|
-
getToken: () => Promise<string | null>;
|
|
22
|
-
supabaseUrl: string;
|
|
23
|
-
supabaseAnonKey: string;
|
|
24
|
-
}
|
|
25
|
-
export declare const createServerSupabase: (config: ServerSupabaseConfig) => SupabaseClient;
|
|
26
|
-
//# sourceMappingURL=supabase.d.ts.map
|
package/dist/supabase.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"supabase.d.ts","sourceRoot":"","sources":["../src/supabase.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,KAAK,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAE1E;;;;;;;;;;;;;;;;GAgBG;AAEH,MAAM,WAAW,oBAAoB;IACpC,8EAA8E;IAC9E,QAAQ,EAAE,MAAM,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IACvC,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;CACxB;AAED,eAAO,MAAM,oBAAoB,GAAI,QAAQ,oBAAoB,KAAG,cAMjE,CAAC"}
|
package/dist/supabase.js
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import { createClient } from "@supabase/supabase-js";
|
|
2
|
-
export const createServerSupabase = (config) => createClient(config.supabaseUrl, config.supabaseAnonKey, {
|
|
3
|
-
accessToken: async () => {
|
|
4
|
-
const token = await config.getToken();
|
|
5
|
-
return token ?? "";
|
|
6
|
-
},
|
|
7
|
-
});
|
|
8
|
-
//# sourceMappingURL=supabase.js.map
|
package/dist/supabase.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"supabase.js","sourceRoot":"","sources":["../src/supabase.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAuB,MAAM,uBAAuB,CAAC;AA2B1E,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,MAA4B,EAAkB,EAAE,CACpF,YAAY,CAAC,MAAM,CAAC,WAAW,EAAE,MAAM,CAAC,eAAe,EAAE;IACxD,WAAW,EAAE,KAAK,IAAI,EAAE;QACvB,MAAM,KAAK,GAAG,MAAM,MAAM,CAAC,QAAQ,EAAE,CAAC;QACtC,OAAO,KAAK,IAAI,EAAE,CAAC;IACpB,CAAC;CACD,CAAC,CAAC"}
|