Skip to content
36 linesCodeBlameRaw
1import { env } from "cloudflare:workers";
2
3import type { User } from "@g1t/contracts";
4
5import { getViewer } from "./session.server";
6import { identity } from "./services.server";
7import { ticketViewer } from "./socket-ticket";
8import { websiteUser } from "./website-token";
9
10let isolateSecret: string | null = null;
11
12/**
13 * What tickets are sealed with: the site's `USERCONTENT_KEY`, from which
14 * lib/socket-ticket.ts derives a key of their own. Without it (a local
15 * run), a key made for this isolate, which is enough where one process
16 * serves the site.
17 */
18export function ticketSecret(): string {
19 if (env.USERCONTENT_KEY) return env.USERCONTENT_KEY;
20 if (!isolateSecret) {
21 const bytes = crypto.getRandomValues(new Uint8Array(32));
22 isolateSecret = Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
23 }
24 return isolateSecret;
25}
26
27/**
28 * Who opens a live socket: the session or token the request carries, as
29 * on any page, or else the person a socket ticket was made for
30 * (lib/socket-ticket.ts), whose token is checked again now.
31 */
32export async function socketViewer(context: Parameters<typeof getViewer>[0], request: Request): Promise<User | null> {
33 const viewer = getViewer(context);
34 if (viewer) return viewer;
35 return ticketViewer(request, ticketSecret(), async (token) => websiteUser(await identity.userForAccessToken(token)));
36}