import { type AnyObject, type JsonCompatibleObject, type MaybePromise, type PartialWithUndefined } from '@augment-vir/common';
import { type AnyDuration } from 'date-vir';
import { type IncomingHttpHeaders, type OutgoingHttpHeaders } from 'node:http';
import { type EmptyObject, type RequireExactlyOne, type RequireOneOrNone } from 'type-fest';
import { type UserIdResult } from '../auth.js';
import { type CookieParams } from '../cookie.js';
import { type CsrfHeaderNameOption } from '../csrf-token.js';
import { type JwtKeys, type RawJwtKeys } from '../jwt/jwt-keys.js';
import { type CreateJwtParams, type ParseJwtParams } from '../jwt/jwt.js';
/**
 * Output from `BackendAuthClient.getSecureUser()`.
 *
 * @category Internal
 */
export type GetUserResult<DatabaseUser extends AnyObject> = {
    /** The retrieved user. */
    user: DatabaseUser;
    /**
     * When `true`, indicates that the current `user` result is as assumed user. This can only be
     * `true` if you've configured user assuming in `BackendAuthClient`.
     */
    isAssumed: boolean;
    /**
     * This should be merged into your own response headers. It usually contains auth cookie
     * duration refresh headers.
     */
    responseHeaders: OutgoingHttpHeaders;
};
/**
 * Config for {@link BackendAuthClient}.
 *
 * @category Internal
 */
export type BackendAuthClientConfig<DatabaseUser extends AnyObject, UserId extends string | number, AssumedUserParams extends JsonCompatibleObject = EmptyObject> = Readonly<{
    csrf: Readonly<CsrfHeaderNameOption>;
    /** The origin of your backend that is offering auth cookies. */
    serviceOrigin: string;
    /** Finds the relevant user from your own database. */
    getUserFromDatabase: (userParams: {
        /** The user id extracted from the request cookie. */
        userId: UserId;
        /** Indicates that we're loading the user from a sign up cookie. */
        isSignUpCookie: boolean;
        /**
         * If this is set, we're attempting to load a database user for the purpose of assuming
         * their user identity. Otherwise, this is `undefined`.
         */
        assumingUser: AssumedUserParams | undefined;
        requestHeaders: Readonly<IncomingHttpHeaders>;
    }) => MaybePromise<DatabaseUser | undefined | null>;
    /**
     * Get JWT keys produced by {@link generateNewJwtKeys}. Make sure that each time this is
     * called, the same JWT keys are returned (do not call {@link generateNewJwtKeys} each time
     * this is called). Any time the JWT keys change, all current sessions will terminate.
     */
    getJwtKeys: () => MaybePromise<Readonly<RawJwtKeys>>;
    /**
     * When `isDev` is set, cookies do not require HTTPS (so they can be used with
     * http://localhost).
     */
    isDev: boolean;
} & PartialWithUndefined<{
    /** If this returns true, logging will be enabled while handling the relevant session. */
    enableLogging(params: {
        user: DatabaseUser | undefined;
        userId: UserId | undefined;
        assumedUserParams: AssumedUserParams | undefined;
    }): boolean;
    /**
     * Overwrite the header name used for tracking is an admin is assuming the identity of
     * another user.
     */
    assumedUserHeaderName: string;
    /**
     * Optionally generate a service origin from request headers. The generated origin is used
     * for set-cookie headers.
     */
    generateServiceOrigin(params: {
        requestHeaders: Readonly<IncomingHttpHeaders>;
    }): MaybePromise<undefined | string>;
    /** If provided, logs will be sent to this method. */
    log?: (message: string, extraData: AnyObject) => void;
    /**
     * Set this to allow specific users (determined by `canAssumeUser`) to assume the identity
     * of other users. This should only be used for admins so that they can troubleshoot user
     * issues.
     *
     * @see {@link AuthHeaderName}
     */
    assumeUser: {
        /**
         * Parse the assumed user header value.
         *
         * @see {@link AuthHeaderName}
         */
        parseAssumedUserHeaderValue: (
        /**
         * The assumed user header value.
         *
         * @see {@link AuthHeaderName}
         */
        data: string) => MaybePromise<{
            assumedUserParams: AssumedUserParams;
            userId: UserId;
        } | undefined>;
        /**
         * Return `true` to allow the current/original user to assume identities of other users.
         * Return `false` to block it. It is recommended to only return `true` for admin users.
         *
         * @see {@link AuthHeaderName}
         */
        canAssumeUser: (originalUser: DatabaseUser) => MaybePromise<boolean>;
    };
    /**
     * This determines how long a cookie will be valid until it needs to be refreshed.
     *
     * @default {minutes: 20}
     */
    userSessionIdleTimeout: Readonly<AnyDuration>;
    /**
     * How long into a user's session when we should start trying to refresh their session.
     *
     * @default {minutes: 2}
     */
    sessionRefreshStartTime: Readonly<AnyDuration>;
    /**
     * The maximum duration a session can last, regardless of activity. After this time, the
     * user will be logged out even if they are actively using the application.
     *
     * @default {days: 1.5}
     */
    maxSessionDuration: Readonly<AnyDuration>;
    /**
     * Allowed clock skew tolerance for JWT and CSRF token expiration checks. Accounts for
     * differences between server and client clocks.
     *
     * @default {minutes: 5}
     */
    allowedClockSkew: Readonly<AnyDuration>;
    /**
     * Optional separate origin for the CSRF cookie's `Domain` attribute. When set, the
     * non-`HttpOnly` CSRF cookie will use this origin's hostname instead of `serviceOrigin`.
     *
     * This is useful when the backend and frontend live on different subdomains that don't
     * share a common parent narrower than the top-level domain. The `HttpOnly` auth cookie
     * stays scoped to `serviceOrigin` (protecting it from unrelated subdomains), while the CSRF
     * cookie uses the broader domain so frontend JavaScript can read it via `document.cookie`.
     *
     * The CSRF token alone is not a security risk — it is only meaningful when paired with the
     * JWT embedded in the `HttpOnly` auth cookie.
     */
    csrfCookieOrigin: string;
    /**
     * Optional suffix appended to cookie names (e.g., `'staging'` produces `auth-staging`,
     * `auth-vir-csrf-staging`). When `undefined`, cookie names are unchanged. Useful for
     * running multiple environments on the same domain without cookie collisions.
     */
    cookieNameSuffix: string;
}>>;
/**
 * An auth client for creating and validating JWTs embedded in cookies. This should only be used in
 * a backend environment as it accesses native Node packages.
 *
 * @category Auth : Host
 * @category Clients
 */
