podcast manager
at prototype-deno 51 lines 1.9 kB view raw
1import { jwtVerify } from "@panva/jose"; 2import { combineSignals, timeoutSignal } from "@repo/common/aborts.ts"; 3import { jwtDecodedSchema } from "@repo/common/crypto-sig.ts"; 4import { AuthError, normalizeError } from "@repo/common/errors.ts"; 5import { takeSocket } from "@repo/common/socket.ts"; 6import { StrictMap } from "@repo/common/strict-map.ts"; 7import { preauthAuthnMessageSchema } from "@repo/schema/proto.ts"; 8import { parseIdentId, parseRealmId } from "@repo/schema/state.ts"; 9 10import { Realm, realmMap, Session } from "./state.ts"; 11 12export async function preauthHandler( 13 ws: WebSocket, 14 signal: AbortSignal, 15): Promise<Session & { realm: Realm }> { 16 const timeout = timeoutSignal(3000); 17 const combinedSignal = combineSignals(signal, timeout.signal); 18 19 // wait for preauth.authn message;it will be signed by the identity, 20 // which we already have if the realm exists, and we add to a new realm if being created 21 try { 22 const data = await takeSocket(ws, combinedSignal); 23 const jwt = jwtDecodedSchema.parse(data); 24 25 const msg = await preauthAuthnMessageSchema.parseAsync(jwt); 26 const realmid = parseRealmId(jwt.aud); 27 const identid = parseIdentId(jwt.iss); 28 29 // make sure we have a realm 30 // if a new one is being created, the current identity to it 31 const realm = realmMap.ensure(realmid, () => ({ 32 id: realmid, 33 sockets: new StrictMap(), 34 identities: new StrictMap([[identid, msg.pubkey]]), 35 })); 36 37 try { 38 // we've looked up the realm 39 // pubkey should match what we've got stored, and the signature should be valid 40 const pubkey = realm.identities.require(identid); 41 await jwtVerify(data, pubkey); 42 43 // the signature is good 44 return { realm, realmid, identid, pubkey }; 45 } catch (e) { 46 throw new AuthError("jwt verification failed", normalizeError(e)); 47 } 48 } finally { 49 timeout.cleanup(); 50 } 51}