@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 +12 -0
- package/dist/client.cjs +2 -2
- package/dist/client.js +1 -1
- package/dist/client.mjs +2 -2
- package/dist/cookies.cjs +8 -37
- package/dist/cookies.d.ts +2 -15
- package/dist/cookies.d.ts.map +1 -1
- package/dist/cookies.js +4 -47
- package/dist/cookies.mjs +4 -33
- package/dist/remote.cjs +52 -0
- package/dist/remote.d.ts +20 -0
- package/dist/remote.d.ts.map +1 -0
- package/dist/remote.js +30 -0
- package/dist/remote.mjs +31 -0
- package/dist/server.cjs +94 -39
- package/dist/server.d.ts +78 -4
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +86 -2
- package/dist/server.mjs +91 -36
- package/dist/tsconfig.lib.tsbuildinfo +1 -1
- package/dist/types.d.ts +2 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +14 -11
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.
|
|
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.
|
|
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
|
-
|
|
27
|
-
|
|
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
|
-
|
|
32
|
-
|
|
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
|
|
2
|
-
|
|
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. */
|
package/dist/cookies.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cookies.d.ts","sourceRoot":"","sources":["../src/cookies.ts"],"names":[],"mappings":"
|
|
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
|
-
*
|
|
3
|
-
*
|
|
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
|
-
|
|
9
|
-
export
|
|
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
|
-
|
|
4
|
-
|
|
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
|
-
|
|
19
|
+
isValidCookieName,
|
|
20
|
+
parseCookies2 as parseCookies,
|
|
50
21
|
serializeCookie
|
|
51
22
|
};
|
package/dist/remote.cjs
ADDED
|
@@ -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
|
+
}
|
package/dist/remote.d.ts
ADDED
|
@@ -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
|
+
}
|
package/dist/remote.mjs
ADDED
|
@@ -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.
|
|
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
|
-
|
|
48
|
-
|
|
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
|
+
}
|