@capgo/cli
Version:
A CLI to upload to capgo servers
135 lines (134 loc) • 5.34 kB
TypeScript
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 {};