@fluixi/auth 1.0.0-alpha.92 → 1.0.0-alpha.93

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
@@ -34,6 +34,18 @@ const admin = await requireRole('admin'); // throws AuthError(403)
34
34
  await requirePermission(() => can(user, 'edit', post));
35
35
  const maybe = await optionalUser(); // U | null
36
36
  ```
37
+ ```ts
38
+ // API route contracts (@fluixi/api) take a guard instead: it runs before the input is read
39
+ // and types `auth` in the handler.
40
+ import { route } from '@fluixi/api';
41
+ import { member, signedIn } from '@fluixi/auth/server';
42
+
43
+ export const GET = route({ guard: signedIn(), handler: ({ auth }) => auth.user });
44
+ export const DELETE = route({
45
+ guard: member({ roles: ['owner', 'admin'] }), // 403 no_active_organization, not_a_member, forbidden
46
+ handler: ({ auth }) => projects.remove(auth.member.organizationId),
47
+ });
48
+ ```
37
49
  > The app entry must forward the request so middleware locals reach the render:
38
50
  > `export const render = (url, request) => renderRequestAsync(App, { url, request })`.
39
51
 
package/dist/client.cjs CHANGED
@@ -1,4 +1,4 @@
1
- /*! @fluixi/auth v1.0.0-alpha.92 | (c) 2026 Ibrahima Touré and Fluixi contributors | MIT */
1
+ /*! @fluixi/auth v1.0.0-alpha.93 | (c) 2026 Ibrahima Touré and Fluixi contributors | MIT */
2
2
  "use strict";
3
3
  var __defProp = Object.defineProperty;
4
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
@@ -43,7 +43,7 @@ function createAuthHooks(options) {
43
43
  if (sess) {
44
44
  const apply = () => store.set({ user: sess.user(), session: null });
45
45
  apply();
46
- (0, import_core.createRoot)(() => (0, import_core.createEffect)(apply));
46
+ (0, import_core.createRoot)(() => (0, import_core.createEffect)(apply, void 0, { name: "auth ← session" }));
47
47
  }
48
48
  const refresh = async () => {
49
49
  store.set(sess ? { user: sess.user(), session: null } : normalize(await options.client.getSession()));
package/dist/client.js CHANGED
@@ -29,7 +29,7 @@ export function createAuthHooks(options) {
29
29
  if (sess) {
30
30
  const apply = () => store.set({ user: sess.user(), session: null });
31
31
  apply(); // immediate, so a first protectedRoute read isn't stale
32
- createRoot(() => createEffect(apply));
32
+ createRoot(() => createEffect(apply, undefined, { name: 'auth ← session' }));
33
33
  }
34
34
  const refresh = async () => {
35
35
  store.set(sess ? { user: sess.user(), session: null } : normalize(await options.client.getSession()));
package/dist/client.mjs CHANGED
@@ -1,4 +1,4 @@
1
- /*! @fluixi/auth v1.0.0-alpha.92 | (c) 2026 Ibrahima Touré and Fluixi contributors | MIT */
1
+ /*! @fluixi/auth v1.0.0-alpha.93 | (c) 2026 Ibrahima Touré and Fluixi contributors | MIT */
2
2
 
3
3
  // src/client.ts
4
4
  import { createIsomorphicStore, createEffect, createRoot, env } from "@fluixi/core";
@@ -20,7 +20,7 @@ function createAuthHooks(options) {
20
20
  if (sess) {
21
21
  const apply = () => store.set({ user: sess.user(), session: null });
22
22
  apply();
23
- createRoot(() => createEffect(apply));
23
+ createRoot(() => createEffect(apply, void 0, { name: "auth ← session" }));
24
24
  }
25
25
  const refresh = async () => {
26
26
  store.set(sess ? { user: sess.user(), session: null } : normalize(await options.client.getSession()));
package/dist/cookies.cjs CHANGED
@@ -20,53 +20,24 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/cookies.ts
21
21
  var cookies_exports = {};
22
22
  __export(cookies_exports, {
23
- deleteCookie: () => deleteCookie,
23
+ deleteCookie: () => import_cookie2.deleteCookie,
24
24
  getCookie: () => getCookie,
25
25
  getCookies: () => getCookies,
26
- parseCookies: () => parseCookies,
27
- serializeCookie: () => serializeCookie
26
+ isValidCookieName: () => import_cookie2.isValidCookieName,
27
+ parseCookies: () => import_cookie2.parseCookies,
28
+ serializeCookie: () => import_cookie2.serializeCookie
28
29
  });
29
30
  module.exports = __toCommonJS(cookies_exports);
30
31
  var import_server = require("@fluixi/server");
31
- function parseCookies(header) {
32
- const out = {};
33
- if (!header) return out;
34
- for (const part of header.split(";")) {
35
- const eq = part.indexOf("=");
36
- if (eq < 0) continue;
37
- const k = part.slice(0, eq).trim();
38
- if (k) out[k] = decodeURIComponent(part.slice(eq + 1).trim());
39
- }
40
- return out;
41
- }
42
- function serializeCookie(name, value, opts = {}) {
43
- const o = {
44
- path: "/",
45
- httpOnly: true,
46
- secure: true,
47
- sameSite: "lax",
48
- ...opts
49
- };
50
- let s = `${name}=${encodeURIComponent(value)}`;
51
- if (o.maxAge != null) s += `; Max-Age=${Math.floor(o.maxAge)}`;
52
- if (o.expires) s += `; Expires=${o.expires.toUTCString()}`;
53
- if (o.domain) s += `; Domain=${o.domain}`;
54
- s += `; Path=${o.path}`;
55
- if (o.httpOnly) s += "; HttpOnly";
56
- if (o.secure) s += "; Secure";
57
- s += `; SameSite=${o.sameSite[0].toUpperCase()}${o.sameSite.slice(1)}`;
58
- return s;
59
- }
60
- function deleteCookie(name, opts = {}) {
61
- return serializeCookie(name, "", { ...opts, maxAge: 0 });
62
- }
32
+ var import_cookie = require("@fluixi/utils/cookie");
33
+ var import_cookie2 = require("@fluixi/utils/cookie");
63
34
  function requestHeaders() {
64
35
  const req = (0, import_server.getRequestEvent)()?.request;
65
36
  return req?.headers ?? null;
66
37
  }
67
38
  function getCookie(name) {
68
- return parseCookies(requestHeaders()?.get("cookie"))[name] ?? null;
39
+ return (0, import_cookie.parseCookies)(requestHeaders()?.get("cookie"))[name] ?? null;
69
40
  }
70
41
  function getCookies() {
71
- return parseCookies(requestHeaders()?.get("cookie"));
42
+ return (0, import_cookie.parseCookies)(requestHeaders()?.get("cookie"));
72
43
  }
package/dist/cookies.d.ts CHANGED
@@ -1,18 +1,5 @@
1
- export interface CookieOptions {
2
- maxAge?: number;
3
- expires?: Date;
4
- path?: string;
5
- domain?: string;
6
- secure?: boolean;
7
- httpOnly?: boolean;
8
- sameSite?: 'strict' | 'lax' | 'none';
9
- }
10
- /** Parse a `Cookie:` header into a record. */
11
- export declare function parseCookies(header: string | null | undefined): Record<string, string>;
12
- /** Serialize a cookie into a `Set-Cookie` value. Secure by default. */
13
- export declare function serializeCookie(name: string, value: string, opts?: CookieOptions): string;
14
- /** A `Set-Cookie` string that deletes a cookie (Max-Age=0). */
15
- export declare function deleteCookie(name: string, opts?: Pick<CookieOptions, 'path' | 'domain'>): string;
1
+ export { parseCookies, serializeCookie, deleteCookie, isValidCookieName } from '@fluixi/utils/cookie';
2
+ export type { CookieOptions } from '@fluixi/utils/cookie';
16
3
  /** Read one cookie from the CURRENT request (request-scoped). */
17
4
  export declare function getCookie(name: string): string | null;
18
5
  /** All cookies on the current request. */
@@ -1 +1 @@
1
- {"version":3,"file":"cookies.d.ts","sourceRoot":"","sources":["../src/cookies.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,aAAa;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,IAAI,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,QAAQ,GAAG,KAAK,GAAG,MAAM,CAAC;CACtC;AAED,8CAA8C;AAC9C,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAUtF;AAED,uEAAuE;AACvE,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,GAAE,aAAkB,GAAG,MAAM,CAiB7F;AAED,+DAA+D;AAC/D,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,GAAE,IAAI,CAAC,aAAa,EAAE,MAAM,GAAG,QAAQ,CAAM,GAAG,MAAM,CAEpG;AAOD,iEAAiE;AACjE,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAErD;AAED,0CAA0C;AAC1C,wBAAgB,UAAU,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAEnD"}
1
+ {"version":3,"file":"cookies.d.ts","sourceRoot":"","sources":["../src/cookies.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AACtG,YAAY,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAO1D,iEAAiE;AACjE,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAErD;AAED,0CAA0C;AAC1C,wBAAgB,UAAU,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAEnD"}
package/dist/cookies.js CHANGED
@@ -1,53 +1,10 @@
1
1
  /**
2
- * Cookie API: proper, secure-by-default cookie management for auth (and anything else).
3
- * Parsing + request-scoped reads work standalone; `serializeCookie` builds a spec-compliant
4
- * `Set-Cookie` string with safe defaults (HttpOnly, Secure, SameSite=Lax, Path=/) that
5
- * middleware appends to its Response (`response.headers.append('set-cookie', …)`).
2
+ * Request-scoped cookie reads. The codec lives in `@fluixi/utils/cookie` and is re-exported
3
+ * here; middleware appends `serializeCookie` output to its Response's `set-cookie` header.
6
4
  */
7
5
  import { getRequestEvent } from '@fluixi/server';
8
- /** Parse a `Cookie:` header into a record. */
9
- export function parseCookies(header) {
10
- const out = {};
11
- if (!header)
12
- return out;
13
- for (const part of header.split(';')) {
14
- const eq = part.indexOf('=');
15
- if (eq < 0)
16
- continue;
17
- const k = part.slice(0, eq).trim();
18
- if (k)
19
- out[k] = decodeURIComponent(part.slice(eq + 1).trim());
20
- }
21
- return out;
22
- }
23
- /** Serialize a cookie into a `Set-Cookie` value. Secure by default. */
24
- export function serializeCookie(name, value, opts = {}) {
25
- const o = {
26
- path: '/',
27
- httpOnly: true,
28
- secure: true,
29
- sameSite: 'lax',
30
- ...opts,
31
- };
32
- let s = `${name}=${encodeURIComponent(value)}`;
33
- if (o.maxAge != null)
34
- s += `; Max-Age=${Math.floor(o.maxAge)}`;
35
- if (o.expires)
36
- s += `; Expires=${o.expires.toUTCString()}`;
37
- if (o.domain)
38
- s += `; Domain=${o.domain}`;
39
- s += `; Path=${o.path}`;
40
- if (o.httpOnly)
41
- s += '; HttpOnly';
42
- if (o.secure)
43
- s += '; Secure';
44
- s += `; SameSite=${o.sameSite[0].toUpperCase()}${o.sameSite.slice(1)}`;
45
- return s;
46
- }
47
- /** A `Set-Cookie` string that deletes a cookie (Max-Age=0). */
48
- export function deleteCookie(name, opts = {}) {
49
- return serializeCookie(name, '', { ...opts, maxAge: 0 });
50
- }
6
+ import { parseCookies } from '@fluixi/utils/cookie';
7
+ export { parseCookies, serializeCookie, deleteCookie, isValidCookieName } from '@fluixi/utils/cookie';
51
8
  function requestHeaders() {
52
9
  const req = getRequestEvent()?.request;
53
10
  return req?.headers ?? null;
package/dist/cookies.mjs CHANGED
@@ -1,37 +1,7 @@
1
1
  // src/cookies.ts
2
2
  import { getRequestEvent } from "@fluixi/server";
3
- function parseCookies(header) {
4
- const out = {};
5
- if (!header) return out;
6
- for (const part of header.split(";")) {
7
- const eq = part.indexOf("=");
8
- if (eq < 0) continue;
9
- const k = part.slice(0, eq).trim();
10
- if (k) out[k] = decodeURIComponent(part.slice(eq + 1).trim());
11
- }
12
- return out;
13
- }
14
- function serializeCookie(name, value, opts = {}) {
15
- const o = {
16
- path: "/",
17
- httpOnly: true,
18
- secure: true,
19
- sameSite: "lax",
20
- ...opts
21
- };
22
- let s = `${name}=${encodeURIComponent(value)}`;
23
- if (o.maxAge != null) s += `; Max-Age=${Math.floor(o.maxAge)}`;
24
- if (o.expires) s += `; Expires=${o.expires.toUTCString()}`;
25
- if (o.domain) s += `; Domain=${o.domain}`;
26
- s += `; Path=${o.path}`;
27
- if (o.httpOnly) s += "; HttpOnly";
28
- if (o.secure) s += "; Secure";
29
- s += `; SameSite=${o.sameSite[0].toUpperCase()}${o.sameSite.slice(1)}`;
30
- return s;
31
- }
32
- function deleteCookie(name, opts = {}) {
33
- return serializeCookie(name, "", { ...opts, maxAge: 0 });
34
- }
3
+ import { parseCookies } from "@fluixi/utils/cookie";
4
+ import { parseCookies as parseCookies2, serializeCookie, deleteCookie, isValidCookieName } from "@fluixi/utils/cookie";
35
5
  function requestHeaders() {
36
6
  const req = getRequestEvent()?.request;
37
7
  return req?.headers ?? null;
@@ -46,6 +16,7 @@ export {
46
16
  deleteCookie,
47
17
  getCookie,
48
18
  getCookies,
49
- parseCookies,
19
+ isValidCookieName,
20
+ parseCookies2 as parseCookies,
50
21
  serializeCookie
51
22
  };
@@ -0,0 +1,52 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/remote.ts
21
+ var remote_exports = {};
22
+ __export(remote_exports, {
23
+ remoteAuth: () => remoteAuth
24
+ });
25
+ module.exports = __toCommonJS(remote_exports);
26
+ function remoteAuth(options) {
27
+ const base = new URL((options.basePath ?? "/api/auth").replace(/\/$/, "") + "/", options.url);
28
+ const call = (input, init) => (options.fetch ?? globalThis.fetch)(input, init);
29
+ return {
30
+ async handler(request) {
31
+ const url = new URL(request.url);
32
+ const hasBody = request.method !== "GET" && request.method !== "HEAD";
33
+ return call(new URL(url.pathname + url.search, base), {
34
+ method: request.method,
35
+ headers: request.headers,
36
+ body: hasBody ? request.body : void 0,
37
+ redirect: "manual",
38
+ ...hasBody ? { duplex: "half" } : {}
39
+ });
40
+ },
41
+ api: {
42
+ async getSession({ headers }) {
43
+ const cookie = headers.get("cookie");
44
+ if (!cookie) return null;
45
+ const response = await call(new URL("get-session", base), { headers: { cookie, accept: "application/json" } });
46
+ if (!response.ok) return null;
47
+ const body = await response.json().catch(() => null);
48
+ return body && body.user ? { user: body.user, session: body.session ?? null } : null;
49
+ }
50
+ }
51
+ };
52
+ }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * An auth engine that lives in another service. A web app whose Better Auth runs in its API
3
+ * registers this with `defineAuth(remoteAuth({ url }))`; `authMiddleware()`, `requireUser()` and
4
+ * the client hooks then work as they would with the engine in-process, each session read being
5
+ * one request to the API with the visitor's cookie.
6
+ *
7
+ * The API's guards (`member()`, `hasPermission`) call the engine directly and stay there: a web
8
+ * app reads the session, the API decides what it may do.
9
+ */
10
+ import type { BetterAuthServer } from './types.js';
11
+ export interface RemoteAuthOptions {
12
+ /** The API's origin, `http://localhost:3301`. */
13
+ url: string;
14
+ /** Where the API mounts its auth routes. `/api/auth` by default. */
15
+ basePath?: string;
16
+ /** `globalThis.fetch` by default. */
17
+ fetch?: typeof fetch;
18
+ }
19
+ export declare function remoteAuth(options: RemoteAuthOptions): BetterAuthServer;
20
+ //# sourceMappingURL=remote.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"remote.d.ts","sourceRoot":"","sources":["../src/remote.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAEnD,MAAM,WAAW,iBAAiB;IAChC,iDAAiD;IACjD,GAAG,EAAE,MAAM,CAAC;IACZ,oEAAoE;IACpE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,qCAAqC;IACrC,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;CACtB;AAED,wBAAgB,UAAU,CAAC,OAAO,EAAE,iBAAiB,GAAG,gBAAgB,CA4BvE"}
package/dist/remote.js ADDED
@@ -0,0 +1,30 @@
1
+ export function remoteAuth(options) {
2
+ const base = new URL((options.basePath ?? '/api/auth').replace(/\/$/, '') + '/', options.url);
3
+ const call = (input, init) => (options.fetch ?? globalThis.fetch)(input, init);
4
+ return {
5
+ async handler(request) {
6
+ const url = new URL(request.url);
7
+ const hasBody = request.method !== 'GET' && request.method !== 'HEAD';
8
+ return call(new URL(url.pathname + url.search, base), {
9
+ method: request.method,
10
+ headers: request.headers,
11
+ body: hasBody ? request.body : undefined,
12
+ redirect: 'manual',
13
+ ...(hasBody ? { duplex: 'half' } : {}),
14
+ });
15
+ },
16
+ api: {
17
+ async getSession({ headers }) {
18
+ const cookie = headers.get('cookie');
19
+ // No cookie, no session: spare the API a request from every visitor.
20
+ if (!cookie)
21
+ return null;
22
+ const response = await call(new URL('get-session', base), { headers: { cookie, accept: 'application/json' } });
23
+ if (!response.ok)
24
+ return null;
25
+ const body = (await response.json().catch(() => null));
26
+ return body && body.user ? { user: body.user, session: body.session ?? null } : null;
27
+ },
28
+ },
29
+ };
30
+ }
@@ -0,0 +1,31 @@
1
+ // src/remote.ts
2
+ function remoteAuth(options) {
3
+ const base = new URL((options.basePath ?? "/api/auth").replace(/\/$/, "") + "/", options.url);
4
+ const call = (input, init) => (options.fetch ?? globalThis.fetch)(input, init);
5
+ return {
6
+ async handler(request) {
7
+ const url = new URL(request.url);
8
+ const hasBody = request.method !== "GET" && request.method !== "HEAD";
9
+ return call(new URL(url.pathname + url.search, base), {
10
+ method: request.method,
11
+ headers: request.headers,
12
+ body: hasBody ? request.body : void 0,
13
+ redirect: "manual",
14
+ ...hasBody ? { duplex: "half" } : {}
15
+ });
16
+ },
17
+ api: {
18
+ async getSession({ headers }) {
19
+ const cookie = headers.get("cookie");
20
+ if (!cookie) return null;
21
+ const response = await call(new URL("get-session", base), { headers: { cookie, accept: "application/json" } });
22
+ if (!response.ok) return null;
23
+ const body = await response.json().catch(() => null);
24
+ return body && body.user ? { user: body.user, session: body.session ?? null } : null;
25
+ }
26
+ }
27
+ };
28
+ }
29
+ export {
30
+ remoteAuth
31
+ };
package/dist/server.cjs CHANGED
@@ -1,4 +1,4 @@
1
- /*! @fluixi/auth v1.0.0-alpha.92 | (c) 2026 Ibrahima Touré and Fluixi contributors | MIT */
1
+ /*! @fluixi/auth v1.0.0-alpha.93 | (c) 2026 Ibrahima Touré and Fluixi contributors | MIT */
2
2
  "use strict";
