import type { AuthRequestPayload } from "./types.js"; export const AUTH_NONCE_LENGTH = 24; export const AUTH_MAX_TIMESTAMP_DRIFT_SECONDS = 10; export const AUTH_MAX_ATTEMPTS_PER_WINDOW = 10; export const AUTH_ATTEMPT_WINDOW_SECONDS = 10; export const AUTH_RECENT_NONCE_WINDOW_SIZE = 10; export interface AuthProofMaterial { secret: string; nonce: string; timestamp: number; } export function createAuthProofMaterial(input: AuthProofMaterial): AuthProofMaterial { return { secret: input.secret, nonce: input.nonce, timestamp: input.timestamp }; } export function serializeAuthProofMaterial(input: AuthProofMaterial): string { const material = createAuthProofMaterial(input); return JSON.stringify({ secret: material.secret, nonce: material.nonce, timestamp: material.timestamp }); } export function isValidAuthNonce(nonce: string): boolean { return /^[A-Za-z0-9_-]{24}$/.test(nonce); } export function isTimestampFresh( proofTimestamp: number, now: number, maxDriftSeconds: number = AUTH_MAX_TIMESTAMP_DRIFT_SECONDS ): { ok: true } | { ok: false; reason: "stale_timestamp" | "future_timestamp" } { const drift = proofTimestamp - now; if (Math.abs(drift) < maxDriftSeconds) { return { ok: true }; } return { ok: false, reason: drift < 0 ? "stale_timestamp" : "future_timestamp" }; } export function createAuthRequestSigningInput(input: { secret: string; nonce: string; proofTimestamp: number; }): string { return serializeAuthProofMaterial({ secret: input.secret, nonce: input.nonce, timestamp: input.proofTimestamp }); } export function extractAuthRequestSigningInput( payload: Pick, secret: string ): string { return createAuthRequestSigningInput({ secret, nonce: payload.nonce, proofTimestamp: payload.proofTimestamp }); }