Skip to content

Public Creds

diegosouzapw edited this page May 23, 2026 · 2 revisions

🌍 View in other languages

Public Credentials Handling

Source of truth: open-sse/utils/publicCreds.ts Tests: tests/unit/publicCreds.test.ts Last updated: 2026-05-14 — v3.8.0 Audience: Engineers integrating providers that ship public OAuth client_id / client_secret / Firebase Web API keys in their public CLIs. Status: MANDATORY for all new code that embeds upstream identifiers.

Why this exists

Some upstream providers (Gemini CLI, Antigravity CLI, Windsurf / Devin CLI, GitHub Copilot, and similar OAuth-native clients) ship credentials extracted from their public binaries or web apps. Google explicitly documents that these are not secrets:

OmniRoute must embed these values so users who do not configure .env still get a working OAuth flow out of the box. Without an embedded fallback, the Gemini / Antigravity / Windsurf providers stop working for any user who follows the "just clone and run" path.

However, literal values like AIzaSy…, GOCSPX-…, …apps.googleusercontent.com are matched by GitHub Secret Scanning, Semgrep, and similar pattern scanners. Every release becomes a noisy stream of false positives, push protection blocks legitimate commits, and operators stop trusting the alert feed.

The open-sse/utils/publicCreds.ts helper solves both constraints at once:

  • Embeds the public identifier as a XOR-masked byte sequence (no scanner pattern in source).
  • Decodes at runtime via decodePublicCred / resolvePublicCred.
  • Detects raw values that already follow well-known prefixes (AIza, GOCSPX-, <digits>-<32hex>.apps.googleusercontent.com, Iv1.<hex>) and passes them through unchanged, so users with raw values in their existing .env keep working with zero migration.

This is obfuscation, not encryption. Anyone reading the source can recover the value — which is fine because the value is public by design. The only goal is to avoid scanner regex matches.

The mandatory pattern

1. Adding a new public credential

When you need to embed a new upstream-provided value that:

  • comes from a public CLI / desktop app / browser bundle, and
  • the upstream provider documents (or treats) it as a public client identifier, and
  • a pattern scanner would otherwise match it (AIza…, GOCSPX-…, <digits>-…apps.googleusercontent.com, etc.),

…follow this checklist:

  1. Generate the masked byte sequence:

    node --import tsx/esm -e \
      'import("./open-sse/utils/publicCreds.ts").then(m =>
         console.log(JSON.stringify(Array.from(
           Buffer.from(m.encodePublicCred("THE_PUBLIC_VALUE"), "base64")
         ))))'
  2. Add a new entry to EMBEDDED_DEFAULTS in open-sse/utils/publicCreds.ts with a neutral key name (<provider>_id, <provider>_alt, <provider>_fb, etc.). Do not use names like client_secret or api_key in the helper — those words trigger Semgrep generic-secret rules.

  3. Add a keyof typeof EMBEDDED_DEFAULTS to the public type union (it is inferred automatically).

  4. In the consumer code, replace the hardcoded literal with:

    // single env override
    clientSecret: resolvePublicCred("provider_alt", "PROVIDER_OAUTH_CLIENT_SECRET"),
    
    // multiple env aliases (first non-empty wins)
    clientId: resolvePublicCredMulti("provider_id", [
      "PROVIDER_CLI_OAUTH_CLIENT_ID",
      "PROVIDER_OAUTH_CLIENT_ID",
    ]),
    
    // no env override (always embedded default)
    firebaseApiKey: resolvePublicCred("provider_fb"),
  5. Remove the literal from .env.example (replace with comment-only documentation pointing readers here):

    # ── Provider (Google / Firebase / etc.) ──
    # Public OAuth credentials are baked into the code via
    # open-sse/utils/publicCreds.ts. Set these vars only to use your own.
    # PROVIDER_OAUTH_CLIENT_ID=
    # PROVIDER_OAUTH_CLIENT_SECRET=
  6. Update tests/unit/publicCreds.test.ts to add a shape assertion for the new key (verify format, not literal value — see existing tests for the pattern).

  7. Never add AIza… / GOCSPX-… / …apps.googleusercontent.com literals to test files. Use the FAKE_* constants built from .join("") fragments (see existing tests).

2. Consumers

  • Read from resolvePublicCred() / resolvePublicCredMulti() only — never call decodePublicCredBytes() directly outside the helper.
  • The helper is intentionally cheap (linear byte XOR) and safe to call at module-load time; defaults are computed once.
  • The env override always wins. If a user sets PROVIDER_OAUTH_CLIENT_SECRET=GOCSPX-myown, the helper passes that raw value straight through.

3. Forbidden patterns

Never do any of the following in production code (src/, open-sse/, electron/, bin/):

// BAD: literal value triggers Secret Scanning + Semgrep
clientSecret: process.env.PROVIDER_OAUTH_CLIENT_SECRET || "GOCSPX-realvalue",

// BAD: base64 of the literal — GitHub still detects since Feb/2025
clientSecret: process.env.PROVIDER_OAUTH_CLIENT_SECRET ||
  Buffer.from("R09DU1BYLXJlYWx2YWx1ZQ==", "base64").toString(),

// BAD: string concatenation that re-assembles the pattern at runtime
clientSecret: "GO" + "CS" + "PX-" + "realvalue",

// BAD: hex/ROT13 encoding — different obfuscation, same risk of detection
clientSecret: hexDecode("474f4353..."),

These all eventually trip a scanner. Use resolvePublicCred().

Never add literal credentials to .env.example. Users who need real upstream values can extract them from the public CLI themselves, or use their own OAuth registration.

Never dismiss a new secret-scanning alert without first checking whether the credential should be moved to this helper.

Related controls

  • RAW_VALUE_PATTERN in publicCreds.ts enumerates the prefixes that trigger passthrough (retrocompat). Extend it only for documented public credential formats, never for proprietary secrets.
  • .env.example lives in CI's check-env-doc-sync script — when you remove a var here, make sure the docs match.
  • The npm run test:vitest and node --import tsx/esm --test tests/unit/publicCreds.test.ts suites must both stay green.

When NOT to use this helper

This helper is only for credentials that are:

  1. Distributed publicly by the upstream provider (CLI binary, browser bundle, official docs).
  2. Documented or strongly implied to be non-confidential (PKCE-protected, Firebase Web key, similar).

For everything else — operator-issued tokens, per-tenant secrets, your own OAuth app's client_secret, encryption keys, JWT secrets, database passwords — use env vars only (process.env.FOO, || fallback to empty / explicit error). These belong in .env and the encrypted credentials store, not in source.

References

Clone this wiki locally