import type { ProtectLogging } from "../logging.ts"; import type { Transport } from "./http.ts"; /** * The credentials for a Protect controller. `host` is the controller address without a scheme (e.g., `"192.168.1.1"`); the session derives the UniFi OS login URL * from it. * * @category Transport */ export interface ProtectCredentials { host: string; password: string; username: string; } /** * Construction options for {@link AuthSession}. The session performs its handshake through the supplied {@link Transport}, which is the only dependency it holds - * the import points downward (`AuthSession` -> `Transport`), never the reverse. * * @category Transport */ export interface AuthSessionOptions { log?: ProtectLogging; transport: Transport; } /** * Owns the authenticated session against a Protect controller: the UniFi OS credential handshake, the session cookie, the CSRF token and its rotation, and the * relogin used when a request comes back 401. * * The session composes {@link Transport} - it sends its handshake requests through the transport (so login traffic is pooled, timed, throttle-aware, and observable * like any other request) using `authRetry: false` so a handshake 401 cannot recurse back into relogin. It exposes two seams the transport wires in at the * composition root: {@link AuthSession.authHeaders} (the cookie + CSRF headers stamped onto every authenticated request) and {@link AuthSession.reauthenticate} (the * `onUnauthorized` hook). Because those seams are plain methods the transport invokes through injected function references, `Transport` never imports `AuthSession`: * the dependency flows in one direction only. * * State is invalid-by-default: {@link AuthSession.isAuthenticated} is true only once both a cookie and a CSRF token are in hand. There is no `Nullable` return on the * surface - {@link AuthSession.login} resolves on success and throws {@link ProtectAuthError} on credential failure. * * @category Transport */ export declare class AuthSession { #private; constructor(options: AuthSessionOptions); /** * Whether the session currently holds a complete set of credentials (both a session cookie and a CSRF token). Synchronous - safe on a hot path. */ get isAuthenticated(): boolean; /** * The headers to stamp on every authenticated request: the session cookie and the CSRF token, each included only when held. Wired into the transport as its * `getAuthHeaders` seam. * * @returns The auth headers, possibly empty before login completes. */ authHeaders(): Record; /** * Authenticate with the controller. Retains the credentials for later relogin, performs the handshake, and on failure throws. * * @param credentials - The controller address and account credentials. * @param opts - Optional abort signal threaded through the handshake requests. * * @throws {@link ProtectAuthError} if the handshake does not yield a cookie and CSRF token. */ login(credentials: ProtectCredentials, opts?: { signal?: AbortSignal; }): Promise; /** * End the session and forget the credentials. After logout a fresh {@link AuthSession.login} is required; the relogin hook will not re-authenticate (there are no * credentials to use), which is the correct response to an explicit logout. */ logout(): void; /** * Re-run the handshake with the retained credentials. Wired into the transport as its `onUnauthorized` seam: the transport invokes it when a request returns 401, * then retries the original request once if this resolves `true`. * * @returns `true` if re-authentication succeeded, `false` if there are no retained credentials or the handshake failed. */ reauthenticate(): Promise; } //# sourceMappingURL=auth.d.ts.map