3
3
  var __defProp = Object.defineProperty;
4
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
@@ -24,17 +24,20 @@ __export(server_exports, {
24
24
  AuthError: () => AuthError,
25
25
  authMiddleware: () => authMiddleware,
26
26
  defineAuth: () => defineAuth,
27
- deleteCookie: () => deleteCookie,
27
+ deleteCookie: () => import_cookie2.deleteCookie,
28
28
  getAuth: () => getAuth,
29
29
  getCookie: () => getCookie,
30
30
  getCookies: () => getCookies,
31
+ member: () => member,
31
32
  mountAuthRoutes: () => mountAuthRoutes,
32
33
  optionalUser: () => optionalUser,
33
- parseCookies: () => parseCookies,
34
+ parseCookies: () => import_cookie2.parseCookies,
35
+ remoteAuth: () => remoteAuth,
34
36
  requirePermission: () => requirePermission,
35
37
  requireRole: () => requireRole,
36
38
  requireUser: () => requireUser,
37
- serializeCookie: () => serializeCookie
39
+ serializeCookie: () => import_cookie2.serializeCookie,
40
+ signedIn: () => signedIn
38
41
  });
39
42
  module.exports = __toCommonJS(server_exports);
40
43
  var import_server2 = require("@fluixi/server");
@@ -44,47 +47,46 @@ var AUTH_LOCALS_KEY = "auth";
44
47
 
