Spaces:
Runtime error
Runtime error
File size: 2,219 Bytes
cd8bd0a | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | /**
* Server-side gate for the remote login helper's "paste credentials" flow.
*
* Google's `firstparty/nativeapp` consent for embedded desktop clients only
* releases the authorization code when the loopback redirect is reachable, which
* never happens on a remote VPS install. The remote login helper runs the OAuth
* locally and emits a credential blob (see ./credentialBlob.ts); the dashboard
* POSTs that blob to /api/oauth/<provider>/paste-credentials, which decodes it
* and persists the connection via the same finalize path as `device-complete`.
*
* This module holds the pure, security-relevant gate: which providers may use
* the paste path, and the requirement that the blob's embedded provider matches
* the route provider (so a blob minted for one provider cannot be replayed
* against another).
*/
import { decodeCredentialBlob, type CredentialBlob } from "./credentialBlob";
/**
* Providers eligible for the paste-credentials flow: Google native-loopback
* clients whose consent cannot complete on a headless/remote host. `agy` is the
* Antigravity alias. Codex is intentionally excluded — it has its own browser
* device flow (`device-complete`) that works remotely.
*/
export const PASTE_CREDENTIAL_PROVIDERS = new Set(["antigravity", "agy", "gemini-cli"]);
/**
* Validate + decode a pasted credential blob for a given route provider.
* Throws a descriptive error if the provider is not allowlisted, if the blob's
* embedded provider does not match, or if the blob itself is malformed.
*/
export function parsePastedCredentials(routeProvider: string, blob: string): CredentialBlob {
if (!PASTE_CREDENTIAL_PROVIDERS.has(routeProvider)) {
throw new Error(
`paste-credentials not supported for provider: ${routeProvider}. ` +
`Supported: ${[...PASTE_CREDENTIAL_PROVIDERS].join(", ")}`
);
}
// decodeCredentialBlob validates prefix/version/JSON shape + access_token.
const decoded = decodeCredentialBlob(blob);
if (decoded.provider !== routeProvider) {
throw new Error(
`Pasted credential provider mismatch: blob is for "${decoded.provider}" ` +
`but the route provider is "${routeProvider}"`
);
}
return decoded;
}
|