decentralised message store

refactor: session info generation

serenity ceef6ff0 e77b44dc

Changed files
+56 -3
src
lib
+47 -2
src/lib/sessions.ts
···
import type { WebSocket } from "ws";
+
import * as crypto from "node:crypto";
+
import { SESSIONS_SECRET } from "@/lib/utils/crypto";
+
import z from "zod";
+
+
export const sessionInfoSchema = z.object({
+
id: z.string(),
+
token: z.string(),
+
fingerprint: z.string(),
+
});
+
export type SessionInfo = z.infer<typeof sessionInfoSchema>;
export type Session = Map<string, WebSocket>;
···
return crypto.randomUUID();
};
-
export const assignSessionTo = (socket: WebSocket) => {
+
export const generateSessionInfo = (sessionId: string): SessionInfo => {
+
const token = crypto.randomBytes(32).toString("base64url");
+
+
const hmac = crypto.createHmac("sha256", SESSIONS_SECRET);
+
hmac.update(`${token}:${sessionId}`);
+
const fingerprint = hmac.digest("hex");
+
+
return { id: sessionId, token, fingerprint };
+
};
+
+
export const verifySessionToken = ({
+
token,
+
fingerprint,
+
id: sessionId,
+
}: SessionInfo) => {
+
const hmac = crypto.createHmac("sha256", SESSIONS_SECRET);
+
hmac.update(`${token}:${sessionId}`);
+
const expectedFingerprint = hmac.digest("hex");
+
try {
-
const sessionId = generateSessionId();
+
return crypto.timingSafeEqual(
+
Buffer.from(fingerprint, "hex"),
+
Buffer.from(expectedFingerprint, "hex"),
+
);
+
} catch {
+
return false;
+
}
+
};
+
+
export const assignSessionTo = ({
+
sessionInfo,
+
socket,
+
}: {
+
sessionInfo: SessionInfo;
+
socket: WebSocket;
+
}) => {
+
try {
+
const sessionId = sessionInfo.id;
sessions.set(sessionId, socket);
return { ok: true };
} catch (err: unknown) {
+2 -1
src/lib/types/http/responses.ts
···
+
import { sessionInfoSchema } from "@/lib/sessions";
import { httpResponseErrorInfoSchema } from "@/lib/types/http/errors";
import { z } from "zod";
···
>;
export const handshakeResponseSchema = z.object({
-
shardToken: z.string(),
+
sessionInfo: sessionInfoSchema,
});
export type HandshakeResponse = z.infer<typeof handshakeResponseSchema>;
+7
src/lib/utils/crypto.ts
···
+
import * as crypto from "node:crypto";
+
+
export const generateNewSecret = () => {
+
return crypto.randomBytes(32).toString("hex");
+
};
+
+
export const SESSIONS_SECRET = generateNewSecret();