45
48
  // src/cookies.ts
46
49
  var import_server = require("@fluixi/server");
47
- function parseCookies(header) {
48
- const out = {};
49
- if (!header) return out;
50
- for (const part of header.split(";")) {
51
- const eq = part.indexOf("=");
52
- if (eq < 0) continue;
53
- const k = part.slice(0, eq).trim();
54
- if (k) out[k] = decodeURIComponent(part.slice(eq + 1).trim());
55
- }
56
- return out;
57
- }
58
- function serializeCookie(name, value, opts = {}) {
59
- const o = {
60
- path: "/",
61
- httpOnly: true,
62
- secure: true,
63
- sameSite: "lax",
64
- ...opts
65
- };
66
- let s = `${name}=${encodeURIComponent(value)}`;
67
- if (o.maxAge != null) s += `; Max-Age=${Math.floor(o.maxAge)}`;
68
- if (o.expires) s += `; Expires=${o.expires.toUTCString()}`;
69
- if (o.domain) s += `; Domain=${o.domain}`;
70
- s += `; Path=${o.path}`;
71
- if (o.httpOnly) s += "; HttpOnly";
72
- if (o.secure) s += "; Secure";
73
- s += `; SameSite=${o.sameSite[0].toUpperCase()}${o.sameSite.slice(1)}`;
74
- return s;
75
- }
76
- function deleteCookie(name, opts = {}) {
77
- return serializeCookie(name, "", { ...opts, maxAge: 0 });
78
- }
50
+ var import_cookie = require("@fluixi/utils/cookie");
51
+ var import_cookie2 = require("@fluixi/utils/cookie");
79
52
  function requestHeaders() {
80
53
  const req = (0, import_server.getRequestEvent)()?.request;
81
54
  return req?.headers ?? null;
82
55
  }
