import { SmrtClassOptions } from '@happyvertical/smrt-core'; import { CliAuthRequestStatus, UsersCliAuthRequest } from '../models/CliAuthRequest.js'; import { User } from '../models/User.js'; import { SessionContext } from './SessionService.js'; /** Default polling interval the CLI should honour while waiting for approval. */ export declare const DEFAULT_CLI_AUTH_POLL_INTERVAL_SECONDS = 2; /** Default lifetime of a pending request (10 minutes). */ export declare const DEFAULT_CLI_AUTH_REQUEST_TTL_SECONDS: number; /** Default lifetime of the session minted on approval (30 days). */ export declare const DEFAULT_CLI_SESSION_TTL_SECONDS: number; /** Default cap on consecutive failed approve attempts per user before lockout. */ export declare const DEFAULT_CLI_AUTH_MAX_APPROVE_ATTEMPTS = 5; /** Default sliding window for counting failed approve attempts (5 minutes). */ export declare const DEFAULT_CLI_AUTH_APPROVE_ATTEMPT_WINDOW_SECONDS: number; /** * Options for {@link TerminalAuthService}. */ export interface TerminalAuthServiceOptions extends SmrtClassOptions { /** * Cookie name passed through to {@link SessionService}. Should match the * site's normal session cookie name so the same SessionService can * resolve both browser cookies and CLI bearer tokens. */ sessionCookieName?: string; /** * Prefix prepended to randomly generated user codes — purely for human * recognition ("WG-1A2B3C4D"). Defaults to no prefix. */ userCodePrefix?: string; /** TTL of a pending request, in seconds. */ requestTtlSeconds?: number; /** TTL of the session minted on approval, in seconds. */ sessionTtlSeconds?: number; /** Polling interval hint returned to clients. */ pollIntervalSeconds?: number; /** * Path on the site's origin where the approval page is mounted. Used to * build the verification URL returned to the CLI. Defaults to * `/terminal-login`. */ verificationPath?: string; /** Whether the underlying SessionService should auto-extend on access. */ sessionAutoExtend?: boolean; /** * Maximum consecutive failed approve attempts allowed per user inside the * sliding window before the user is locked out from approving any further * codes. Defaults to {@link DEFAULT_CLI_AUTH_MAX_APPROVE_ATTEMPTS}. * * User codes are only 32 bits of entropy; without this throttle, an * authenticated attacker could brute-force pending codes and hijack * another user's CLI session by approving it themselves. */ maxApproveAttempts?: number; /** * Sliding window (seconds) over which {@link maxApproveAttempts} counts. * Defaults to {@link DEFAULT_CLI_AUTH_APPROVE_ATTEMPT_WINDOW_SECONDS}. */ approveAttemptWindowSeconds?: number; } /** * What `createRequest` returns to the CLI. */ export interface CliAuthStartResult { deviceCode: string; expiresAt: string; interval: number; /** Stable issuer used by clients to bind the resulting bearer credential. */ issuer: string; userCode: string; verificationUrl: string; } /** * What `exchangeDeviceCode` returns to the polling CLI. */ export type CliAuthTokenResult = { status: 'pending'; expiresAt: string; interval: number; } | { status: 'expired'; } | { status: 'approved'; accessToken: string; expiresIn: number; tokenType: 'Bearer'; }; /** * Inputs for `approveRequest`. The caller is responsible for authenticating * the user via its existing browser session before calling this. */ export interface ApproveCliAuthRequestInput { userCode: string; user: Pick; tenantId: string | null | undefined; ipAddress?: string; userAgent?: string; } /** * High-level orchestration of the terminal device-code flow. */ export declare class TerminalAuthService { private readonly options; private readonly userCodePrefix; private readonly requestTtlSeconds; private readonly sessionTtlSeconds; private readonly pollIntervalSeconds; private readonly verificationPath; private readonly maxApproveAttempts; private readonly approveAttemptWindowMs; private readonly approveQueuesByUser; private approveLimitCollection; private requestCollection; private sessionService; constructor(options: TerminalAuthServiceOptions); initialize(): Promise; static create(options: TerminalAuthServiceOptions): Promise; private makeUserCode; /** * Generate a user code that is not present in durable request history. * Collisions are vanishingly rare (1 in 2^32 per attempt) but happen at * scale; retrying keeps the affected CLI from being locked out. */ private makeUniqueUserCode; /** * Start a new request. Returns the device code the CLI keeps secret, the * user code the human types into the browser, and the verification URL to * open. The device code is stored only as a hash. */ createRequest(origin: string): Promise; /** * Look up a request by user code. Performs lazy expiry: pending requests * past their TTL are flipped to `expired` and persisted. */ getRequestForUserCode(userCode: string): Promise; /** * Mark a pending request as approved and mint a bearer session bound to the * approving user. Idempotent: re-approving an already-approved request is a * no-op. Throws if the user/tenant are missing, the request is unknown, or * the request has expired. */ approveRequest(input: ApproveCliAuthRequestInput): Promise; /** * Serialize approval attempts per user so parallel requests cannot all pass * the failed-attempt check before any one of them records its failure. */ private withSerializedApprove; /** * Exchange a device code (the secret the CLI keeps) for an access token * once the request has been approved. Returns `{ status: 'pending' }` while * the CLI should keep polling, and `{ status: 'expired' }` if the request * was never approved within its TTL. */ exchangeDeviceCode(deviceCode: string): Promise; /** * Resolve a bearer token issued by this service into a full session * context — load the user, permissions, and tenant just like the cookie * path does. Returns `null` if the token is unknown, expired, or revoked. */ loadBearerSession(token: string): Promise; /** * Revoke a bearer token (CLI logout). Returns true if a session was * actually revoked. */ destroyBearerSession(token: string): Promise; /** * Delete expired pending or already-consumed requests (cleanup job). */ cleanupExpiredRequests(): Promise; /** The default polling interval clients should use. Exposed for tests. */ get pollInterval(): number; } /** * Error class for terminal auth failures the caller is expected to surface to * the user (vs. unexpected internal errors). */ export declare class TerminalAuthError extends Error { constructor(message: string); } /** * Thrown when a user has exceeded the configured failed-approve budget. The * caller (SvelteKit handler, REST adapter, etc.) is expected to surface this * as HTTP 429 with `Retry-After: retryAfterSeconds`. */ export declare class TerminalAuthRateLimitError extends TerminalAuthError { readonly retryAfterSeconds: number; constructor(message: string, retryAfterSeconds: number); } export type { CliAuthRequestStatus }; //# sourceMappingURL=TerminalAuthService.d.ts.map