-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathsession-token.ts
More file actions
62 lines (53 loc) · 1.71 KB
/
session-token.ts
File metadata and controls
62 lines (53 loc) · 1.71 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
import { createHmac, timingSafeEqual } from "node:crypto";
interface SessionTokenPayload {
sessionId: string;
orgId: string;
exp: number;
}
function getSecret(): string {
const secret = process.env.PROXY_SESSION_SECRET?.trim();
if (!secret) {
throw new Error("PROXY_SESSION_SECRET is required");
}
return secret;
}
function toBase64Url(value: string): string {
return Buffer.from(value).toString("base64url");
}
function fromBase64Url(value: string): string {
return Buffer.from(value, "base64url").toString("utf8");
}
function sign(unsignedToken: string): string {
return createHmac("sha256", getSecret()).update(unsignedToken).digest("base64url");
}
export function createSessionToken(
sessionId: string,
orgId: string,
expiresAtMs: number,
): string {
const payload: SessionTokenPayload = {
sessionId,
orgId,
exp: expiresAtMs,
};
const encodedPayload = toBase64Url(JSON.stringify(payload));
const signature = sign(encodedPayload);
return `${encodedPayload}.${signature}`;
}
export function verifySessionToken(token: string): SessionTokenPayload | null {
const [encodedPayload, signature] = token.split(".");
if (!encodedPayload || !signature) return null;
const expectedSignature = sign(encodedPayload);
const provided = Buffer.from(signature);
const expected = Buffer.from(expectedSignature);
if (provided.length !== expected.length) return null;
if (!timingSafeEqual(provided, expected)) return null;
try {
const payload = JSON.parse(fromBase64Url(encodedPayload)) as SessionTokenPayload;
if (!payload.sessionId || !payload.orgId || !payload.exp) return null;
if (Date.now() > payload.exp) return null;
return payload;
} catch {
return null;
}
}