export declare const GOOGLE_OAUTH_SCOPES_ANDROIDPUBLISHER: readonly ["openid", "https://www.googleapis.com/auth/userinfo.email", "https://www.googleapis.com/auth/androidpublisher"];
export interface GoogleOAuthConfig {
    clientId: string;
    /**
     * Desktop clients receive a "secret" from the Console that isn't truly
     * confidential; pass it when available — Google accepts the token exchange
     * with or without it as long as PKCE is used.
     */
    clientSecret?: string;
    scopes: readonly string[];
    /** Extra params to include on the auth URL (e.g. `login_hint`, `prompt`). */
    extraAuthParams?: Record<string, string>;
}
export interface GoogleOAuthTokens {
    accessToken: string;
    refreshToken?: string;
    /**
     * Unix epoch in milliseconds — the wall-clock time the access token stops
     * being accepted. Callers should refresh before this.
     */
    expiresAt: number;
    idToken?: string;
    scope: string;
    tokenType: string;
}
export interface GoogleUserInfo {
    sub: string;
    email: string;
    emailVerified: boolean;
    name?: string;
    picture?: string;
}
export interface RunOAuthFlowOptions {
    /**
     * Called once with the authorization URL right before we open it. Useful
     * for logging the URL in case `open()` fails.
     */
    onAuthUrl?: (url: string) => void;
    /** Called with user-visible status updates while we wait for the redirect. */
    onStatus?: (message: string) => void;
    /** Overall deadline for the whole flow. Defaults to 5 minutes. */
    timeoutMs?: number;
    /** Abort the flow early — useful for React cleanup. */
    signal?: AbortSignal;
}
export interface PkcePair {
    verifier: string;
    challenge: string;
    method: 'S256';
}
/**
 * Generate a PKCE verifier (43–128 chars of unreserved URL chars) and its
 * SHA-256 challenge. The verifier must be held until the token exchange.
 */
export declare function generatePkcePair(): PkcePair;
export declare function generateState(): string;
export declare function buildAuthUrl(args: {
    clientId: string;
    redirectUri: string;
    scopes: readonly string[];
    state: string;
    codeChallenge: string;
    extra?: Record<string, string>;
}): string;
interface RawTokenResponse {
    access_token: string;
    expires_in: number;
    refresh_token?: string;
    scope: string;
    token_type: string;
    id_token?: string;
}
export declare function parseTokenResponse(raw: RawTokenResponse, now?: number): GoogleOAuthTokens;
/** Exchange an authorization code + PKCE verifier for tokens. */
export declare function exchangeAuthCode(args: {
    config: GoogleOAuthConfig;
    code: string;
    codeVerifier: string;
    redirectUri: string;
}): Promise<GoogleOAuthTokens>;
/**
 * Use a stored refresh token to mint a new access token. Refresh tokens may be
 * revoked by the user at any time; callers should surface a clean re-auth
 * prompt if this throws.
 */
export declare function refreshAccessToken(config: GoogleOAuthConfig, refreshToken: string): Promise<GoogleOAuthTokens>;
/** Fetch the signed-in user's email and subject (stable Google ID). */
export declare function fetchUserInfo(accessToken: string): Promise<GoogleUserInfo>;
/**
 * Revoke a Google OAuth token. Accepts either an access or refresh token —
 * revoking a refresh token also invalidates any access tokens minted from it.
 */
export declare function revokeToken(token: string): Promise<void>;
/**
 * Error thrown by runOAuthFlow when the user approves the consent screen but
 * deselects one or more requested scopes. The CLI catches this specifically
 * to route the user back to a "please grant all permissions" re-sign-in step
 * instead of failing several phases later with confusing API errors.
 */
export declare class MissingScopesError extends Error {
    readonly missing: readonly string[];
    readonly granted: string;
    constructor(missing: readonly string[], granted: string);
}
/**
 * Compare a space-separated `scope` string from a token response against the
 * scopes the CLI requested. Returns the subset of requested scopes that the
 * user did not grant.
 *
 * Google's tokeninfo response uses a space-separated, unordered list — the
 * order in `requestedScopes` is not preserved. Empty strings are filtered out.
 */
export declare function findMissingScopes(grantedScope: string, requestedScopes: readonly string[]): string[];
export interface LoopbackCallbackResult {
    /** Authorization code Google returned in the query string. */
    code: string;
    /**
     * Finishes the browser response with the given HTML. Call this AFTER doing
     * the token exchange and scope validation so the user sees a result that
     * reflects the post-exchange state (e.g. "missing permissions") rather than
     * a generic "you can close this tab" page that's stale by the time it
     * matters. Idempotent — second call is a no-op.
     */
    finishResponse: (html: string, statusCode?: number) => void;
}
/**
 * Run the full browser-based OAuth flow and return tokens.
 *
 * Side effects:
 *  - Opens a browser window at Google's consent screen.
 *  - Starts (and later stops) a loopback HTTP server on 127.0.0.1.
 */
export declare function runOAuthFlow(config: GoogleOAuthConfig, options?: RunOAuthFlowOptions): Promise<GoogleOAuthTokens>;
export {};
