kaneo (minimalist kanban) fork to experiment adding a tangled integration
github.com/usekaneo/kaneo
1import type { Context } from "hono";
2import { HTTPException } from "hono/http-exception";
3import { auth } from "../auth";
4import { verifyApiKey } from "./verify-api-key";
5
6async function getSessionFromBearerOnlyHeaders(c: Context) {
7 const headers = new Headers(c.req.raw.headers);
8 headers.delete("cookie");
9
10 return auth.api.getSession({ headers });
11}
12
13function parseBearerToken(authHeader: string | undefined): {
14 token: string | null;
15 malformed: boolean;
16} {
17 if (!authHeader) {
18 return { token: null, malformed: false };
19 }
20
21 if (!authHeader.match(/^Bearer\b/i)) {
22 return { token: null, malformed: false };
23 }
24
25 const match = authHeader.match(/^Bearer\s+(\S+)$/i);
26 if (!match) {
27 return { token: null, malformed: true };
28 }
29
30 return {
31 token: match[1],
32 malformed: false,
33 };
34}
35
36export async function authenticateApiRequest(c: Context): Promise<void> {
37 const { token, malformed } = parseBearerToken(c.req.header("Authorization"));
38 if (malformed) {
39 throw new HTTPException(401, { message: "Unauthorized" });
40 }
41
42 if (token) {
43 const apiKeyResult = await verifyApiKey(token);
44 if (apiKeyResult?.valid && apiKeyResult.key) {
45 const key = apiKeyResult.key;
46 c.set("userId", key.userId);
47 c.set("userEmail", "");
48 c.set("user", null);
49 c.set("session", null);
50 c.set("apiKey", {
51 id: key.id,
52 userId: key.userId,
53 enabled: key.enabled,
54 });
55 return;
56 }
57 const sessionResult = await getSessionFromBearerOnlyHeaders(c);
58 if (sessionResult?.user && sessionResult.session) {
59 c.set("user", sessionResult.user);
60 c.set("session", sessionResult.session);
61 c.set("userId", sessionResult.user.id);
62 c.set("userEmail", sessionResult.user.email ?? "");
63 return;
64 }
65 throw new HTTPException(401, { message: "Unauthorized" });
66 }
67
68 const sessionResult = await auth.api.getSession({
69 headers: c.req.raw.headers,
70 });
71 c.set("user", sessionResult?.user ?? null);
72 c.set("session", sessionResult?.session ?? null);
73 c.set("userId", sessionResult?.user?.id ?? "");
74 c.set("userEmail", sessionResult?.user?.email ?? "");
75
76 if (!sessionResult?.user) {
77 throw new HTTPException(401, { message: "Unauthorized" });
78 }
79}
80
81export async function resolveAssetBearerOrCookie(c: Context): Promise<{
82 userId: string;
83 apiKeyId?: string;
84}> {
85 const { token, malformed } = parseBearerToken(c.req.header("Authorization"));
86 if (malformed) {
87 throw new HTTPException(401, { message: "Unauthorized" });
88 }
89
90 if (token) {
91 const apiKeyResult = await verifyApiKey(token);
92 if (apiKeyResult?.valid && apiKeyResult.key) {
93 return {
94 userId: apiKeyResult.key.userId,
95 apiKeyId: apiKeyResult.key.id,
96 };
97 }
98 const sessionResult = await getSessionFromBearerOnlyHeaders(c);
99 if (sessionResult?.user?.id) {
100 return { userId: sessionResult.user.id };
101 }
102 throw new HTTPException(401, { message: "Unauthorized" });
103 }
104
105 const sessionResult = await auth.api.getSession({
106 headers: c.req.raw.headers,
107 });
108 if (!sessionResult?.user) {
109 throw new HTTPException(401, { message: "Unauthorized" });
110 }
111
112 return { userId: sessionResult.user.id };
113}