import { SecretValue as SecretValue$1 } from "../secrets/secret-value.js"; import { ParsedScope } from "./scope.js"; import { AuthTokenStore } from "@graphorin/core/contracts"; //#region src/auth/verify.d.ts /** * Result of a successful `verifyToken(...)` call. The shape is the * minimum that callers (HTTP middleware, RPC handlers, CLI auth) * need to make an authorization decision. * * @stable */ interface VerifiedToken { readonly tokenId: string; readonly label?: string; readonly scopes: ReadonlyArray; readonly env: string; readonly expiresAt?: number; } /** * Discriminated result of a verify call. The pipeline never throws * on the unhappy path so callers can map `reason` directly to an * HTTP status code without try/catch in their hot path. * * @stable */ type VerifyResult = { readonly ok: true; readonly token: VerifiedToken; } | { readonly ok: false; readonly reason: VerifyFailureReason; readonly retryAfterMs?: number; }; /** * Reasons a verify call can fail. Each value is a stable lowercase * discriminator suitable for direct logging. * * @stable */ type VerifyFailureReason = 'malformed' | 'unknown-token' | 'revoked' | 'expired' | 'ip-locked-out' | 'token-locked-out'; /** * Options that govern the rate-limit, lockout, and cache behaviour of * the verify pipeline. * * @stable */ interface VerifierOptions { readonly tokenStore: AuthTokenStore; /** * Pepper used to derive the per-token HMAC. The pepper is supplied * as a `SecretValue` so its bytes never live in a plain string at * rest. */ readonly pepper: SecretValue$1; /** Optional accepted prefix override for `parseToken(...)`. */ readonly acceptPrefix?: string; /** Optional accepted environments override for `parseToken(...)`. */ readonly acceptEnvironments?: ReadonlyArray; /** Cache size for the warm-path lookup. Defaults to 1024. */ readonly cacheCapacity?: number; /** Hard cap on cache TTL in ms. Defaults to 60 000 (60 s). */ readonly cacheTtlMaxMs?: number; /** Per-IP failure threshold inside the sliding window. Defaults to 5. */ readonly perIpFailureThreshold?: number; /** Sliding-window length in ms for the per-IP counter. Defaults to 60 000 (60 s). */ readonly perIpWindowMs?: number; /** Lockout duration in ms after the per-IP counter trips. Defaults to 5 * 60 000 (5 min). */ readonly perIpLockoutMs?: number; /** Per-token failure threshold. Defaults to 10. */ readonly perTokenFailureThreshold?: number; /** Sliding-window length in ms for the per-token counter. Defaults to 5 * 60 000 (5 min). */ readonly perTokenWindowMs?: number; /** Concurrent-verify cap. Defaults to 100. */ readonly maxConcurrentVerify?: number; /** * Cap on distinct IPs tracked in the failure/lockout maps. * Default 10 000 - overflow sweeps expired lockouts, then evicts the * oldest entries. */ readonly maxTrackedIps?: number; /** Wall-clock provider for testing. Defaults to `Date.now`. */ readonly now?: () => number; } /** * Optional context surfaced to the verify pipeline. * * @stable */ interface VerifyContext { /** Caller IP address (or pseudonymous hash). Used by the per-IP rate limit. */ readonly ip?: string; } /** * Diagnostic snapshot for the rate limiter and concurrent-verify cap. * Used by health endpoints / `graphorin doctor` once those ship. * * @stable */ interface TokenVerifierStatus { readonly cacheSize: number; readonly inFlight: number; /** Distinct IPs currently in the failure window map (capped). */ readonly perIpFailures: number; readonly perIpLockouts: number; readonly perTokenLockouts: number; } /** * Stateful verifier. One instance is constructed per server runtime; * tests use the optional `now` to drive the sliding windows. * * @stable */ declare class TokenVerifier { #private; constructor(options: VerifierOptions); /** * Run the verify pipeline against a single raw token. Resolves with a * `{ ok: false, reason }` result for every authentication failure - * including IP/token lockout - so callers can map them straight to HTTP * responses; it does NOT reject for a failed verification. The single * exception is backpressure: when more than `maxConcurrent` verifications * are already in flight it throws {@link TokenVerifyOverloadError} so the * caller sheds load instead of queueing unboundedly. * * @stable */ verify(rawToken: string, ctx?: VerifyContext): Promise; /** * Snapshot of the verifier's current load. Useful for the * `/v1/health/secrets` endpoint and for in-process metrics. * * @stable */ status(): TokenVerifierStatus; /** Force-evict a single token from the warm cache. */ invalidate(rawTokenOrHashHex: string): void; /** Drop every cached entry. */ invalidateAll(): void; /** Lift a per-token lockout. Used by `revokeToken` / `rotateToken`. */ clearTokenLockout(tokenId: string): void; /** Lift a per-IP lockout. Used by privileged operators. */ clearIpLockout(ip: string): void; /** Throw an overload error if invoked. Test hook for the cap. */ _simulateOverloadForTesting(): never; } /** * Functional convenience wrapper around `TokenVerifier#verify`. The * stateless variant constructs a one-shot verifier per call and is * **only** suitable for tests; production code holds a long-lived * `TokenVerifier` so the warm cache earns its keep. * * @stable */ declare function verifyToken(rawToken: string, options: VerifierOptions, ctx?: VerifyContext): Promise; /** * Helper that authorises a parsed verify result against a required * scope. Keeps the scope plumbing close to the rest of the auth * surface so callers do not have to import from two places. * * @stable */ declare function authorize(result: VerifyResult, required: string | ParsedScope): { readonly ok: true; readonly token: VerifiedToken; } | { readonly ok: false; readonly reason: 'unauthenticated' | 'insufficient-scope'; }; //#endregion export { TokenVerifier, TokenVerifierStatus, VerifiedToken, VerifierOptions, VerifyContext, VerifyFailureReason, VerifyResult, authorize, verifyToken }; //# sourceMappingURL=verify.d.ts.map