import { getAuthGeneration, getAuthState } from "./authState"; export type AuthOperationGuard = Readonly<{ authGeneration: number; issuer: string; subject: string; sessionId: string | null; disposalEpoch: number; }>; export type AuthOperationPrecondition = Readonly<{ operation: AuthOperationGuard; isCurrent: () => boolean; }>; export class StaleAuthOperationError extends Error { constructor() { super("The authenticated operation is no longer current"); this.name = "StaleAuthOperationError"; } } export function captureAuthOperation(options: { sessionId?: string | null; disposalEpoch?: number; } = {}): AuthOperationGuard | null { const user = getAuthState(); if (!user) return null; return { authGeneration: getAuthGeneration(), issuer: user.issuer, subject: user.subject, sessionId: options.sessionId ?? null, disposalEpoch: options.disposalEpoch ?? 0, }; } export function isAuthOperationCurrent( operation: AuthOperationGuard | null, options: { sessionId?: string | null; disposalEpoch: number }, ): boolean { if (!operation) return false; const user = getAuthState(); return Boolean( user && getAuthGeneration() === operation.authGeneration && user.issuer === operation.issuer && user.subject === operation.subject && operation.sessionId === (options.sessionId ?? null) && operation.disposalEpoch === options.disposalEpoch, ); } export function requireCurrentAuthOperation( operation: AuthOperationGuard | null, options: { sessionId?: string | null; disposalEpoch: number }, ): asserts operation is AuthOperationGuard { if (!isAuthOperationCurrent(operation, options)) throw new StaleAuthOperationError(); } export function requireAuthOperationPrecondition( precondition: AuthOperationPrecondition | undefined, ): void { if (precondition && !precondition.isCurrent()) throw new StaleAuthOperationError(); }