@holeauth/hono 0.0.2-alpha.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Robert Julian Kratz <contact@holeauth.dev> (https://holeauth.dev)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,84 @@
1
+ import { Context, Hono, MiddlewareHandler } from 'hono';
2
+ import { HoleauthInstance, HoleauthConfig, IssuedTokens, SessionData, HoleauthPlugin, PluginsApi } from '@holeauth/core';
3
+ export { HoleauthConfig } from '@holeauth/core';
4
+ import { cookieName } from '@holeauth/core/cookies';
5
+ export { RequestRefreshResult, getSessionOrRefreshFromRequest } from '@holeauth/core/session';
6
+
7
+ interface DispatchOptions {
8
+ /** Used to strip the prefix from pathnames. Default '/api/auth'. */
9
+ basePath?: string;
10
+ /** Default post-signin redirect destination (SSO callback). */
11
+ defaultRedirect?: string;
12
+ }
13
+ /**
14
+ * Build the unified GET/POST dispatcher. Mounted under `/api/auth/[...holeauth]`.
15
+ *
16
+ * Core routes take precedence; plugin routes are matched only if no core
17
+ * route accepts the request.
18
+ */
19
+ declare function createDispatcher(instance: HoleauthInstance, opts?: DispatchOptions): (req: Request) => Promise<Response>;
20
+
21
+ /** Append one Set-Cookie header entry. Headers.append() preserves multiple values. */
22
+ declare function setCookie(headers: Headers, cookie: string): void;
23
+ /**
24
+ * Apply a freshly-issued token bundle to a Response. Writes access + refresh
25
+ * (httpOnly) plus the JS-readable CSRF cookie.
26
+ */
27
+ declare function writeAuthCookies(cfg: HoleauthConfig, headers: Headers, tokens: IssuedTokens): void;
28
+ declare function clearAuthCookies(cfg: HoleauthConfig, headers: Headers): void;
29
+ declare function writePending(cfg: HoleauthConfig, headers: Headers, pendingToken: string): void;
30
+ /** Parse a Cookie header into a map. Next gives us a cookies API but we also
31
+ * work off raw Request here for edge compatibility. */
32
+ declare function parseCookies(header: string | null): Record<string, string>;
33
+ declare function readCookie(req: Request, cfg: HoleauthConfig, kind: Parameters<typeof cookieName>[1]): string | undefined;
34
+ /** Returns true if the double-submit CSRF check passes. */
35
+ declare function checkCsrf(req: Request, cfg: HoleauthConfig): boolean;
36
+
37
+ type HoleauthHonoVariables = {
38
+ holeauthSession: SessionData | null;
39
+ };
40
+ type HonoHoleauth<Plugins extends readonly HoleauthPlugin<string, unknown>[] = []> = HoleauthInstance & PluginsApi<Plugins> & {
41
+ /** Mount with `app.route('/api/auth', auth.app)`. */
42
+ app: Hono;
43
+ };
44
+ /**
45
+ * Build a Hono sub-app that handles all holeauth core + plugin routes.
46
+ *
47
+ * ```ts
48
+ * const auth = createHonoAuth({ ... });
49
+ * app.route('/api/auth', auth.app);
50
+ * ```
51
+ */
52
+ declare function createHonoAuth<const Plugins extends readonly HoleauthPlugin<string, unknown>[] = []>(config: Omit<HoleauthConfig, 'plugins'> & {
53
+ plugins?: Plugins;
54
+ }, opts?: DispatchOptions): HonoHoleauth<Plugins>;
55
+ /** Build a Hono sub-app from an existing `HoleauthInstance`. */
56
+ declare function createHonoAuthApp(auth: HoleauthInstance, opts?: DispatchOptions): Hono;
57
+ /**
58
+ * Hono middleware that resolves `c.var.holeauthSession`. Use:
59
+ *
60
+ * ```ts
61
+ * app.use('*', holeauthHonoMiddleware(auth));
62
+ * app.get('/me', (c) => c.json({ session: c.get('holeauthSession') }));
63
+ * ```
64
+ */
65
+ declare function holeauthHonoMiddleware(auth: HoleauthInstance): MiddlewareHandler<{
66
+ Variables: HoleauthHonoVariables;
67
+ }>;
68
+ /** Read the current session from a Hono context. */
69
+ declare function getSession(c: Context, auth: HoleauthInstance): Promise<SessionData | null>;
70
+ interface HoleauthHonoContext {
71
+ c: Context;
72
+ req: Request;
73
+ session: SessionData | null;
74
+ /** True when the access token was silently rotated via the refresh token. */
75
+ refreshed: boolean;
76
+ auth: HoleauthInstance;
77
+ }
78
+ /**
79
+ * Build a tRPC context factory for Hono — pair with `@hono/trpc-server`. The
80
+ * returned function accepts the same shape passed by Hono's tRPC adapter.
81
+ */
82
+ declare function createHoleauthHonoContext(auth: HoleauthInstance): (_opts: unknown, c: Context) => Promise<HoleauthHonoContext>;
83
+
84
+ export { type DispatchOptions, type HoleauthHonoContext, type HoleauthHonoVariables, type HonoHoleauth, checkCsrf, clearAuthCookies, createDispatcher, createHoleauthHonoContext, createHonoAuth, createHonoAuthApp, getSession, holeauthHonoMiddleware, parseCookies, readCookie, setCookie, writeAuthCookies, writePending };
package/dist/index.js ADDED
@@ -0,0 +1,480 @@
1
+ // src/index.ts
2
+ import { Hono } from "hono";
3
+ import {
4
+ defineHoleauth
5
+ } from "@holeauth/core";
6
+ import { getSessionOrRefreshFromRequest } from "@holeauth/core/session";
7
+ import { cookieName as cookieName3 } from "@holeauth/core/cookies";
8
+
9
+ // src/dispatch.ts
10
+ import { HoleauthError, CsrfError } from "@holeauth/core/errors";
11
+ import { getRegistry } from "@holeauth/core";
12
+
13
+ // src/cookies.ts
14
+ import {
15
+ buildCookie,
16
+ deleteCookie,
17
+ serializeCookie,
18
+ cookieName,
19
+ verifyCsrf,
20
+ CSRF_HEADER
21
+ } from "@holeauth/core/cookies";
22
+ function setCookie(headers, cookie) {
23
+ headers.append("Set-Cookie", cookie);
24
+ }
25
+ function writeAuthCookies(cfg, headers, tokens) {
26
+ const accessTtl = cfg.tokens?.accessTtl ?? 900;
27
+ const refreshTtl = cfg.tokens?.refreshTtl ?? 2592e3;
28
+ setCookie(
29
+ headers,
30
+ serializeCookie(buildCookie(cfg, { kind: "access", value: tokens.accessToken, maxAge: accessTtl }))
31
+ );
32
+ setCookie(
33
+ headers,
34
+ serializeCookie(buildCookie(cfg, { kind: "refresh", value: tokens.refreshToken, maxAge: refreshTtl }))
35
+ );
36
+ setCookie(
37
+ headers,
38
+ serializeCookie(buildCookie(cfg, { kind: "csrf", value: tokens.csrfToken, maxAge: refreshTtl, httpOnly: false }))
39
+ );
40
+ }
41
+ function clearAuthCookies(cfg, headers) {
42
+ setCookie(headers, serializeCookie(deleteCookie(cfg, "access")));
43
+ setCookie(headers, serializeCookie(deleteCookie(cfg, "refresh")));
44
+ setCookie(headers, serializeCookie(deleteCookie(cfg, "csrf")));
45
+ setCookie(headers, serializeCookie(deleteCookie(cfg, "pending")));
46
+ }
47
+ function writePending(cfg, headers, pendingToken) {
48
+ const ttl = cfg.tokens?.pendingTtl ?? 300;
49
+ setCookie(
50
+ headers,
51
+ serializeCookie(buildCookie(cfg, { kind: "pending", value: pendingToken, maxAge: ttl }))
52
+ );
53
+ }
54
+ function parseCookies(header) {
55
+ const out = {};
56
+ if (!header) return out;
57
+ for (const part of header.split(";")) {
58
+ const i = part.indexOf("=");
59
+ if (i < 0) continue;
60
+ const k = part.slice(0, i).trim();
61
+ const v = decodeURIComponent(part.slice(i + 1).trim());
62
+ if (k) out[k] = v;
63
+ }
64
+ return out;
65
+ }
66
+ function readCookie(req, cfg, kind) {
67
+ const jar = parseCookies(req.headers.get("cookie"));
68
+ return jar[cookieName(cfg, kind)];
69
+ }
70
+ function checkCsrf(req, cfg) {
71
+ const cookie = readCookie(req, cfg, "csrf");
72
+ const header = req.headers.get(CSRF_HEADER);
73
+ return verifyCsrf(cookie, header ?? void 0);
74
+ }
75
+
76
+ // src/dispatch.ts
77
+ import {
78
+ buildCookie as buildCookie2,
79
+ serializeCookie as serializeCookie2,
80
+ deleteCookie as deleteCookie2,
81
+ cookieName as cookieName2
82
+ } from "@holeauth/core/cookies";
83
+ function json(body, init = {}) {
84
+ const headers = new Headers(init.headers);
85
+ headers.set("content-type", "application/json");
86
+ return new Response(JSON.stringify(body), { ...init, headers });
87
+ }
88
+ function errorResponse(e) {
89
+ if (e instanceof HoleauthError) {
90
+ return json({ error: { code: e.code, message: e.message } }, { status: e.status });
91
+ }
92
+ if (e instanceof Error && e.name === "HoleauthError" && typeof e.code === "string" && typeof e.status === "number") {
93
+ const err = e;
94
+ return json({ error: { code: err.code, message: err.message } }, { status: err.status });
95
+ }
96
+ console.error("[holeauth] Unhandled error in request dispatch:", e);
97
+ return json({ error: { code: "INTERNAL", message: "Internal error" } }, { status: 500 });
98
+ }
99
+ function getMeta(req) {
100
+ return {
101
+ ip: req.headers.get("x-forwarded-for")?.split(",")[0]?.trim() ?? req.headers.get("x-real-ip") ?? void 0,
102
+ userAgent: req.headers.get("user-agent") ?? void 0
103
+ };
104
+ }
105
+ async function parseBody(req) {
106
+ try {
107
+ return await req.json();
108
+ } catch {
109
+ return {};
110
+ }
111
+ }
112
+ function pathSegments(req, basePath) {
113
+ const url = new URL(req.url);
114
+ let p = url.pathname;
115
+ if (p.startsWith(basePath)) p = p.slice(basePath.length);
116
+ return p.split("/").filter(Boolean);
117
+ }
118
+ function writeTokens(cfg, result) {
119
+ const headers = new Headers({ "content-type": "application/json" });
120
+ if (result.kind === "ok") {
121
+ writeAuthCookies(cfg, headers, result.tokens);
122
+ setCookie(headers, serializeCookie2(deleteCookie2(cfg, "pending")));
123
+ return new Response(
124
+ JSON.stringify({ ok: true, user: publicUser(result.user), csrfToken: result.tokens.csrfToken }),
125
+ { status: 200, headers }
126
+ );
127
+ }
128
+ writePending(cfg, headers, result.pendingToken);
129
+ return new Response(
130
+ JSON.stringify({
131
+ ok: true,
132
+ pending: true,
133
+ pluginId: result.pluginId,
134
+ userId: result.userId,
135
+ data: result.data ?? null
136
+ }),
137
+ { status: 200, headers }
138
+ );
139
+ }
140
+ function publicUser(u) {
141
+ return { id: u.id, email: u.email, name: u.name ?? null, image: u.image ?? null };
142
+ }
143
+ function matchPluginRoute(routes, method, segs) {
144
+ for (const r of routes) {
145
+ if (r.method !== method) continue;
146
+ const rSegs = r.path.split("/").filter(Boolean);
147
+ if (rSegs.length !== segs.length) continue;
148
+ const params = {};
149
+ let ok = true;
150
+ for (let i = 0; i < rSegs.length; i++) {
151
+ const a = rSegs[i];
152
+ const b = segs[i];
153
+ if (a.startsWith(":")) {
154
+ params[a.slice(1)] = decodeURIComponent(b);
155
+ } else if (a !== b) {
156
+ ok = false;
157
+ break;
158
+ }
159
+ }
160
+ if (ok) return { route: r, params };
161
+ }
162
+ return null;
163
+ }
164
+ async function runPluginRoute(instance, route, req, params) {
165
+ const cfg = instance.config;
166
+ const registry = getRegistry(instance);
167
+ const jar = parseCookies(req.headers.get("cookie"));
168
+ const responseHeaders = new Headers();
169
+ const meta = getMeta(req);
170
+ if (route.requireCsrf) {
171
+ if (!checkCsrf(req, cfg)) throw new CsrfError();
172
+ }
173
+ const session = await (async () => {
174
+ const at = readCookie(req, cfg, "access");
175
+ return at ? instance.getSession(at) : null;
176
+ })();
177
+ if (route.requireAuth && !session) {
178
+ return json({ error: { code: "UNAUTHENTICATED", message: "authentication required" } }, { status: 401 });
179
+ }
180
+ const body = req.method === "POST" ? await parseBody(req) : {};
181
+ const ctx = {
182
+ req,
183
+ body: { ...body, ...params },
184
+ responseHeaders,
185
+ cookies: { get: (name) => jar[name] },
186
+ setCookie(spec) {
187
+ const secure = cfg.tokens?.cookieSecure ?? globalThis.process?.env?.NODE_ENV === "production";
188
+ const parts = [`${spec.name}=${encodeURIComponent(spec.value)}`];
189
+ parts.push(`Path=${spec.path ?? "/"}`);
190
+ if (spec.maxAge !== void 0) parts.push(`Max-Age=${spec.maxAge}`);
191
+ if (spec.httpOnly ?? true) parts.push("HttpOnly");
192
+ if (secure) parts.push("Secure");
193
+ const ss = spec.sameSite ?? cfg.tokens?.sameSite ?? "lax";
194
+ parts.push(`SameSite=${ss.charAt(0).toUpperCase()}${ss.slice(1)}`);
195
+ setCookie(responseHeaders, parts.join("; "));
196
+ },
197
+ async getSession() {
198
+ return session;
199
+ },
200
+ meta,
201
+ plugin: registry.ctx
202
+ };
203
+ const res = await route.handler(ctx);
204
+ if (responseHeaders.has("Set-Cookie")) {
205
+ const merged = new Headers(res.headers);
206
+ responseHeaders.forEach((v, k) => {
207
+ if (k.toLowerCase() === "set-cookie") merged.append("Set-Cookie", v);
208
+ else merged.set(k, v);
209
+ });
210
+ return new Response(res.body, { status: res.status, statusText: res.statusText, headers: merged });
211
+ }
212
+ return res;
213
+ }
214
+ function createDispatcher(instance, opts = {}) {
215
+ const basePath = opts.basePath ?? "/api/auth";
216
+ const cfg = instance.config;
217
+ const registry = getRegistry(instance);
218
+ return async function dispatch(req) {
219
+ const segs = pathSegments(req, basePath);
220
+ const method = req.method.toUpperCase();
221
+ try {
222
+ if (method === "GET" && segs[0] === "session" && !segs[1]) {
223
+ const at = readCookie(req, cfg, "access");
224
+ const session = at ? await instance.getSession(at) : null;
225
+ return json({ session });
226
+ }
227
+ if (method === "GET" && segs[0] === "csrf" && !segs[1]) {
228
+ const existing = readCookie(req, cfg, "csrf");
229
+ return json({ csrfToken: existing ?? null });
230
+ }
231
+ if (method === "GET" && segs[0] === "invite" && segs[1] === "info" && !segs[2]) {
232
+ const url = new URL(req.url);
233
+ const token = url.searchParams.get("token") ?? "";
234
+ if (!token) return json({ error: { code: "MISSING_TOKEN", message: "token required" } }, { status: 400 });
235
+ const info = await instance.getInviteInfo({ token });
236
+ return json({
237
+ invite: {
238
+ email: info.email,
239
+ name: info.name ?? null,
240
+ expiresAt: info.expiresAt,
241
+ identifier: info.identifier
242
+ }
243
+ });
244
+ }
245
+ if (method === "GET" && segs[0] === "invite" && segs[1] === "list" && !segs[2]) {
246
+ const at = readCookie(req, cfg, "access");
247
+ const s = at ? await instance.getSession(at) : null;
248
+ if (!s) return json({ error: { code: "UNAUTHENTICATED" } }, { status: 401 });
249
+ const invites = await instance.listInvites();
250
+ return json({ invites });
251
+ }
252
+ if (method === "GET" && segs[0] === "authorize" && segs[1]) {
253
+ const { url, state, codeVerifier } = await instance.sso.authorize(segs[1]);
254
+ const headers = new Headers({ location: url });
255
+ setCookie(headers, serializeCookie2(buildCookie2(cfg, {
256
+ kind: "oauthState",
257
+ value: state,
258
+ maxAge: 600,
259
+ sameSite: "lax"
260
+ })));
261
+ setCookie(headers, serializeCookie2(buildCookie2(cfg, {
262
+ kind: "oauthPkce",
263
+ value: codeVerifier,
264
+ maxAge: 600,
265
+ sameSite: "lax"
266
+ })));
267
+ return new Response(null, { status: 302, headers });
268
+ }
269
+ if (method === "GET" && segs[0] === "callback" && segs[1]) {
270
+ const url = new URL(req.url);
271
+ const code = url.searchParams.get("code") ?? "";
272
+ const state = url.searchParams.get("state") ?? "";
273
+ const jar = parseCookies(req.headers.get("cookie"));
274
+ const storedState = jar[cookieName2(cfg, "oauthState")];
275
+ const codeVerifier = jar[cookieName2(cfg, "oauthPkce")];
276
+ if (!state || !storedState || state !== storedState || !codeVerifier) {
277
+ return json({ error: { code: "SSO_STATE_MISMATCH", message: "state/pkce invalid" } }, { status: 400 });
278
+ }
279
+ const meta = getMeta(req);
280
+ const { user, tokens } = await instance.sso.callback(segs[1], {
281
+ code,
282
+ state,
283
+ codeVerifier,
284
+ ip: meta.ip,
285
+ userAgent: meta.userAgent
286
+ });
287
+ const headers = new Headers({ location: opts.defaultRedirect ?? "/dashboard" });
288
+ writeAuthCookies(cfg, headers, tokens);
289
+ setCookie(headers, serializeCookie2(deleteCookie2(cfg, "oauthState")));
290
+ setCookie(headers, serializeCookie2(deleteCookie2(cfg, "oauthPkce")));
291
+ void user;
292
+ return new Response(null, { status: 302, headers });
293
+ }
294
+ if (method === "POST") {
295
+ if (segs[0] === "register" && !segs[1]) {
296
+ const body = await parseBody(req);
297
+ const user = await instance.register({
298
+ email: String(body.email ?? ""),
299
+ password: String(body.password ?? ""),
300
+ name: body.name ? String(body.name) : void 0
301
+ });
302
+ return json({ ok: true, user: publicUser(user) });
303
+ }
304
+ if (segs[0] === "signin" && !segs[1]) {
305
+ const body = await parseBody(req);
306
+ const meta = getMeta(req);
307
+ const result = await instance.signIn({
308
+ email: String(body.email ?? ""),
309
+ password: String(body.password ?? ""),
310
+ ip: meta.ip,
311
+ userAgent: meta.userAgent
312
+ });
313
+ return writeTokens(cfg, result);
314
+ }
315
+ if (segs[0] === "signout" && !segs[1]) {
316
+ if (!checkCsrf(req, cfg)) throw new CsrfError();
317
+ const at = readCookie(req, cfg, "access");
318
+ const rt = readCookie(req, cfg, "refresh");
319
+ await instance.signOut({ accessToken: at, refreshToken: rt });
320
+ const headers = new Headers({ "content-type": "application/json" });
321
+ clearAuthCookies(cfg, headers);
322
+ return new Response(JSON.stringify({ ok: true }), { status: 200, headers });
323
+ }
324
+ if (segs[0] === "refresh" && !segs[1]) {
325
+ const rt = readCookie(req, cfg, "refresh");
326
+ if (!rt) return json({ error: { code: "NO_REFRESH", message: "no refresh token" } }, { status: 401 });
327
+ const meta = getMeta(req);
328
+ const tokens = await instance.refresh({ refreshToken: rt, ip: meta.ip, userAgent: meta.userAgent });
329
+ const headers = new Headers({ "content-type": "application/json" });
330
+ writeAuthCookies(cfg, headers, tokens);
331
+ return new Response(JSON.stringify({ ok: true, csrfToken: tokens.csrfToken }), { status: 200, headers });
332
+ }
333
+ if (segs[0] === "password" && segs[1] === "change" && !segs[2]) {
334
+ if (!checkCsrf(req, cfg)) throw new CsrfError();
335
+ const at = readCookie(req, cfg, "access");
336
+ const s = at ? await instance.getSession(at) : null;
337
+ if (!s) return json({ error: { code: "UNAUTHENTICATED" } }, { status: 401 });
338
+ const body = await parseBody(req);
339
+ await instance.changePassword({
340
+ userId: s.userId,
341
+ currentPassword: String(body.currentPassword ?? ""),
342
+ newPassword: String(body.newPassword ?? ""),
343
+ revokeOtherSessions: body.revokeOtherSessions !== false
344
+ });
345
+ return json({ ok: true });
346
+ }
347
+ if (segs[0] === "password" && segs[1] === "reset" && segs[2] === "request" && !segs[3]) {
348
+ const body = await parseBody(req);
349
+ await instance.requestPasswordReset({ email: String(body.email ?? "") });
350
+ return json({ ok: true });
351
+ }
352
+ if (segs[0] === "password" && segs[1] === "reset" && segs[2] === "consume" && !segs[3]) {
353
+ const body = await parseBody(req);
354
+ await instance.consumePasswordReset({
355
+ email: String(body.email ?? ""),
356
+ token: String(body.token ?? ""),
357
+ newPassword: String(body.newPassword ?? "")
358
+ });
359
+ return json({ ok: true });
360
+ }
361
+ if (segs[0] === "invite" && segs[1] === "create" && !segs[2]) {
362
+ if (!checkCsrf(req, cfg)) throw new CsrfError();
363
+ const at = readCookie(req, cfg, "access");
364
+ const s = at ? await instance.getSession(at) : null;
365
+ if (!s) return json({ error: { code: "UNAUTHENTICATED" } }, { status: 401 });
366
+ const body = await parseBody(req);
367
+ const result = await instance.createInvite({
368
+ email: String(body.email ?? ""),
369
+ name: body.name != null ? String(body.name) : void 0,
370
+ groupIds: Array.isArray(body.groupIds) ? body.groupIds.map(String) : void 0,
371
+ metadata: body.metadata ?? null,
372
+ ttlSeconds: typeof body.ttlSeconds === "number" ? body.ttlSeconds : void 0,
373
+ invitedBy: s.userId
374
+ });
375
+ return json({ ok: true, invite: result });
376
+ }
377
+ if (segs[0] === "invite" && segs[1] === "consume" && !segs[2]) {
378
+ const body = await parseBody(req);
379
+ const meta = getMeta(req);
380
+ const result = await instance.consumeInvite({
381
+ token: String(body.token ?? ""),
382
+ password: String(body.password ?? ""),
383
+ name: body.name != null ? String(body.name) : void 0,
384
+ autoSignIn: body.autoSignIn !== false,
385
+ ip: meta.ip,
386
+ userAgent: meta.userAgent
387
+ });
388
+ if (result.tokens) {
389
+ const headers = new Headers({ "content-type": "application/json" });
390
+ writeAuthCookies(cfg, headers, result.tokens);
391
+ return new Response(
392
+ JSON.stringify({
393
+ ok: true,
394
+ user: publicUser(result.user),
395
+ csrfToken: result.tokens.csrfToken,
396
+ groupIds: result.groupIds ?? []
397
+ }),
398
+ { status: 200, headers }
399
+ );
400
+ }
401
+ return json({ ok: true, user: publicUser(result.user), groupIds: result.groupIds ?? [] });
402
+ }
403
+ if (segs[0] === "invite" && segs[1] === "revoke" && !segs[2]) {
404
+ if (!checkCsrf(req, cfg)) throw new CsrfError();
405
+ const at = readCookie(req, cfg, "access");
406
+ const s = at ? await instance.getSession(at) : null;
407
+ if (!s) return json({ error: { code: "UNAUTHENTICATED" } }, { status: 401 });
408
+ const body = await parseBody(req);
409
+ await instance.revokeInvite({ identifier: String(body.identifier ?? "") });
410
+ return json({ ok: true });
411
+ }
412
+ }
413
+ const match = matchPluginRoute(registry.routes, method, segs);
414
+ if (match) {
415
+ return runPluginRoute(instance, match.route, req, match.params);
416
+ }
417
+ return json({ error: { code: "NOT_FOUND", message: "route not found" } }, { status: 404 });
418
+ } catch (e) {
419
+ return errorResponse(e);
420
+ }
421
+ };
422
+ }
423
+
424
+ // src/index.ts
425
+ import { getSessionOrRefreshFromRequest as getSessionOrRefreshFromRequest2 } from "@holeauth/core/session";
426
+ function createHonoAuth(config, opts = {}) {
427
+ const base = defineHoleauth(config);
428
+ const app = createHonoAuthApp(base, opts);
429
+ return { ...base, app };
430
+ }
431
+ function createHonoAuthApp(auth, opts = {}) {
432
+ const dispatch = createDispatcher(auth, opts);
433
+ const app = new Hono();
434
+ app.all("*", async (c) => {
435
+ const inner = c.req.raw;
436
+ const webRes = await dispatch(inner);
437
+ return webRes;
438
+ });
439
+ return app;
440
+ }
441
+ function holeauthHonoMiddleware(auth) {
442
+ return async (c, next) => {
443
+ const session = await getSession(c, auth);
444
+ c.set("holeauthSession", session);
445
+ await next();
446
+ };
447
+ }
448
+ async function getSession(c, auth) {
449
+ const cookieHeader = c.req.header("cookie") ?? null;
450
+ const jar = parseCookies(cookieHeader);
451
+ const token = jar[cookieName3(auth.config, "access")];
452
+ if (!token) return null;
453
+ return auth.getSession(token);
454
+ }
455
+ function createHoleauthHonoContext(auth) {
456
+ return async function createContext(_opts, c) {
457
+ const { session, refreshed, setCookieHeaders } = await getSessionOrRefreshFromRequest(c.req.raw, auth);
458
+ for (const cookie of setCookieHeaders) {
459
+ c.header("Set-Cookie", cookie, { append: true });
460
+ }
461
+ return { c, req: c.req.raw, session, refreshed, auth };
462
+ };
463
+ }
464
+ export {
465
+ checkCsrf,
466
+ clearAuthCookies,
467
+ createDispatcher,
468
+ createHoleauthHonoContext,
469
+ createHonoAuth,
470
+ createHonoAuthApp,
471
+ getSession,
472
+ getSessionOrRefreshFromRequest2 as getSessionOrRefreshFromRequest,
473
+ holeauthHonoMiddleware,
474
+ parseCookies,
475
+ readCookie,
476
+ setCookie,
477
+ writeAuthCookies,
478
+ writePending
479
+ };
480
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/dispatch.ts","../src/cookies.ts"],"sourcesContent":["import { Hono, type Context, type MiddlewareHandler } from 'hono';\nimport {\n defineHoleauth,\n type HoleauthConfig,\n type HoleauthInstance,\n type HoleauthPlugin,\n type PluginsApi,\n type SessionData,\n} from '@holeauth/core';\nimport { getSessionOrRefreshFromRequest } from '@holeauth/core/session';\nimport { cookieName } from '@holeauth/core/cookies';\nimport { createDispatcher, type DispatchOptions } from './dispatch.js';\nimport { parseCookies } from './cookies.js';\n\nexport { createDispatcher, type DispatchOptions } from './dispatch.js';\nexport * from './cookies.js';\nexport { getSessionOrRefreshFromRequest, type RequestRefreshResult } from '@holeauth/core/session';\n\nexport type HoleauthHonoVariables = {\n holeauthSession: SessionData | null;\n};\n\nexport type HonoHoleauth<\n Plugins extends readonly HoleauthPlugin<string, unknown>[] = [],\n> = HoleauthInstance &\n PluginsApi<Plugins> & {\n /** Mount with `app.route('/api/auth', auth.app)`. */\n app: Hono;\n };\n\n/* ──────────────────────────── handler ──────────────────────────── */\n\n/**\n * Build a Hono sub-app that handles all holeauth core + plugin routes.\n *\n * ```ts\n * const auth = createHonoAuth({ ... });\n * app.route('/api/auth', auth.app);\n * ```\n */\nexport function createHonoAuth<\n const Plugins extends readonly HoleauthPlugin<string, unknown>[] = [],\n>(\n config: Omit<HoleauthConfig, 'plugins'> & { plugins?: Plugins },\n opts: DispatchOptions = {},\n): HonoHoleauth<Plugins> {\n const base = defineHoleauth(config);\n const app = createHonoAuthApp(base, opts);\n return { ...base, app } as HonoHoleauth<Plugins>;\n}\n\n/** Build a Hono sub-app from an existing `HoleauthInstance`. */\nexport function createHonoAuthApp(auth: HoleauthInstance, opts: DispatchOptions = {}): Hono {\n const dispatch = createDispatcher(auth, opts);\n const app = new Hono();\n\n // Hono passes the raw Web Request through `c.req.raw`. The dispatcher uses\n // `new URL(req.url).pathname` so we must reconstruct a request whose path\n // includes the mount prefix (Hono strips it). The dispatcher itself strips\n // its configured basePath.\n app.all('*', async (c) => {\n const inner = c.req.raw;\n // The basePath is part of the original request URL via the mount point;\n // Hono exposes the matched route path stripped, so we re-derive the full\n // path from `c.req.url` (which is unchanged) — but we still need to keep\n // the original URL. Hono's `c.req.raw.url` is the full URL — pass through.\n const webRes = await dispatch(inner);\n return webRes;\n });\n\n return app;\n}\n\n/* ───────────────────────── session helpers ─────────────────────── */\n\n/**\n * Hono middleware that resolves `c.var.holeauthSession`. Use:\n *\n * ```ts\n * app.use('*', holeauthHonoMiddleware(auth));\n * app.get('/me', (c) => c.json({ session: c.get('holeauthSession') }));\n * ```\n */\nexport function holeauthHonoMiddleware(\n auth: HoleauthInstance,\n): MiddlewareHandler<{ Variables: HoleauthHonoVariables }> {\n return async (c, next) => {\n const session = await getSession(c, auth);\n c.set('holeauthSession', session);\n await next();\n };\n}\n\n/** Read the current session from a Hono context. */\nexport async function getSession(\n c: Context,\n auth: HoleauthInstance,\n): Promise<SessionData | null> {\n const cookieHeader = c.req.header('cookie') ?? null;\n const jar = parseCookies(cookieHeader);\n const token = jar[cookieName(auth.config, 'access')];\n if (!token) return null;\n return auth.getSession(token);\n}\n\n/* ─────────────────────── tRPC context factory ──────────────────── */\n\nexport interface HoleauthHonoContext {\n c: Context;\n req: Request;\n session: SessionData | null;\n /** True when the access token was silently rotated via the refresh token. */\n refreshed: boolean;\n auth: HoleauthInstance;\n}\n\n/**\n * Build a tRPC context factory for Hono — pair with `@hono/trpc-server`. The\n * returned function accepts the same shape passed by Hono's tRPC adapter.\n */\nexport function createHoleauthHonoContext(auth: HoleauthInstance) {\n return async function createContext(_opts: unknown, c: Context): Promise<HoleauthHonoContext> {\n const { session, refreshed, setCookieHeaders } = await getSessionOrRefreshFromRequest(c.req.raw, auth);\n for (const cookie of setCookieHeaders) {\n c.header('Set-Cookie', cookie, { append: true });\n }\n return { c, req: c.req.raw, session, refreshed, auth };\n };\n}\n\nexport type { HoleauthConfig } from '@holeauth/core';\n","import type { HoleauthConfig, HoleauthInstance, SignInResult, PluginRoute, PluginRouteContext } from '@holeauth/core';\nimport { HoleauthError, CsrfError } from '@holeauth/core/errors';\nimport { getRegistry } from '@holeauth/core';\nimport {\n readCookie,\n checkCsrf,\n writeAuthCookies,\n clearAuthCookies,\n writePending,\n parseCookies,\n setCookie,\n} from './cookies.js';\nimport {\n buildCookie,\n serializeCookie,\n deleteCookie,\n cookieName,\n} from '@holeauth/core/cookies';\n\nfunction json(body: unknown, init: ResponseInit = {}): Response {\n const headers = new Headers(init.headers);\n headers.set('content-type', 'application/json');\n return new Response(JSON.stringify(body), { ...init, headers });\n}\n\nfunction errorResponse(e: unknown): Response {\n if (e instanceof HoleauthError) {\n return json({ error: { code: e.code, message: e.message } }, { status: e.status });\n }\n // Duck-type fallback: `@holeauth/core` is built as multiple bundled\n // entrypoints (splitting: false), so a `HoleauthError` thrown from one\n // bundle (e.g. core/flows via dist/index.js) is not `instanceof` the\n // class re-imported from `@holeauth/core/errors` in this package.\n if (\n e instanceof Error &&\n e.name === 'HoleauthError' &&\n typeof (e as { code?: unknown }).code === 'string' &&\n typeof (e as { status?: unknown }).status === 'number'\n ) {\n const err = e as Error & { code: string; status: number };\n return json({ error: { code: err.code, message: err.message } }, { status: err.status });\n }\n // Log unexpected errors so they don't disappear into a generic 500.\n // eslint-disable-next-line no-console\n console.error('[holeauth] Unhandled error in request dispatch:', e);\n return json({ error: { code: 'INTERNAL', message: 'Internal error' } }, { status: 500 });\n}\n\nfunction getMeta(req: Request): { ip?: string; userAgent?: string } {\n return {\n ip:\n req.headers.get('x-forwarded-for')?.split(',')[0]?.trim() ??\n req.headers.get('x-real-ip') ??\n undefined,\n userAgent: req.headers.get('user-agent') ?? undefined,\n };\n}\n\nasync function parseBody(req: Request): Promise<Record<string, unknown>> {\n try { return (await req.json()) as Record<string, unknown>; } catch { return {}; }\n}\n\nfunction pathSegments(req: Request, basePath: string): string[] {\n const url = new URL(req.url);\n let p = url.pathname;\n if (p.startsWith(basePath)) p = p.slice(basePath.length);\n return p.split('/').filter(Boolean);\n}\n\nfunction writeTokens(cfg: HoleauthConfig, result: SignInResult): Response {\n const headers = new Headers({ 'content-type': 'application/json' });\n if (result.kind === 'ok') {\n writeAuthCookies(cfg, headers, result.tokens);\n setCookie(headers, serializeCookie(deleteCookie(cfg, 'pending')));\n return new Response(\n JSON.stringify({ ok: true, user: publicUser(result.user), csrfToken: result.tokens.csrfToken }),\n { status: 200, headers },\n );\n }\n writePending(cfg, headers, result.pendingToken);\n return new Response(\n JSON.stringify({\n ok: true,\n pending: true,\n pluginId: result.pluginId,\n userId: result.userId,\n data: result.data ?? null,\n }),\n { status: 200, headers },\n );\n}\n\nfunction publicUser(u: { id: string; email: string; name?: string | null; image?: string | null }) {\n return { id: u.id, email: u.email, name: u.name ?? null, image: u.image ?? null };\n}\n\n/* ───────────────────── Plugin route matching ───────────────────── */\n\nfunction matchPluginRoute(\n routes: readonly PluginRoute[],\n method: string,\n segs: string[],\n): { route: PluginRoute; params: Record<string, string> } | null {\n for (const r of routes) {\n if (r.method !== method) continue;\n const rSegs = r.path.split('/').filter(Boolean);\n if (rSegs.length !== segs.length) continue;\n const params: Record<string, string> = {};\n let ok = true;\n for (let i = 0; i < rSegs.length; i++) {\n const a = rSegs[i]!;\n const b = segs[i]!;\n if (a.startsWith(':')) {\n params[a.slice(1)] = decodeURIComponent(b);\n } else if (a !== b) {\n ok = false;\n break;\n }\n }\n if (ok) return { route: r, params };\n }\n return null;\n}\n\nasync function runPluginRoute(\n instance: HoleauthInstance,\n route: PluginRoute,\n req: Request,\n params: Record<string, string>,\n): Promise<Response> {\n const cfg = instance.config;\n const registry = getRegistry(instance);\n\n const jar = parseCookies(req.headers.get('cookie'));\n const responseHeaders = new Headers();\n const meta = getMeta(req);\n\n if (route.requireCsrf) {\n if (!checkCsrf(req, cfg)) throw new CsrfError();\n }\n\n const session = await (async () => {\n const at = readCookie(req, cfg, 'access');\n return at ? instance.getSession(at) : null;\n })();\n\n if (route.requireAuth && !session) {\n return json({ error: { code: 'UNAUTHENTICATED', message: 'authentication required' } }, { status: 401 });\n }\n\n const body = req.method === 'POST' ? await parseBody(req) : {};\n const ctx: PluginRouteContext = {\n req,\n body: { ...body, ...params },\n responseHeaders,\n cookies: { get: (name) => jar[name] },\n setCookie(spec) {\n const secure = (cfg.tokens?.cookieSecure ?? ((globalThis as { process?: { env?: { NODE_ENV?: string } } }).process?.env?.NODE_ENV === 'production'));\n const parts = [`${spec.name}=${encodeURIComponent(spec.value)}`];\n parts.push(`Path=${spec.path ?? '/'}`);\n if (spec.maxAge !== undefined) parts.push(`Max-Age=${spec.maxAge}`);\n if (spec.httpOnly ?? true) parts.push('HttpOnly');\n if (secure) parts.push('Secure');\n const ss = spec.sameSite ?? cfg.tokens?.sameSite ?? 'lax';\n parts.push(`SameSite=${ss.charAt(0).toUpperCase()}${ss.slice(1)}`);\n setCookie(responseHeaders, parts.join('; '));\n },\n async getSession() {\n return session;\n },\n meta,\n plugin: registry.ctx,\n };\n\n const res = await route.handler(ctx);\n // Merge any headers the plugin appended into the returned response.\n if (responseHeaders.has('Set-Cookie')) {\n const merged = new Headers(res.headers);\n responseHeaders.forEach((v, k) => {\n if (k.toLowerCase() === 'set-cookie') merged.append('Set-Cookie', v);\n else merged.set(k, v);\n });\n return new Response(res.body, { status: res.status, statusText: res.statusText, headers: merged });\n }\n return res;\n}\n\n/* ─────────────────────────── Dispatcher ─────────────────────────── */\n\nexport interface DispatchOptions {\n /** Used to strip the prefix from pathnames. Default '/api/auth'. */\n basePath?: string;\n /** Default post-signin redirect destination (SSO callback). */\n defaultRedirect?: string;\n}\n\n/**\n * Build the unified GET/POST dispatcher. Mounted under `/api/auth/[...holeauth]`.\n *\n * Core routes take precedence; plugin routes are matched only if no core\n * route accepts the request.\n */\nexport function createDispatcher(\n instance: HoleauthInstance,\n opts: DispatchOptions = {},\n): (req: Request) => Promise<Response> {\n const basePath = opts.basePath ?? '/api/auth';\n const cfg = instance.config;\n const registry = getRegistry(instance);\n\n return async function dispatch(req: Request): Promise<Response> {\n const segs = pathSegments(req, basePath);\n const method = req.method.toUpperCase();\n\n try {\n // ── GET /session ─────────────────────────────────────────\n if (method === 'GET' && segs[0] === 'session' && !segs[1]) {\n const at = readCookie(req, cfg, 'access');\n const session = at ? await instance.getSession(at) : null;\n return json({ session });\n }\n\n // ── GET /csrf ────────────────────────────────────────────\n if (method === 'GET' && segs[0] === 'csrf' && !segs[1]) {\n const existing = readCookie(req, cfg, 'csrf');\n return json({ csrfToken: existing ?? null });\n }\n\n // ── GET /invite/info?token=... ───────────────────────────\n if (method === 'GET' && segs[0] === 'invite' && segs[1] === 'info' && !segs[2]) {\n const url = new URL(req.url);\n const token = url.searchParams.get('token') ?? '';\n if (!token) return json({ error: { code: 'MISSING_TOKEN', message: 'token required' } }, { status: 400 });\n const info = await instance.getInviteInfo({ token });\n // Expose only fields intended for public pre-fill.\n return json({\n invite: {\n email: info.email,\n name: info.name ?? null,\n expiresAt: info.expiresAt,\n identifier: info.identifier,\n },\n });\n }\n\n // ── GET /invite/list ─────────────────────────────────────\n if (method === 'GET' && segs[0] === 'invite' && segs[1] === 'list' && !segs[2]) {\n const at = readCookie(req, cfg, 'access');\n const s = at ? await instance.getSession(at) : null;\n if (!s) return json({ error: { code: 'UNAUTHENTICATED' } }, { status: 401 });\n const invites = await instance.listInvites();\n return json({ invites });\n }\n\n // ── GET /authorize/:provider ─────────────────────────────\n if (method === 'GET' && segs[0] === 'authorize' && segs[1]) {\n const { url, state, codeVerifier } = await instance.sso.authorize(segs[1]);\n const headers = new Headers({ location: url });\n setCookie(headers, serializeCookie(buildCookie(cfg, {\n kind: 'oauthState', value: state, maxAge: 600, sameSite: 'lax',\n })));\n setCookie(headers, serializeCookie(buildCookie(cfg, {\n kind: 'oauthPkce', value: codeVerifier, maxAge: 600, sameSite: 'lax',\n })));\n return new Response(null, { status: 302, headers });\n }\n\n // ── GET /callback/:provider?code=&state= ─────────────────\n if (method === 'GET' && segs[0] === 'callback' && segs[1]) {\n const url = new URL(req.url);\n const code = url.searchParams.get('code') ?? '';\n const state = url.searchParams.get('state') ?? '';\n const jar = parseCookies(req.headers.get('cookie'));\n const storedState = jar[cookieName(cfg, 'oauthState')];\n const codeVerifier = jar[cookieName(cfg, 'oauthPkce')];\n if (!state || !storedState || state !== storedState || !codeVerifier) {\n return json({ error: { code: 'SSO_STATE_MISMATCH', message: 'state/pkce invalid' } }, { status: 400 });\n }\n const meta = getMeta(req);\n const { user, tokens } = await instance.sso.callback(segs[1], {\n code, state, codeVerifier, ip: meta.ip, userAgent: meta.userAgent,\n });\n const headers = new Headers({ location: opts.defaultRedirect ?? '/dashboard' });\n writeAuthCookies(cfg, headers, tokens);\n setCookie(headers, serializeCookie(deleteCookie(cfg, 'oauthState')));\n setCookie(headers, serializeCookie(deleteCookie(cfg, 'oauthPkce')));\n void user;\n return new Response(null, { status: 302, headers });\n }\n\n if (method === 'POST') {\n // POST /register\n if (segs[0] === 'register' && !segs[1]) {\n const body = await parseBody(req);\n const user = await instance.register({\n email: String(body.email ?? ''),\n password: String(body.password ?? ''),\n name: body.name ? String(body.name) : undefined,\n });\n return json({ ok: true, user: publicUser(user) });\n }\n\n // POST /signin (password)\n if (segs[0] === 'signin' && !segs[1]) {\n const body = await parseBody(req);\n const meta = getMeta(req);\n const result = await instance.signIn({\n email: String(body.email ?? ''),\n password: String(body.password ?? ''),\n ip: meta.ip, userAgent: meta.userAgent,\n });\n return writeTokens(cfg, result);\n }\n\n // POST /signout\n if (segs[0] === 'signout' && !segs[1]) {\n if (!checkCsrf(req, cfg)) throw new CsrfError();\n const at = readCookie(req, cfg, 'access');\n const rt = readCookie(req, cfg, 'refresh');\n await instance.signOut({ accessToken: at, refreshToken: rt });\n const headers = new Headers({ 'content-type': 'application/json' });\n clearAuthCookies(cfg, headers);\n return new Response(JSON.stringify({ ok: true }), { status: 200, headers });\n }\n\n // POST /refresh\n if (segs[0] === 'refresh' && !segs[1]) {\n const rt = readCookie(req, cfg, 'refresh');\n if (!rt) return json({ error: { code: 'NO_REFRESH', message: 'no refresh token' } }, { status: 401 });\n const meta = getMeta(req);\n const tokens = await instance.refresh({ refreshToken: rt, ip: meta.ip, userAgent: meta.userAgent });\n const headers = new Headers({ 'content-type': 'application/json' });\n writeAuthCookies(cfg, headers, tokens);\n return new Response(JSON.stringify({ ok: true, csrfToken: tokens.csrfToken }), { status: 200, headers });\n }\n\n // POST /password/change\n if (segs[0] === 'password' && segs[1] === 'change' && !segs[2]) {\n if (!checkCsrf(req, cfg)) throw new CsrfError();\n const at = readCookie(req, cfg, 'access');\n const s = at ? await instance.getSession(at) : null;\n if (!s) return json({ error: { code: 'UNAUTHENTICATED' } }, { status: 401 });\n const body = await parseBody(req);\n await instance.changePassword({\n userId: s.userId,\n currentPassword: String(body.currentPassword ?? ''),\n newPassword: String(body.newPassword ?? ''),\n revokeOtherSessions: body.revokeOtherSessions !== false,\n });\n return json({ ok: true });\n }\n\n // POST /password/reset/request\n if (segs[0] === 'password' && segs[1] === 'reset' && segs[2] === 'request' && !segs[3]) {\n const body = await parseBody(req);\n await instance.requestPasswordReset({ email: String(body.email ?? '') });\n // Do not echo token — consumer is expected to have delivered it out-of-band.\n return json({ ok: true });\n }\n\n // POST /password/reset/consume\n if (segs[0] === 'password' && segs[1] === 'reset' && segs[2] === 'consume' && !segs[3]) {\n const body = await parseBody(req);\n await instance.consumePasswordReset({\n email: String(body.email ?? ''),\n token: String(body.token ?? ''),\n newPassword: String(body.newPassword ?? ''),\n });\n return json({ ok: true });\n }\n\n // POST /invite/create (auth + CSRF)\n if (segs[0] === 'invite' && segs[1] === 'create' && !segs[2]) {\n if (!checkCsrf(req, cfg)) throw new CsrfError();\n const at = readCookie(req, cfg, 'access');\n const s = at ? await instance.getSession(at) : null;\n if (!s) return json({ error: { code: 'UNAUTHENTICATED' } }, { status: 401 });\n const body = await parseBody(req);\n const result = await instance.createInvite({\n email: String(body.email ?? ''),\n name: body.name != null ? String(body.name) : undefined,\n groupIds: Array.isArray(body.groupIds) ? (body.groupIds as unknown[]).map(String) : undefined,\n metadata: (body.metadata ?? null) as Record<string, unknown> | null,\n ttlSeconds: typeof body.ttlSeconds === 'number' ? body.ttlSeconds : undefined,\n invitedBy: s.userId,\n });\n return json({ ok: true, invite: result });\n }\n\n // POST /invite/consume (public)\n if (segs[0] === 'invite' && segs[1] === 'consume' && !segs[2]) {\n const body = await parseBody(req);\n const meta = getMeta(req);\n const result = await instance.consumeInvite({\n token: String(body.token ?? ''),\n password: String(body.password ?? ''),\n name: body.name != null ? String(body.name) : undefined,\n autoSignIn: body.autoSignIn !== false,\n ip: meta.ip,\n userAgent: meta.userAgent,\n });\n if (result.tokens) {\n const headers = new Headers({ 'content-type': 'application/json' });\n writeAuthCookies(cfg, headers, result.tokens);\n return new Response(\n JSON.stringify({\n ok: true,\n user: publicUser(result.user),\n csrfToken: result.tokens.csrfToken,\n groupIds: result.groupIds ?? [],\n }),\n { status: 200, headers },\n );\n }\n return json({ ok: true, user: publicUser(result.user), groupIds: result.groupIds ?? [] });\n }\n\n // POST /invite/revoke (auth + CSRF)\n if (segs[0] === 'invite' && segs[1] === 'revoke' && !segs[2]) {\n if (!checkCsrf(req, cfg)) throw new CsrfError();\n const at = readCookie(req, cfg, 'access');\n const s = at ? await instance.getSession(at) : null;\n if (!s) return json({ error: { code: 'UNAUTHENTICATED' } }, { status: 401 });\n const body = await parseBody(req);\n await instance.revokeInvite({ identifier: String(body.identifier ?? '') });\n return json({ ok: true });\n }\n }\n\n // ── Plugin routes ────────────────────────────────────────\n const match = matchPluginRoute(registry.routes, method, segs);\n if (match) {\n return runPluginRoute(instance, match.route, req, match.params);\n }\n\n return json({ error: { code: 'NOT_FOUND', message: 'route not found' } }, { status: 404 });\n } catch (e) {\n return errorResponse(e);\n }\n };\n}\n","import type { HoleauthConfig, IssuedTokens } from '@holeauth/core';\nimport {\n buildCookie,\n deleteCookie,\n serializeCookie,\n cookieName,\n verifyCsrf,\n CSRF_HEADER,\n} from '@holeauth/core/cookies';\n\n/** Append one Set-Cookie header entry. Headers.append() preserves multiple values. */\nexport function setCookie(headers: Headers, cookie: string): void {\n headers.append('Set-Cookie', cookie);\n}\n\n/**\n * Apply a freshly-issued token bundle to a Response. Writes access + refresh\n * (httpOnly) plus the JS-readable CSRF cookie.\n */\nexport function writeAuthCookies(cfg: HoleauthConfig, headers: Headers, tokens: IssuedTokens): void {\n const accessTtl = cfg.tokens?.accessTtl ?? 900;\n const refreshTtl = cfg.tokens?.refreshTtl ?? 2592000;\n\n setCookie(\n headers,\n serializeCookie(buildCookie(cfg, { kind: 'access', value: tokens.accessToken, maxAge: accessTtl })),\n );\n setCookie(\n headers,\n serializeCookie(buildCookie(cfg, { kind: 'refresh', value: tokens.refreshToken, maxAge: refreshTtl })),\n );\n setCookie(\n headers,\n serializeCookie(buildCookie(cfg, { kind: 'csrf', value: tokens.csrfToken, maxAge: refreshTtl, httpOnly: false })),\n );\n}\n\nexport function clearAuthCookies(cfg: HoleauthConfig, headers: Headers): void {\n setCookie(headers, serializeCookie(deleteCookie(cfg, 'access')));\n setCookie(headers, serializeCookie(deleteCookie(cfg, 'refresh')));\n setCookie(headers, serializeCookie(deleteCookie(cfg, 'csrf')));\n setCookie(headers, serializeCookie(deleteCookie(cfg, 'pending')));\n}\n\nexport function writePending(cfg: HoleauthConfig, headers: Headers, pendingToken: string): void {\n const ttl = cfg.tokens?.pendingTtl ?? 300;\n setCookie(\n headers,\n serializeCookie(buildCookie(cfg, { kind: 'pending', value: pendingToken, maxAge: ttl })),\n );\n}\n\n/** Parse a Cookie header into a map. Next gives us a cookies API but we also\n * work off raw Request here for edge compatibility. */\nexport function parseCookies(header: string | null): Record<string, string> {\n const out: Record<string, string> = {};\n if (!header) return out;\n for (const part of header.split(';')) {\n const i = part.indexOf('=');\n if (i < 0) continue;\n const k = part.slice(0, i).trim();\n const v = decodeURIComponent(part.slice(i + 1).trim());\n if (k) out[k] = v;\n }\n return out;\n}\n\nexport function readCookie(req: Request, cfg: HoleauthConfig, kind: Parameters<typeof cookieName>[1]): string | undefined {\n const jar = parseCookies(req.headers.get('cookie'));\n return jar[cookieName(cfg, kind)];\n}\n\n/** Returns true if the double-submit CSRF check passes. */\nexport function checkCsrf(req: Request, cfg: HoleauthConfig): boolean {\n const cookie = readCookie(req, cfg, 'csrf');\n const header = req.headers.get(CSRF_HEADER);\n return verifyCsrf(cookie, header ?? undefined);\n}\n"],"mappings":"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","names":["cookieName","buildCookie","serializeCookie","deleteCookie","cookieName","getSessionOrRefreshFromRequest","cookieName"]}
package/package.json ADDED
@@ -0,0 +1,60 @@
1
+ {
2
+ "name": "@holeauth/hono",
3
+ "version": "0.0.2-alpha.0",
4
+ "description": "Hono bindings for holeauth.",
5
+ "license": "MIT",
6
+ "author": "Robert Kratz",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/robert-kratz/holeauth.git",
10
+ "directory": "packages/hono"
11
+ },
12
+ "homepage": "https://robert-kratz.github.io/holeauth",
13
+ "bugs": "https://github.com/robert-kratz/holeauth/issues",
14
+ "keywords": [
15
+ "auth",
16
+ "hono",
17
+ "holeauth"
18
+ ],
19
+ "sideEffects": false,
20
+ "type": "module",
21
+ "types": "./dist/index.d.ts",
22
+ "exports": {
23
+ ".": {
24
+ "types": "./dist/index.d.ts",
25
+ "import": "./dist/index.js"
26
+ },
27
+ "./package.json": "./package.json"
28
+ },
29
+ "files": [
30
+ "dist",
31
+ "README.md",
32
+ "LICENSE"
33
+ ],
34
+ "publishConfig": {
35
+ "access": "public",
36
+ "provenance": true
37
+ },
38
+ "dependencies": {
39
+ "@holeauth/core": "0.0.2-alpha.0"
40
+ },
41
+ "peerDependencies": {
42
+ "hono": "^4"
43
+ },
44
+ "devDependencies": {
45
+ "@types/node": "^20.16.10",
46
+ "hono": "^4.6.0",
47
+ "tsup": "^8.3.0",
48
+ "typescript": "^5.6.2",
49
+ "vitest": "^2.1.2",
50
+ "@holeauth/tsconfig": "0.0.0"
51
+ },
52
+ "scripts": {
53
+ "build": "tsup",
54
+ "dev": "tsup --watch",
55
+ "clean": "rm -rf dist .turbo",
56
+ "lint": "echo 'lint skipped'",
57
+ "typecheck": "tsc --noEmit",
58
+ "test": "vitest run --passWithNoTests"
59
+ }
60
+ }