export declare class BackendAuthClient<DatabaseUser extends AnyObject, UserId extends string | number, AssumedUserParams extends AnyObject = EmptyObject> {
    protected readonly config: BackendAuthClientConfig<DatabaseUser, UserId, AssumedUserParams>;
    protected cachedParsedJwtKeys: Record<string, Readonly<JwtKeys>>;
    constructor(config: BackendAuthClientConfig<DatabaseUser, UserId, AssumedUserParams>);
    /**
     * Resolves the origin to use for CSRF cookie generation. Returns `csrfCookieOrigin` if
     * configured, otherwise falls back to the auth cookie origin.
     */
    protected resolveCsrfCookieOrigin(authCookieOrigin: string): string;
    /** Conditionally logs a message if logging is enabled for the given user context. */
    protected logForUser(params: {
        user: DatabaseUser | undefined;
        userId: UserId | undefined;
        assumedUserParams: AssumedUserParams | undefined;
    }, message: string, extra?: Record<string, unknown>): void;
    /** Get all the parameters used for cookie generation. */
    protected getCookieParams({ isSignUpCookie, requestHeaders, }: {
        /**
         * Set this to `true` when we are setting the initial cookie right after a user signs up.
         * This allows them to auto-authorize when they verify their email address.
         *
         * This should only be set to `true` when a new user is signing up.
         */
        isSignUpCookie: boolean;
        requestHeaders: Readonly<IncomingHttpHeaders> | undefined;
    }): Promise<Readonly<CookieParams>>;
    /** Calls the provided `getUserFromDatabase` config. */
    protected getDatabaseUser({ isSignUpCookie, userId, assumingUser, requestHeaders, }: {
        userId: UserId | undefined;
        assumingUser: AssumedUserParams | undefined;
        isSignUpCookie: boolean;
        requestHeaders: IncomingHttpHeaders;
    }): Promise<undefined | DatabaseUser>;
    /** Creates a `'cookie-set'` header to refresh the user's session cookie. */
    protected createCookieRefreshHeaders({ userIdResult, requestHeaders, }: {
        userIdResult: Readonly<UserIdResult<UserId>>;
        requestHeaders: IncomingHttpHeaders;
    }): Promise<OutgoingHttpHeaders | undefined>;
    /** Reads the user's assumed user headers and, if configured, gets the assumed user. */
    protected getAssumedUser({ requestHeaders, user, }: {
        user: DatabaseUser;
        requestHeaders: IncomingHttpHeaders;
    }): Promise<DatabaseUser | undefined>;
    /** Securely extract a user from their request headers. */
    getSecureUser({ requestHeaders, isSignUpCookie, allowUserAuthRefresh, }: {
        requestHeaders: IncomingHttpHeaders;
        isSignUpCookie: boolean;
        /**
         * If true, this method will generate headers to refresh the user's auth session. This
         * should likely only be done with a specific endpoint, like whatever endpoint you trigger
         * with the frontend auth client's `checkUser.performCheck` callback.
         */
        allowUserAuthRefresh: boolean;
    }): Promise<GetUserResult<DatabaseUser> | undefined>;
    /**
     * Get all the JWT params used when creating the auth cookie, in case you need them for
     * something else too.
     */
    getJwtParams(): Promise<Readonly<CreateJwtParams> & ParseJwtParams>;
    /** Use these headers to log out the user. */
    createLogoutHeaders(params: Readonly<RequireExactlyOne<{
        allCookies: true;
        isSignUpCookie: boolean;
    }> & {
        /** Overrides the client's already established `serviceOrigin`. */
        serviceOrigin?: string | undefined;
    }>): Promise<Record<string, string | string[]> & {
        'set-cookie': string[];
    }>;
    /**
     * Refreshes a login session by reissuing the auth cookie with the same CSRF token instead of
     * generating a new one.
     */
    protected refreshLoginHeaders({ userId, cookieParams, existingUserIdResult, }: {
        userId: UserId;
        cookieParams: Readonly<CookieParams>;
        existingUserIdResult: Readonly<UserIdResult<UserId>>;
    }): Promise<Record<string, string | string[]>>;
    /** Generates login headers for a brand-new session (no existing JWT to reuse). */
    protected generateFreshLoginHeaders(userId: UserId, cookieParams: Readonly<CookieParams>): Promise<Record<string, string[]>>;
    /** Use these headers to log a user in. */
    createLoginHeaders({ userId, requestHeaders, isSignUpCookie, }: {
        userId: UserId;
        requestHeaders: IncomingHttpHeaders;
        isSignUpCookie: boolean;
    }): Promise<OutgoingHttpHeaders>;
    /** Combines `.getInsecureUser()` and `.getSecureUser()` into one method. */
    getInsecureOrSecureUser(params: {
        requestHeaders: IncomingHttpHeaders;
        isSignUpCookie: boolean;
        /**
         * If true, this method will generate headers to refresh the user's auth session. This
         * should likely only be done with a specific endpoint, like whatever endpoint you trigger
         * with the frontend auth client's `checkUser.performCheck` callback.
         */
        allowUserAuthRefresh: boolean;
        /** Overrides the client's already established `serviceOrigin`. */
        serviceOrigin?: string | undefined;
    }): Promise<RequireOneOrNone<{
        secureUser: GetUserResult<DatabaseUser>;
        /**
         * @deprecated This only half authenticates the user. It should only be used in
         *   circumstances where JavaScript cannot be used to attach the CSRF token header to
         *   the request (like when opening a PDF file). Use `.getSecureUser()` instead,
         *   whenever possible.
         */
        insecureUser: GetUserResult<DatabaseUser>;
    }>>;
    /**
     * @deprecated This only half authenticates the user. It should only be used in circumstances
     *   where JavaScript cannot be used to attach the CSRF token header to the request (like when
     *   opening a PDF file). Use `.getSecureUser()` instead, whenever possible.
     */
    getInsecureUser({ requestHeaders, allowUserAuthRefresh, }: {
        requestHeaders: IncomingHttpHeaders;
        /**
         * If true, this method will generate headers to refresh the user's auth session. This
         * should likely only be done with a specific endpoint, like whatever endpoint you trigger
         * with the frontend auth client's `checkUser.performCheck` callback.
         */
        allowUserAuthRefresh: boolean;
    }): Promise<GetUserResult<DatabaseUser> | undefined>;
}
