Fix guide · critical · oauth_client_secret_in_client
OAuth client_secret embedded in client bundle
An OAuth client_secret was found inside your deployed JavaScript. Anyone who views your bundle can now impersonate your application against the OAuth provider.
Why it matters
The client_secret is the credential your *backend* uses to prove to the OAuth provider that token-exchange requests genuinely come from your app. It is the OAuth equivalent of a server-side API key: never meant to leave your servers, never meant to be visible to a user, and absolutely never meant to be in a browser bundle.
Once it leaks:
- An attacker can complete the auth-code → access-token exchange themselves, indistinguishable from your real backend.
- They can ask the provider for refresh tokens that mint long-lived access tokens against any user who logs in to a flow they control.
- For providers that allow client-credentials grants (Microsoft, Auth0 with M2M), the secret alone gives the attacker full app-level access.
This is a direct impersonation. It is not theoretical. Bots harvesting public bundles for OAuth secrets exist; the secret is compromised the moment the bundle ships.
The reason it ends up in the bundle: usually a copy-paste error from a tutorial that says "set CLIENT_ID and CLIENT_SECRET", and the developer prefixes both with NEXT_PUBLIC_ / VITE_ / PUBLIC_ because that's what made the build pipeline pick them up. The provider expects only CLIENT_ID to be public; CLIENT_SECRET belongs server-side only.
How to fix it
- Rotate the secret immediately at the OAuth provider's developer console. Treat the leaked one as fully compromised.
- Move OAuth code-exchange to your backend. The browser hands the auth code to your server; your server exchanges it for a token using the secret.
- Use the public client + PKCE pattern instead if you have no backend (SPAs, mobile apps). PKCE replaces the client_secret with a per-flow ephemeral hash. Every modern OAuth provider supports it.
- Audit your env-var prefixes.
NEXT_PUBLIC_,VITE_,PUBLIC_,REACT_APP_all inline values into the client bundle at build time. Anything secret must NOT have those prefixes. - Add a CI gate:
vibecheck https://your-deploy.com --exit-on criticalwill fail any future deploy that includes a client_secret pattern.
Reference architecture (correct):
Browser ── auth code ──> Your backend ── code + secret ──> OAuth provider
Browser <── session cookie ── Your backend <── access token ── OAuth provider
The browser never sees the access token (it's stored server-side, behind a session cookie). The access token never sees the browser. The secret never sees the browser.
Did vibecheck flag this on your app?
If you reached this page from a vibecheck inspection report, the redacted match in your scan output is the exact string we found in your bundle. After applying the fix above, run the inspection again — the finding should clear.
Run another inspection