83
56
  function getCookie(name) {
84
- return parseCookies(requestHeaders()?.get("cookie"))[name] ?? null;
57
+ return (0, import_cookie.parseCookies)(requestHeaders()?.get("cookie"))[name] ?? null;
85
58
  }
86
59
  function getCookies() {
87
- return parseCookies(requestHeaders()?.get("cookie"));
60
+ return (0, import_cookie.parseCookies)(requestHeaders()?.get("cookie"));
61
+ }
62
+
63
+ // src/remote.ts
64
+ function remoteAuth(options) {
65
+ const base = new URL((options.basePath ?? "/api/auth").replace(/\/$/, "") + "/", options.url);
66
+ const call = (input, init) => (options.fetch ?? globalThis.fetch)(input, init);
67
+ return {
68
+ async handler(request) {
69
+ const url = new URL(request.url);
70
+ const hasBody = request.method !== "GET" && request.method !== "HEAD";
71
+ return call(new URL(url.pathname + url.search, base), {
72
+ method: request.method,
73
+ headers: request.headers,
74
+ body: hasBody ? request.body : void 0,
75
+ redirect: "manual",
76
+ ...hasBody ? { duplex: "half" } : {}
77
+ });
78
+ },
79
+ api: {
80
+ async getSession({ headers }) {
81
+ const cookie = headers.get("cookie");
82
+ if (!cookie) return null;
83
+ const response = await call(new URL("get-session", base), { headers: { cookie, accept: "application/json" } });
84
+ if (!response.ok) return null;
85
+ const body = await response.json().catch(() => null);
86
+ return body && body.user ? { user: body.user, session: body.session ?? null } : null;
87
+ }
88
+ }
89
+ };
88
90
  }
