@nifrajs/better-auth 2.14.1 → 3.2.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 CHANGED
@@ -58,8 +58,8 @@ app.get("/me", async (c) => {
58
58
 
59
59
  ## Protect a route
60
60
 
61
- `requireSession(auth, request, options?)` returns the non-null session or **throws a `Response`** -
62
- nifra returns a thrown `Response` as-is, short-circuiting the handler:
61
+ `requireSession(auth, request, options?)` returns the non-null session or **throws a Nifra `ResponseResult`** -
62
+ nifra handles the thrown `ResponseResult` as control flow, short-circuiting the handler:
63
63
 
64
64
  ```ts
65
65
  import { requireSession } from "@nifrajs/better-auth"
@@ -83,7 +83,7 @@ export const loader = async ({ request }) => {
83
83
  | --- | --- |
84
84
  | `betterAuth(auth, options?)` | Plugin that mounts better-auth's handler at `${basePath}/*` for GET + POST. |
85
85
  | `getSession(auth, request)` | `Promise<SessionOf<A> \| null>` - typed wrapper over `auth.api.getSession`. |
86
- | `requireSession(auth, request, options?)` | Returns the session or throws a `Response` (302 `redirectTo` / 401). |
86
+ | `requireSession(auth, request, options?)` | Returns the session or throws a Nifra `ResponseResult` (302 `redirectTo` / 401). |
87
87
  | `SessionOf<A>` | The non-null session payload type inferred from your `auth`. |
88
88
  | `BetterAuthLike` | The structural contract a better-auth instance satisfies. |
89
89
 
package/dist/index.d.ts CHANGED
@@ -50,7 +50,7 @@ export interface BetterAuthOptions {
50
50
  * Idempotent (named `"better-auth"` - applying twice mounts once). Read the session with
51
51
  * {@link getSession} / {@link requireSession}, which infer your better-auth types.
52
52
  */
53
- export declare function betterAuth(auth: BetterAuthLike, options?: BetterAuthOptions): import("@nifrajs/core").IdentityPlugin;
53
+ export declare function betterAuth(auth: BetterAuthLike, options?: BetterAuthOptions): import("@nifrajs/core").IdentityPlugin<never>;
54
54
  /**
55
55
  * Resolve the better-auth session for a request - a thin, typed wrapper over `auth.api.getSession`.
56
56
  * Returns `null` when unauthenticated. Takes the raw `Request` so it works in both server handlers
@@ -71,8 +71,8 @@ export interface RequireSessionOptions {
71
71
  }
72
72
  /**
73
73
  * Require an authenticated better-auth session at the top of a protected handler/loader/action.
74
- * Returns the (non-null) session when present; otherwise **throws a `Response`** (302/401) - nifra
75
- * returns a thrown `Response` as-is, short-circuiting the rest of the handler.
74
+ * Returns the (non-null) session when present; otherwise **throws a Nifra `ResponseResult`** (302/401).
75
+ * Nifra handles the thrown control-flow value on the same rendering lane as `@nifrajs/auth`.
76
76
  *
77
77
  * ```ts
78
78
  * const { user } = await requireSession(auth, c.req, { redirectTo: "/login" })
@@ -130,14 +130,14 @@ export type WithPrincipal<S extends AnyServer, P> = S extends Server<infer R, in
130
130
  principal: P;
131
131
  }> : never;
132
132
  /**
133
- * Resolve the better-auth session and map it to a {@link Principal}, or **throw a `Response`** so the
133
+ * Resolve the better-auth session and map it to a {@link Principal}, or **throw a Nifra `ResponseResult`** so the
134
134
  * handler never runs unauthenticated:
135
135
  *
136
136
  * - No/invalid session -> `302` to `options.redirectTo` when set, else `401` JSON (`requireSession`).
137
137
  * - `requireTenant: true` and no tenant resolves -> `403` JSON (`{ ok: false, error: "forbidden" }`).
138
138
  *
139
- * nifra returns a thrown `Response` as-is (short-circuit), so this is the fail-closed guard used inline
140
- * or by {@link authed}. `tenantId` is a non-optional `string` in the return type when `requireTenant`.
139
+ * nifra handles the thrown control-flow value as a short-circuit, so this is the fail-closed guard used
140
+ * inline or by {@link authed}. `tenantId` is a non-optional `string` in the return type when `requireTenant`.
141
141
  *
142
142
  * ```ts
143
143
  * const principal = await requirePrincipal(auth, c.req, { requireTenant: true })
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,SAAS,EAId,KAAK,MAAM,EAEZ,MAAM,sBAAsB,CAAA;AAE7B;;;;;GAKG;AACH,MAAM,WAAW,cAAc;IAC7B,+EAA+E;IAC/E,QAAQ,CAAC,OAAO,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAA;IACpE,QAAQ,CAAC,GAAG,EAAE;QACZ,+FAA+F;QAC/F,QAAQ,CAAC,UAAU,EAAE,CAAC,OAAO,EAAE;YAAE,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;SAAE,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;KAClF,CAAA;IACD;;;;;;OAMG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAC5E;AAED;;;GAGG;AACH,MAAM,MAAM,SAAS,CAAC,CAAC,SAAS,cAAc,IAAI,WAAW,CAC3D,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAC5C,CAAA;AAED,MAAM,WAAW,iBAAiB;IAChC,oGAAoG;IACpG,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAC3B;AAKD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,cAAc,EAAE,OAAO,GAAE,iBAAsB,iDAkB/E;AAED;;;;;;;;;GASG;AACH,wBAAgB,UAAU,CAAC,CAAC,SAAS,cAAc,EACjD,IAAI,EAAE,CAAC,EACP,OAAO,EAAE,OAAO,GACf,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,CAI9B;AAED;;;GAGG;AACH,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAC7B;AAgBD;;;;;;;;GAQG;AACH,wBAAsB,cAAc,CAAC,CAAC,SAAS,cAAc,EAC3D,IAAI,EAAE,CAAC,EACP,OAAO,EAAE,OAAO,EAChB,OAAO,GAAE,qBAA0B,GAClC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAIvB;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,SAAS,CAAC,IAAI;IAC7B,+EAA+E;IAC/E,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAA;IACnB,iCAAiC;IACjC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,uCAAuC;IACvC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,iGAAiG;IACjG,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAC3B;AAED;;;GAGG;AACH,MAAM,MAAM,aAAa,CAAC,CAAC,SAAS,cAAc,IAChD,SAAS,CAAC,CAAC,CAAC,SAAS;IAAE,IAAI,EAAE,MAAM,CAAC,CAAA;CAAE,GAAG,CAAC,GAAG,OAAO,CAAA;AAEtD;;GAEG;AACH,MAAM,WAAW,aAAa,CAAC,IAAI;IACjC,4FAA4F;IAC5F,QAAQ,CAAC,aAAa,CAAC,EAAE,OAAO,CAAA;IAChC,6GAA6G;IAC7G,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAA;IAC5B,wGAAwG;IACxG,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,MAAM,GAAG,SAAS,CAAA;CACvD;AAED;;;;GAIG;AACH,MAAM,MAAM,YAAY,CAAC,IAAI,EAAE,aAAa,SAAS,OAAO,IAAI,aAAa,SAAS,IAAI,GACtF,SAAS,CAAC,IAAI,CAAC,GAAG;IAAE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GAC/C,SAAS,CAAC,IAAI,CAAC,CAAA;AAEnB;+FAC+F;AAC/F,MAAM,MAAM,aAAa,CAAC,CAAC,SAAS,SAAS,EAAE,CAAC,IAC9C,CAAC,SAAS,MAAM,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,EAAE,CAAC,GAAG;IAAE,SAAS,EAAE,CAAC,CAAA;CAAE,CAAC,GAAG,KAAK,CAAA;AAI9E;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,gBAAgB,CACpC,CAAC,SAAS,cAAc,EACxB,KAAK,CAAC,aAAa,SAAS,OAAO,GAAG,KAAK,EAE3C,IAAI,EAAE,CAAC,EACP,OAAO,EAAE,OAAO,EAChB,OAAO,CAAC,EAAE,aAAa,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,GAAG;IAAE,QAAQ,CAAC,aAAa,CAAC,EAAE,aAAa,CAAA;CAAE,GACrF,OAAO,CAAC,YAAY,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,aAAa,CAAC,CAAC,CAqCxD;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,MAAM,CAAC,CAAC,SAAS,cAAc,EAAE,KAAK,CAAC,aAAa,SAAS,OAAO,GAAG,KAAK,EAC1F,IAAI,EAAE,CAAC,EACP,OAAO,CAAC,EAAE,aAAa,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,GAAG;IAAE,QAAQ,CAAC,aAAa,CAAC,EAAE,aAAa,CAAA;CAAE,GACrF,CAAC,CAAC,SAAS,SAAS,EACrB,GAAG,EAAE,CAAC,KACH,aAAa,CAAC,CAAC,EAAE,YAAY,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,aAAa,CAAC,CAAC,CAKnE"}
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { defineIdentityPlugin } from "@nifrajs/core/server";
1
+ import { defineIdentityPlugin, isSameOriginPath, status, } from "@nifrajs/core/server";
2
2
  // better-auth dispatches only GET (session, OAuth/email callbacks) and POST (sign-in/up/out, etc.).
3
3
  const HANDLED_METHODS = ["GET", "POST"];
4
4
  /**
@@ -53,18 +53,19 @@ export function getSession(auth, request) {
53
53
  const rejection = (options) => {
54
54
  const to = options.redirectTo;
55
55
  if (to === undefined)
56
- return Response.json({ ok: false, error: "unauthorized" }, { status: 401 });
57
- // Same-origin guard (mirrors @nifrajs/web `redirect` + @nifrajs/auth guards): a single leading "/", never
58
- // "//host" or an absolute URL. `redirectTo` is dev-authored, so a bad value is a config bug - fail loud.
59
- if (!to.startsWith("/") || to.startsWith("//")) {
60
- throw new Error(`[nifra/better-auth] requireSession redirectTo must be a same-origin path beginning with "/" (got ${JSON.stringify(to)})`);
56
+ return status(401, { ok: false, error: "unauthorized" });
57
+ // The kernel's same-origin predicate, shared with `@nifrajs/web`'s `redirect` and the
58
+ // `@nifrajs/auth` guards: a single leading "/", never "//host", an absolute URL, or a form a URL
59
+ // parser resolves off-origin. `redirectTo` is dev-authored, so a bad value is a config bug - fail loud.
60
+ if (!isSameOriginPath(to)) {
61
+ throw new Error(`[nifra/better-auth] requireSession redirectTo must be a same-origin path beginning with "/" - never "//", a backslash, or a control character (got ${JSON.stringify(to)})`);
61
62
  }
62
- return new Response(null, { status: 302, headers: { location: to } });
63
+ return status(302, undefined, { headers: { location: to } });
63
64
  };
64
65
  /**
65
66
  * Require an authenticated better-auth session at the top of a protected handler/loader/action.
66
- * Returns the (non-null) session when present; otherwise **throws a `Response`** (302/401) - nifra
67
- * returns a thrown `Response` as-is, short-circuiting the rest of the handler.
67
+ * Returns the (non-null) session when present; otherwise **throws a Nifra `ResponseResult`** (302/401).
68
+ * Nifra handles the thrown control-flow value on the same rendering lane as `@nifrajs/auth`.
68
69
  *
69
70
  * ```ts
70
71
  * const { user } = await requireSession(auth, c.req, { redirectTo: "/login" })
@@ -76,16 +77,16 @@ export async function requireSession(auth, request, options = {}) {
76
77
  return session;
77
78
  throw rejection(options);
78
79
  }
79
- const forbidden = () => Response.json({ ok: false, error: "forbidden" }, { status: 403 });
80
+ const forbidden = () => status(403, { ok: false, error: "forbidden" });
80
81
  /**
81
- * Resolve the better-auth session and map it to a {@link Principal}, or **throw a `Response`** so the
82
+ * Resolve the better-auth session and map it to a {@link Principal}, or **throw a Nifra `ResponseResult`** so the
82
83
  * handler never runs unauthenticated:
83
84
  *
84
85
  * - No/invalid session -> `302` to `options.redirectTo` when set, else `401` JSON (`requireSession`).
85
86
  * - `requireTenant: true` and no tenant resolves -> `403` JSON (`{ ok: false, error: "forbidden" }`).
86
87
  *
87
- * nifra returns a thrown `Response` as-is (short-circuit), so this is the fail-closed guard used inline
88
- * or by {@link authed}. `tenantId` is a non-optional `string` in the return type when `requireTenant`.
88
+ * nifra handles the thrown control-flow value as a short-circuit, so this is the fail-closed guard used
89
+ * inline or by {@link authed}. `tenantId` is a non-optional `string` in the return type when `requireTenant`.
89
90
  *
90
91
  * ```ts
91
92
  * const principal = await requirePrincipal(auth, c.req, { requireTenant: true })
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nifrajs/better-auth",
3
- "version": "2.14.1",
3
+ "version": "3.2.0",
4
4
  "description": "Mount better-auth into a nifra app - one app.use() wires /api/auth/*, plus typed getSession/requireSession guards. Structural typing: no hard dependency on better-auth.",
5
5
  "keywords": [
6
6
  "bun",
@@ -40,7 +40,7 @@
40
40
  "access": "public"
41
41
  },
42
42
  "peerDependencies": {
43
- "@nifrajs/core": "^2.14.1",
43
+ "@nifrajs/core": "^3.2.0",
44
44
  "better-auth": ">=1.0.0"
45
45
  },
46
46
  "peerDependenciesMeta": {
package/src/index.ts CHANGED
@@ -1,4 +1,11 @@
1
- import { type AnyServer, defineIdentityPlugin, type Server } from "@nifrajs/core/server"
1
+ import {
2
+ type AnyServer,
3
+ defineIdentityPlugin,
4
+ isSameOriginPath,
5
+ type ResponseResult,
6
+ type Server,
7
+ status,
8
+ } from "@nifrajs/core/server"
2
9
 
3
10
  /**
4
11
  * The structural slice of a [better-auth](https://better-auth.com) instance this package needs.
@@ -102,23 +109,24 @@ export interface RequireSessionOptions {
102
109
  readonly redirectTo?: string
103
110
  }
104
111
 
105
- const rejection = (options: RequireSessionOptions): Response => {
112
+ const rejection = (options: RequireSessionOptions): ResponseResult => {
106
113
  const to = options.redirectTo
107
- if (to === undefined) return Response.json({ ok: false, error: "unauthorized" }, { status: 401 })
108
- // Same-origin guard (mirrors @nifrajs/web `redirect` + @nifrajs/auth guards): a single leading "/", never
109
- // "//host" or an absolute URL. `redirectTo` is dev-authored, so a bad value is a config bug - fail loud.
110
- if (!to.startsWith("/") || to.startsWith("//")) {
114
+ if (to === undefined) return status(401, { ok: false, error: "unauthorized" })
115
+ // The kernel's same-origin predicate, shared with `@nifrajs/web`'s `redirect` and the
116
+ // `@nifrajs/auth` guards: a single leading "/", never "//host", an absolute URL, or a form a URL
117
+ // parser resolves off-origin. `redirectTo` is dev-authored, so a bad value is a config bug - fail loud.
118
+ if (!isSameOriginPath(to)) {
111
119
  throw new Error(
112
- `[nifra/better-auth] requireSession redirectTo must be a same-origin path beginning with "/" (got ${JSON.stringify(to)})`,
120
+ `[nifra/better-auth] requireSession redirectTo must be a same-origin path beginning with "/" - never "//", a backslash, or a control character (got ${JSON.stringify(to)})`,
113
121
  )
114
122
  }
115
- return new Response(null, { status: 302, headers: { location: to } })
123
+ return status(302, undefined, { headers: { location: to } })
116
124
  }
117
125
 
118
126
  /**
119
127
  * Require an authenticated better-auth session at the top of a protected handler/loader/action.
120
- * Returns the (non-null) session when present; otherwise **throws a `Response`** (302/401) - nifra
121
- * returns a thrown `Response` as-is, short-circuiting the rest of the handler.
128
+ * Returns the (non-null) session when present; otherwise **throws a Nifra `ResponseResult`** (302/401).
129
+ * Nifra handles the thrown control-flow value on the same rendering lane as `@nifrajs/auth`.
122
130
  *
123
131
  * ```ts
124
132
  * const { user } = await requireSession(auth, c.req, { redirectTo: "/login" })
@@ -187,17 +195,17 @@ export type PrincipalFor<User, RequireTenant extends boolean> = RequireTenant ex
187
195
  export type WithPrincipal<S extends AnyServer, P> =
188
196
  S extends Server<infer R, infer C> ? Server<R, C & { principal: P }> : never
189
197
 
190
- const forbidden = (): Response => Response.json({ ok: false, error: "forbidden" }, { status: 403 })
198
+ const forbidden = (): ResponseResult => status(403, { ok: false, error: "forbidden" })
191
199
 
192
200
  /**
193
- * Resolve the better-auth session and map it to a {@link Principal}, or **throw a `Response`** so the
201
+ * Resolve the better-auth session and map it to a {@link Principal}, or **throw a Nifra `ResponseResult`** so the
194
202
  * handler never runs unauthenticated:
195
203
  *
196
204
  * - No/invalid session -> `302` to `options.redirectTo` when set, else `401` JSON (`requireSession`).
197
205
  * - `requireTenant: true` and no tenant resolves -> `403` JSON (`{ ok: false, error: "forbidden" }`).
198
206
  *
199
- * nifra returns a thrown `Response` as-is (short-circuit), so this is the fail-closed guard used inline
200
- * or by {@link authed}. `tenantId` is a non-optional `string` in the return type when `requireTenant`.
207
+ * nifra handles the thrown control-flow value as a short-circuit, so this is the fail-closed guard used
208
+ * inline or by {@link authed}. `tenantId` is a non-optional `string` in the return type when `requireTenant`.
201
209
  *
202
210
  * ```ts
203
211
  * const principal = await requirePrincipal(auth, c.req, { requireTenant: true })