← All posts
OAuthvibe-codedsecurityAI codingtoken leak

Securing OAuth in Vibe-Coded Apps: A Practical Guide

O

OverMCP Team

Quick answer

OAuth vulnerabilities in vibe-coded apps often stem from missing state parameters, insecure redirect URIs, and exposed client secrets. To secure OAuth, always generate a random state parameter per request, validate redirect URIs strictly, and never expose client secrets in client-side code. Use PKCE (Proof Key for Code Exchange) for mobile or single-page apps to prevent authorization code interception.

What to check first

Before deploying your vibe-coded app, audit your OAuth implementation with this checklist:

  • [ ] State parameter: Every OAuth authorization request includes a unique, unpredictable state value, and it is verified in the callback.
  • [ ] Redirect URI validation: The redirect URI is strictly validated against a whitelist; no wildcards or open redirects.
  • [ ] Client secret storage: Client secrets are never hardcoded or exposed in client-side code (JavaScript, mobile app). Use backend environment variables.
  • [ ] PKCE: For public clients (SPAs, mobile apps), PKCE is implemented to prevent authorization code interception.
  • [ ] Token storage: Access and refresh tokens are stored securely (e.g., httpOnly cookies, secure storage) and not in localStorage or sessionStorage.
  • [ ] Scope minimization: Only request the minimum required OAuth scopes.
  • [ ] Callback URL hardening: The callback endpoint validates the state parameter and logs mismatches to detect CSRF-like attacks.
  • Step-by-step fix

    1. Add a state parameter

    In your OAuth authorization request, generate a random string and store it in the session or a short-lived cookie. Then verify it in the callback.

    Example (Node.js with Express):

    const crypto = require('crypto');
    
    // Authorization request
    app.get('/auth/login', (req, res) => {
      const state = crypto.randomBytes(16).toString('hex');
      req.session.oauthState = state;
      const authUrl = `https://provider.com/oauth/authorize?response_type=code&client_id=CLIENT_ID&redirect_uri=${encodeURIComponent('https://yourapp.com/callback')}&state=${state}&scope=openid%20profile`;
      res.redirect(authUrl);
    });
    
    // Callback
    app.get('/callback', (req, res) => {
      const { code, state } = req.query;
      if (state !== req.session.oauthState) {
        return res.status(403).send('Invalid state parameter');
      }
      // Exchange code for tokens...
    });

    2. Validate redirect URIs

    Ensure your OAuth provider only accepts exact redirect URIs you register. In your code, verify the redirect URI before sending the request.

    3. Implement PKCE for public clients

    For SPAs or mobile apps, use PKCE instead of the implicit flow. Generate a code_verifier and code_challenge.

    Example PKCE flow:

    const crypto = require('crypto');
    
    // Generate verifier and challenge
    const codeVerifier = base64URLEncode(crypto.randomBytes(32));
    const codeChallenge = base64URLEncode(crypto.createHash('sha256').update(codeVerifier).digest());
    
    // Store verifier in session
    req.session.codeVerifier = codeVerifier;
    
    // Authorization request with code_challenge
    const authUrl = `https://provider.com/oauth/authorize?response_type=code&client_id=CLIENT_ID&redirect_uri=...&code_challenge=${codeChallenge}&code_challenge_method=S256&state=...`;
    
    // Token exchange with code_verifier
    const tokenResponse = await axios.post('https://provider.com/oauth/token', {
      grant_type: 'authorization_code',
      code,
      redirect_uri: '...',
      client_id: '...',
      code_verifier: req.session.codeVerifier,
    });

    4. Secure token storage

    Never store tokens in localStorage or sessionStorage – they are accessible to XSS. Use httpOnly, Secure, SameSite=Strict cookies for refresh tokens, and keep access tokens in memory (or short-lived).

    Common mistakes

  • Missing state parameter: Makes your app vulnerable to CSRF attacks where an attacker can hijack the OAuth callback.
  • Open redirect in redirect_uri: Attackers can use your app as an open redirect to steal authorization codes.
  • Exposing client secret in frontend: Many vibe-coded apps using Next.js or React may accidentally put the client secret in a .env file that ends up in client-side bundles. Always prefix Next.js public env vars with NEXT_PUBLIC_ only for non-secret values.
  • Using implicit flow: The implicit flow sends tokens in the URL fragment, which can be leaked via referrer headers or browser history. Use authorization code with PKCE instead.
  • Ignoring token expiration: Storing long-lived refresh tokens without rotation or validation can lead to persistent access if leaked.
  • Not validating scope: Requesting more permissions than needed increases the blast radius of a token leak.
  • How OverMCP can help

    OverMCP's free AI app security scanner can automatically detect exposed secrets, insecure OAuth configurations, and missing security headers in your vibe-coded apps. For continuous protection, continuous security monitoring alerts you when new OAuth endpoints or misconfigurations appear in your deployed app. Check your app's security headers to ensure CSP, X-Frame-Options, and other headers are set correctly to defend against OAuth-related attacks like clickjacking.

    FAQ

    What is the most common OAuth vulnerability in vibe-coded apps?

    The most common is the missing state parameter, which exposes the app to CSRF attacks that can lead to account takeover. Many AI-generated OAuth flows forget this parameter.

    Should I use PKCE for a server-side rendered app?

    If your app uses a backend to handle the OAuth flow, PKCE is not strictly necessary because the client secret can be kept confidential. However, for SPAs or mobile apps, PKCE is mandatory.

    How can I test if my OAuth implementation is secure?

    Use OverMCP's secret leak scanner to check for exposed client secrets in your codebase. Also, manually test with a missing state parameter or a modified redirect URI to see if your app rejects the request.

    Is your app secure?

    Free scan in 30 seconds. No signup needed.

    Scan My App Free