From 7e69a08443f9c45bddd929d46c3d4e71fd64b0f9 Mon Sep 17 00:00:00 2001 From: nav Date: Wed, 8 Apr 2026 22:04:37 +0000 Subject: [PATCH] feat: add shared auth proof helpers --- src/auth.ts | 73 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/index.ts | 1 + 2 files changed, 74 insertions(+) create mode 100644 src/auth.ts diff --git a/src/auth.ts b/src/auth.ts new file mode 100644 index 0000000..ed113cf --- /dev/null +++ b/src/auth.ts @@ -0,0 +1,73 @@ +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 + }); +} diff --git a/src/index.ts b/src/index.ts index 418e72d..b910275 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,3 +1,4 @@ export * from "./types.js"; export * from "./codec.js"; export * from "./errors.js"; +export * from "./auth.js";