89
91
 
90
92
  // src/server.ts
@@ -118,10 +120,11 @@ function authMiddleware() {
118
120
  };
119
121
  }
120
122
  var AuthError = class extends Error {
121
- constructor(status, redirectTo, message) {
123
+ constructor(status, redirectTo, message, code) {
122
124
  super(message ?? (status === 401 ? "Unauthorized" : "Forbidden"));
123
125
  this.status = status;
124
126
  this.redirectTo = redirectTo;
127
+ this.code = code;
125
128
  this.name = "AuthError";
126
129
  }
127
130
  };
@@ -146,3 +149,55 @@ async function requirePermission(check, opts) {
146
149
  const ok = typeof check === "function" ? check() : check;
147
150
  if (!ok) throw new AuthError(403, opts?.redirectTo);
148
151
  }
152
+ var SESSION_COOKIE = {
153
+ type: "apiKey",
154
+ in: "cookie",
155
+ name: "better-auth.session_token",
156
+ description: "Set by sign-in. Named `__Secure-better-auth.session_token` over HTTPS."
157
+ };
158
+ async function sessionFor(request) {
159
+ const resolved = (0, import_server2.getLocals)()[AUTH_LOCALS_KEY];
160
+ return resolved ?? getAuth().getSession(request);
161
+ }
162
+ function signedIn(options = {}) {
163
+ return {
164
+ async check(request) {
165
+ const { user, session } = await sessionFor(request);
166
+ if (!user || !session) throw new AuthError(401);
167
+ return { user, session };
168
+ },
169
+ security: { name: "session", scheme: options.scheme ?? SESSION_COOKIE },
170
+ refuses: [401]
171
+ };
172
+ }
173
+ function member(options = {}) {
174
+ const signed = signedIn(options);
175
+ return {
176
+ async check(request) {
177
+ const auth = await signed.check(request);
178
+ if (!auth.session.activeOrganizationId) {
179
+ throw new AuthError(403, void 0, "Choose an organisation first.", "no_active_organization");
180
+ }
181
+ const api = getAuth().raw.api;
182
+ if (!api.getActiveMember || !api.hasPermission) {
183
+ throw new Error("[@fluixi/auth] member() needs Better Auth's organization() plugin.");
184
+ }
185
+ const membership = await api.getActiveMember({ headers: request.headers });
186
+ if (!membership) {
187
+ throw new AuthError(403, void 0, "You are not a member of this organisation.", "not_a_member");
188
+ }
189
+ if (options.roles) {
190
+ const held = membership.role.split(",").map((role2) => role2.trim());
191
+ if (!options.roles.some((role2) => held.includes(role2))) throw new AuthError(403);
192
+ }
193
+ if (options.permissions) {
194
+ const { success } = await api.hasPermission({ headers: request.headers, body: { permissions: options.permissions } });
195
+ if (!success) throw new AuthError(403);
196
+ }
197
+ const { id, organizationId, userId, role } = membership;
198
+ return { ...auth, member: { id, organizationId, userId, role } };
199
+ },
200
+ security: signed.security,
201
+ refuses: [401, 403]
202
+ };
203
+ }