Files
OmniRoute/src/lib/oauth/pasteCredentials.ts
Diego Rodrigues de Sa e Souza 7c23dab64d Release v3.8.40
v3.8.40 cycle integration → main. All test gates green (Unit/Integration/Coverage/Node-compat/Quality-Ratchet). The only red check, 'PR Test Policy', is the test-masking heuristic firing on the cumulative ~57-commit release diff (legitimate assert consolidations already reviewed per-PR — Gemini CLI removal #5246, retired GPT models #5280, provider catalog refreshes); overridden with --admin per the documented release-PR convention. CodeQL/SonarQube advisory scans non-blocking; #5278's code already passed CodeQL on main. Homologated on VPS 192.168.0.15 (v3.8.40 healthy).
2026-06-29 08:40:06 -03:00

52 lines
2.2 KiB
TypeScript

/**
* 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"]);
/**
* 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;
}