Files

40 lines
1.2 KiB
TypeScript

import { createHash, randomBytes, timingSafeEqual } from "crypto";
export const INVITE_VALID_DAYS = 7;
export function createInviteToken(): string {
return randomBytes(32).toString("base64url");
}
export function hashInviteToken(raw: string): string {
return createHash("sha256").update(raw, "utf8").digest("hex");
}
export function tokensEqual(a: string, b: string): boolean {
const ba = Buffer.from(a, "utf8");
const bb = Buffer.from(b, "utf8");
if (ba.length !== bb.length) return false;
return timingSafeEqual(ba, bb);
}
export type InviteLike = { tokenHash: string; expiresAt: Date; acceptedAt: Date | null };
/** Prüft Einladung anhand des übergebenen Roh-Tokens und Ablaufdatum. */
export function isInviteValid(
invite: InviteLike | null,
rawToken: string,
now: Date = new Date()
): boolean {
if (!invite || !rawToken) return false;
if (invite.acceptedAt !== null) return false;
if (invite.expiresAt.getTime() <= now.getTime()) return false;
return tokensEqual(invite.tokenHash, hashInviteToken(rawToken));
}
export function normalizeEmail(email: string | null | undefined): string | null {
if (!email) return null;
const e = email.trim().toLowerCase();
if (!/^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(e)) return null;
return e;
}