1.8 KB60 lines
Blame
1import { parse as parseCookie } from "cookie";
2
3const GROVE_HUB_API_URL =
4 process.env.GROVE_HUB_API_URL || "http://localhost:4001";
5
6/**
7 * Verify a token by calling the hub-api (the authority that issued it).
8 * Returns the user object on success, throws on failure.
9 */
10export async function verifyToken(token: string): Promise<any> {
11 const res = await fetch(`${GROVE_HUB_API_URL}/api/auth/me`, {
12 headers: { Authorization: `Bearer ${token}` },
13 });
14 if (!res.ok) throw new Error(`hub-api returned ${res.status}`);
15 const data = await res.json();
16 return data.user;
17}
18
19export function extractToken(req: Request): string | null {
20 // 1. Authorization header
21 const authHeader = req.headers.get("authorization");
22 if (authHeader?.startsWith("Bearer ")) {
23 return authHeader.slice(7);
24 }
25 // 2. Cookie
26 const cookieHeader = req.headers.get("cookie");
27 if (cookieHeader) {
28 const cookies = parseCookie(cookieHeader);
29 if (cookies.grove_hub_token) return cookies.grove_hub_token;
30 }
31 return null;
32}
33
34// Socket.IO middleware for /collab namespace
35export function socketAuth(
36 socket: { handshake: { auth?: { token?: string }; headers?: { cookie?: string } }; user?: any; token?: string },
37 next: (err?: Error) => void
38) {
39 // 1. Handshake auth (client passes { auth: { token } })
40 let token = socket.handshake.auth?.token;
41 // 2. Fallback to cookie
42 if (!token) {
43 const cookieHeader = socket.handshake.headers?.cookie;
44 if (cookieHeader) {
45 const cookies = parseCookie(cookieHeader);
46 token = cookies.grove_hub_token;
47 }
48 }
49 if (!token) return next(new Error("unauthorized"));
50 verifyToken(token)
51 .then((user) => {
52 socket.user = user;
53 socket.token = token!;
54 next();
55 })
56 .catch(() => {
57 next(new Error("unauthorized"));
58 });
59}
60