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

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/dist/server.d.ts CHANGED
@@ -8,7 +8,7 @@
8
8
  * AND server functions (same request context).
9
9
  */
10
10
  import type { Middleware } from '@fluixi/start';
11
- import { type AuthUser, type BetterAuthServer, type SessionData } from './types.js';
11
+ import { type AuthSession, type AuthUser, type BetterAuthServer, type SessionData } from './types.js';
12
12
  export interface FluixiAuth<U = AuthUser> {
13
13
  /** The underlying Better Auth instance. */
14
14
  raw: BetterAuthServer;
@@ -27,11 +27,15 @@ export declare function mountAuthRoutes(): (request: Request) => Promise<Respons
27
27
  * Place it in `src/middleware.ts`: `export default defineMiddleware([ authMiddleware() ])`.
28
28
  */
29
29
  export declare function authMiddleware(): Middleware;
30
- /** Thrown by the require* helpers; carries the HTTP status + optional redirect target. */
30
+ /**
31
+ * Thrown by the require* helpers and the guards; carries the HTTP status, an optional redirect
32
+ * target, and a `code` when the refusal is more precise than 401 or 403 alone.
33
+ */
31
34
  export declare class AuthError extends Error {
32
35
  status: 401 | 403;
33
36
  redirectTo?: string | undefined;
34
- constructor(status: 401 | 403, redirectTo?: string | undefined, message?: string);
37
+ code?: string | undefined;
38
+ constructor(status: 401 | 403, redirectTo?: string | undefined, message?: string, code?: string | undefined);
35
39
  }
36
40
  /** The current user or null: never throws. */
37
41
  export declare function optionalUser<U = AuthUser>(): Promise<U | null>;
@@ -52,6 +56,76 @@ export declare function requireRole<U extends {
52
56
  export declare function requirePermission(check: boolean | (() => boolean), opts?: {
53
57
  redirectTo?: string;
54
58
  }): Promise<void>;
59
+ export interface GuardOptions {
60
+ /** The security scheme the API description gives, when the cookie is renamed or a token is used. */
61
+ scheme?: Record<string, unknown>;
62
+ }
63
+ /** A signed-in caller's session, as a guard hands it to the handler. */
64
+ export interface SignedIn<U = AuthUser> {
65
+ user: U;
66
+ session: AuthSession;
67
+ }
68
+ /** The caller's membership of the organisation their session has active. */
69
+ export interface ActiveMember {
70
+ id: string;
71
+ organizationId: string;
72
+ userId: string;
73
+ /** One role, or several comma-separated, as Better Auth stores them. */
74
+ role: string;
75
+ }
76
+ /**
77
+ * An `@fluixi/api` guard: the caller must be signed in. The handler gets `auth.user` and
78
+ * `auth.session`. Refuses with 401.
79
+ *
80
+ * ```ts
81
+ * export const GET = route({ guard: signedIn(), handler: ({ auth }) => auth.user });
82
+ * ```
83
+ */
84
+ export declare function signedIn<U = AuthUser>(options?: GuardOptions): {
85
+ check(request: Request): Promise<SignedIn<U>>;
86
+ security: {
87
+ name: string;
88
+ scheme: {
89
+ type: string;
90
+ in: string;
91
+ name: string;
92
+ description: string;
93
+ } | Record<string, unknown>;
94
+ };
95
+ refuses: readonly [401];
96
+ };
97
+ /**
98
+ * An `@fluixi/api` guard: the caller must be a member of the organisation their session has
99
+ * active, with one of `roles` if given, and every one of `permissions` if given (checked by
100
+ * Better Auth's access control). The handler gets `auth.member.organizationId`, which is the
101
+ * tenant every query of that route is scoped to.
102
+ *
103
+ * Refuses with 401 when signed out, else 403: `no_active_organization` when none is active,
104
+ * `not_a_member` when the membership is gone, `forbidden` for a role or permission.
105
+ *
106
+ * ```ts
107
+ * export const DELETE = route({ guard: member({ roles: ['owner', 'admin'] }), handler });
108
+ * ```
109
+ */
110
+ export declare function member<U = AuthUser>(options?: GuardOptions & {
111
+ roles?: readonly string[];
112
+ permissions?: Record<string, string[]>;
113
+ }): {
114
+ check(request: Request): Promise<SignedIn<U> & {
115
+ member: ActiveMember;
116
+ }>;
117
+ security: {
118
+ name: string;
119
+ scheme: {
120
+ type: string;
121
+ in: string;
122
+ name: string;
123
+ description: string;
124
+ } | Record<string, unknown>;
125
+ };
126
+ refuses: readonly [401, 403];
127
+ };
55
128
  export { parseCookies, serializeCookie, deleteCookie, getCookie, getCookies, type CookieOptions, } from './cookies.js';
56
- export type { SessionData, AuthUser, BetterAuthServer } from './types.js';
129
+ export { remoteAuth, type RemoteAuthOptions } from './remote.js';
130
+ export type { SessionData, AuthSession, AuthUser, BetterAuthServer } from './types.js';
57
131
  //# sourceMappingURL=server.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAEhD,OAAO,EAEL,KAAK,QAAQ,EACb,KAAK,gBAAgB,EACrB,KAAK,WAAW,EACjB,MAAM,YAAY,CAAC;AAEpB,MAAM,WAAW,UAAU,CAAC,CAAC,GAAG,QAAQ;IACtC,2CAA2C;IAC3C,GAAG,EAAE,gBAAgB,CAAC;IACtB,8BAA8B;IAC9B,OAAO,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;IACjD,kEAAkE;IAClE,UAAU,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC;CAC3D;AAMD,wBAAgB,UAAU,CAAC,CAAC,GAAG,QAAQ,EAAE,QAAQ,EAAE,gBAAgB,GAAG,UAAU,CAAC,CAAC,CAAC,CAWlF;AAED,wBAAgB,OAAO,CAAC,CAAC,GAAG,QAAQ,KAAK,UAAU,CAAC,CAAC,CAAC,CAKrD;AAED,6EAA6E;AAC7E,wBAAgB,eAAe,IAAI,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAEzE;AAED;;;;GAIG;AACH,wBAAgB,cAAc,IAAI,UAAU,CAM3C;AAED,0FAA0F;AAC1F,qBAAa,SAAU,SAAQ,KAAK;IAEzB,MAAM,EAAE,GAAG,GAAG,GAAG;IACjB,UAAU,CAAC,EAAE,MAAM;gBADnB,MAAM,EAAE,GAAG,GAAG,GAAG,EACjB,UAAU,CAAC,EAAE,MAAM,YAAA,EAC1B,OAAO,CAAC,EAAE,MAAM;CAKnB;AAMD,8CAA8C;AAC9C,wBAAsB,YAAY,CAAC,CAAC,GAAG,QAAQ,KAAK,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAEpE;AAED,+DAA+D;AAC/D,wBAAsB,WAAW,CAAC,CAAC,GAAG,QAAQ,EAAE,IAAI,CAAC,EAAE;IAAE,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,CAAC,CAAC,CAI1F;AAED,0DAA0D;AAC1D,wBAAsB,WAAW,CAAC,CAAC,SAAS;IAAE,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,QAAQ,EACtE,IAAI,EAAE,MAAM,EACZ,IAAI,CAAC,EAAE;IAAE,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,GAC7B,OAAO,CAAC,CAAC,CAAC,CAIZ;AAED;;;GAGG;AACH,wBAAsB,iBAAiB,CACrC,KAAK,EAAE,OAAO,GAAG,CAAC,MAAM,OAAO,CAAC,EAChC,IAAI,CAAC,EAAE;IAAE,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,GAC7B,OAAO,CAAC,IAAI,CAAC,CAIf;AAGD,OAAO,EACL,YAAY,EACZ,eAAe,EACf,YAAY,EACZ,SAAS,EACT,UAAU,EACV,KAAK,aAAa,GACnB,MAAM,cAAc,CAAC;AAEtB,YAAY,EAAE,WAAW,EAAE,QAAQ,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
package/dist/server.js CHANGED
@@ -37,14 +37,19 @@ export function authMiddleware() {
37
37
  return next();
38
38
  };
39
39
  }
40
- /** Thrown by the require* helpers; carries the HTTP status + optional redirect target. */
40
+ /**
41
+ * Thrown by the require* helpers and the guards; carries the HTTP status, an optional redirect
42
+ * target, and a `code` when the refusal is more precise than 401 or 403 alone.
43
+ */
41
44
  export class AuthError extends Error {
42
45
  status;
43
46
  redirectTo;
44
- constructor(status, redirectTo, message) {
47
+ code;
48
+ constructor(status, redirectTo, message, code) {
45
49
  super(message ?? (status === 401 ? 'Unauthorized' : 'Forbidden'));
46
50
  this.status = status;
47
51
  this.redirectTo = redirectTo;
52
+ this.code = code;
48
53
  this.name = 'AuthError';
49
54
  }
50
55
  }
@@ -79,5 +84,84 @@ export async function requirePermission(check, opts) {
79
84
  if (!ok)
80
85
  throw new AuthError(403, opts?.redirectTo);
81
86
  }
87
+ /** Better Auth's session cookie, as an API description names it. */
88
+ const SESSION_COOKIE = {
89
+ type: 'apiKey',
90
+ in: 'cookie',
91
+ name: 'better-auth.session_token',
92
+ description: 'Set by sign-in. Named `__Secure-better-auth.session_token` over HTTPS.',
93
+ };
94
+ /** The session for this request: from `authMiddleware()` when it ran, else resolved here. */
95
+ async function sessionFor(request) {
96
+ const resolved = getLocals()[AUTH_LOCALS_KEY];
97
+ return resolved ?? getAuth().getSession(request);
98
+ }
99
+ /**
100
+ * An `@fluixi/api` guard: the caller must be signed in. The handler gets `auth.user` and
101
+ * `auth.session`. Refuses with 401.
102
+ *
103
+ * ```ts
104
+ * export const GET = route({ guard: signedIn(), handler: ({ auth }) => auth.user });
105
+ * ```
106
+ */
107
+ export function signedIn(options = {}) {
108
+ return {
109
+ async check(request) {
110
+ const { user, session } = await sessionFor(request);
111
+ if (!user || !session)
112
+ throw new AuthError(401);
113
+ return { user, session };
114
+ },
115
+ security: { name: 'session', scheme: options.scheme ?? SESSION_COOKIE },
116
+ refuses: [401],
117
+ };
118
+ }
119
+ /**
120
+ * An `@fluixi/api` guard: the caller must be a member of the organisation their session has
121
+ * active, with one of `roles` if given, and every one of `permissions` if given (checked by
122
+ * Better Auth's access control). The handler gets `auth.member.organizationId`, which is the
123
+ * tenant every query of that route is scoped to.
124
+ *
125
+ * Refuses with 401 when signed out, else 403: `no_active_organization` when none is active,
126
+ * `not_a_member` when the membership is gone, `forbidden` for a role or permission.
127
+ *
128
+ * ```ts
129
+ * export const DELETE = route({ guard: member({ roles: ['owner', 'admin'] }), handler });
130
+ * ```
131
+ */
132
+ export function member(options = {}) {
133
+ const signed = signedIn(options);
134
+ return {
135
+ async check(request) {
136
+ const auth = await signed.check(request);
137
+ if (!auth.session.activeOrganizationId) {
138
+ throw new AuthError(403, undefined, 'Choose an organisation first.', 'no_active_organization');
139
+ }
140
+ const api = getAuth().raw.api;
141
+ if (!api.getActiveMember || !api.hasPermission) {
142
+ throw new Error("[@fluixi/auth] member() needs Better Auth's organization() plugin.");
143
+ }
144
+ const membership = await api.getActiveMember({ headers: request.headers });
145
+ if (!membership) {
146
+ throw new AuthError(403, undefined, 'You are not a member of this organisation.', 'not_a_member');
147
+ }
148
+ if (options.roles) {
149
+ const held = membership.role.split(',').map((role) => role.trim());
150
+ if (!options.roles.some((role) => held.includes(role)))
151
+ throw new AuthError(403);
152
+ }
153
+ if (options.permissions) {
154
+ const { success } = await api.hasPermission({ headers: request.headers, body: { permissions: options.permissions } });
155
+ if (!success)
156
+ throw new AuthError(403);
157
+ }
158
+ const { id, organizationId, userId, role } = membership;
159
+ return { ...auth, member: { id, organizationId, userId, role } };
160
+ },
161
+ security: signed.security,
162
+ refuses: [401, 403],
163
+ };
164
+ }
82
165
  // Cookie API: proper, secure-by-default cookie management (parse/serialize/request reads).
83
166
  export { parseCookies, serializeCookie, deleteCookie, getCookie, getCookies, } from './cookies.js';
167
+ export { remoteAuth } from './remote.js';
package/dist/server.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.94 | (c) 2026 Ibrahima Touré and Fluixi contributors | MIT */
2
2
 
3
3
  // src/server.ts
4
4
  import { getLocals, getRequestLocals } from "@fluixi/server";
@@ -8,38 +8,8 @@ var AUTH_LOCALS_KEY = "auth";
8
8
 
9
9
  // src/cookies.ts
10
10
  import { getRequestEvent } from "@fluixi/server";
11
- function parseCookies(header) {
12
- const out = {};
13
- if (!header) return out;
14
- for (const part of header.split(";")) {
15
- const eq = part.indexOf("=");
16
- if (eq < 0) continue;
17
- const k = part.slice(0, eq).trim();
18
- if (k) out[k] = decodeURIComponent(part.slice(eq + 1).trim());
19
- }
20
- return out;
21
- }
22
- function serializeCookie(name, value, opts = {}) {
23
- const o = {
24
- path: "/",
25
- httpOnly: true,
26
- secure: true,
27
- sameSite: "lax",
28
- ...opts
29
- };
30
- let s = `${name}=${encodeURIComponent(value)}`;
31
- if (o.maxAge != null) s += `; Max-Age=${Math.floor(o.maxAge)}`;
32
- if (o.expires) s += `; Expires=${o.expires.toUTCString()}`;
33
- if (o.domain) s += `; Domain=${o.domain}`;
34
- s += `; Path=${o.path}`;
35
- if (o.httpOnly) s += "; HttpOnly";
36
- if (o.secure) s += "; Secure";
37
- s += `; SameSite=${o.sameSite[0].toUpperCase()}${o.sameSite.slice(1)}`;
38
- return s;
39
- }
40
- function deleteCookie(name, opts = {}) {
41
- return serializeCookie(name, "", { ...opts, maxAge: 0 });
42
- }
11
+ import { parseCookies } from "@fluixi/utils/cookie";
12
+ import { parseCookies as parseCookies2, serializeCookie, deleteCookie, isValidCookieName } from "@fluixi/utils/cookie";
43
13
  function requestHeaders() {
44
14
  const req = getRequestEvent()?.request;
45
15
  return req?.headers ?? null;
@@ -51,6 +21,35 @@ function getCookies() {
51
21
  return parseCookies(requestHeaders()?.get("cookie"));
52
22
  }
53
23
 
24
+ // src/remote.ts
25
+ function remoteAuth(options) {
26
+ const base = new URL((options.basePath ?? "/api/auth").replace(/\/$/, "") + "/", options.url);
27
+ const call = (input, init) => (options.fetch ?? globalThis.fetch)(input, init);
28
+ return {
29
+ async handler(request) {
30
+ const url = new URL(request.url);
31
+ const hasBody = request.method !== "GET" && request.method !== "HEAD";
32
+ return call(new URL(url.pathname + url.search, base), {
33
+ method: request.method,
34
+ headers: request.headers,
35
+ body: hasBody ? request.body : void 0,
36
+ redirect: "manual",
37
+ ...hasBody ? { duplex: "half" } : {}
38
+ });
39
+ },
40
+ api: {
41
+ async getSession({ headers }) {
42
+ const cookie = headers.get("cookie");
43
+ if (!cookie) return null;
44
+ const response = await call(new URL("get-session", base), { headers: { cookie, accept: "application/json" } });
45
+ if (!response.ok) return null;
46
+ const body = await response.json().catch(() => null);
47
+ return body && body.user ? { user: body.user, session: body.session ?? null } : null;
48
+ }
49
+ }
50
+ };
51
+ }
52
+
54
53
  // src/server.ts
55
54
  var _auth = null;
56
55
  function defineAuth(instance) {
@@ -82,10 +81,11 @@ function authMiddleware() {
82
81
  };
83
82
  }
84
83
  var AuthError = class extends Error {
85
- constructor(status, redirectTo, message) {
84
+ constructor(status, redirectTo, message, code) {
86
85
  super(message ?? (status === 401 ? "Unauthorized" : "Forbidden"));
87
86
  this.status = status;
88
87
  this.redirectTo = redirectTo;
88
+ this.code = code;
89
89
  this.name = "AuthError";
90
90
  }
91
91
  };
@@ -110,6 +110,58 @@ async function requirePermission(check, opts) {
110
110
  const ok = typeof check === "function" ? check() : check;
111
111
  if (!ok) throw new AuthError(403, opts?.redirectTo);
112
112
  }
113
+ var SESSION_COOKIE = {
114
+ type: "apiKey",
115
+ in: "cookie",
116
+ name: "better-auth.session_token",
117
+ description: "Set by sign-in. Named `__Secure-better-auth.session_token` over HTTPS."
118
+ };
119
+ async function sessionFor(request) {
120
+ const resolved = getLocals()[AUTH_LOCALS_KEY];
121
+ return resolved ?? getAuth().getSession(request);
122
+ }
123
+ function signedIn(options = {}) {
124
+ return {
125
+ async check(request) {
126
+ const { user, session } = await sessionFor(request);
127
+ if (!user || !session) throw new AuthError(401);
128
+ return { user, session };
129
+ },
130
+ security: { name: "session", scheme: options.scheme ?? SESSION_COOKIE },
131
+ refuses: [401]
132
+ };
133
+ }
134
+ function member(options = {}) {
135
+ const signed = signedIn(options);
136
+ return {
137
+ async check(request) {
138
+ const auth = await signed.check(request);
139
+ if (!auth.session.activeOrganizationId) {
140
+ throw new AuthError(403, void 0, "Choose an organisation first.", "no_active_organization");
141
+ }
142
+ const api = getAuth().raw.api;
143
+ if (!api.getActiveMember || !api.hasPermission) {
144
+ throw new Error("[@fluixi/auth] member() needs Better Auth's organization() plugin.");
145
+ }
146
+ const membership = await api.getActiveMember({ headers: request.headers });
147
+ if (!membership) {
148
+ throw new AuthError(403, void 0, "You are not a member of this organisation.", "not_a_member");
149
+ }
150
+ if (options.roles) {
151
+ const held = membership.role.split(",").map((role2) => role2.trim());
152
+ if (!options.roles.some((role2) => held.includes(role2))) throw new AuthError(403);
153
+ }
154
+ if (options.permissions) {
155
+ const { success } = await api.hasPermission({ headers: request.headers, body: { permissions: options.permissions } });
156
+ if (!success) throw new AuthError(403);
157
+ }
158
+ const { id, organizationId, userId, role } = membership;
159
+ return { ...auth, member: { id, organizationId, userId, role } };
160
+ },
161
+ security: signed.security,
162
+ refuses: [401, 403]
163
+ };
164
+ }
113
165
  export {
114
166
  AuthError,
115
167
  authMiddleware,
@@ -118,11 +170,14 @@ export {
118
170
  getAuth,
119
171
  getCookie,
120
172
  getCookies,
173
+ member,
121
174
  mountAuthRoutes,
122
175
  optionalUser,
123
- parseCookies,
176
+ parseCookies2 as parseCookies,
177
+ remoteAuth,
124
178
  requirePermission,
125
179
  requireRole,
126
180
  requireUser,
127
- serializeCookie
181
+ serializeCookie,
182
+ signedIn
128
183
  };