All guidesVercel and env vars
On this page

Google OAuth and Supabase Auth

Supabase

Error 400: redirect_uri_mismatch

By Ohad Krispin, founder of VibeRaven · LinkedIn

"Error 400: redirect_uri_mismatch" means the redirect_uri in Google's authorization request is not on the OAuth client's list of Authorized redirect URIs. With Supabase Auth that redirect_uri is your Supabase project's callback, https://<project-ref>.supabase.co/auth/v1/callback, not your app's URL: copy it from the Google provider page in the Supabase dashboard into Authorized redirect URIs on the Google OAuth client whose ID is saved in Supabase, and put your app's origin under Authorized JavaScript origins. Google requires an exact match, scheme, case and trailing slash included. If Google then signs the user in and your app opens with error_description=Unable to exchange external code, Supabase reached Google and the token exchange failed: re-copy the client secret from the same Google client into Supabase. Checked against Google's OAuth docs and the Supabase Auth source on 2026-10-27.

Use this from your agent

  • Claude Code: 4 read-only tools; cannot see your repo.
    claude mcp add --transport http viberaven https://viberaven.dev/mcp
  • Codex: same server, in ~/.codex/config.toml.
    [mcp_servers.viberaven]
    url = "https://viberaven.dev/mcp"
  • In your repo: writes Vercel + Supabase check rules for your agent (what it writes).
    npx -y viberaven init

Which redirect URI goes in Google: your app or Supabase?

With Supabase Auth, Google never sends the user straight to your app. It returns to Supabase Auth's callback, Supabase exchanges the code for tokens, and only then redirects to your app's redirectTo or, without one, the Site URL. So Google's Authorized redirect URIs holds the Supabase callback, and your app's own return URLs go in Supabase's Redirect URLs list. Supabase's Google guide gives http://127.0.0.1:54321/auth/v1/callback for local development with the Supabase CLI.

Google Cloud Console, OAuth client (Web application)
  Authorized JavaScript origins:  https://app.example.com
  Authorized redirect URIs:       https://abcdefghijklmnopqrst.supabase.co/auth/v1/callback

Supabase dashboard, Authentication, URL Configuration
  Site URL:       https://app.example.com
  Redirect URLs:  https://app.example.com/auth/callback

redirect_uri_mismatch right after turning on a Supabase custom domain

Supabase's custom domain docs say OAuth flows advertise the custom domain as the callback once it is active. Add the custom domain callback, such as https://api.example.com/auth/v1/callback, to the Google client in addition to the supabase.co one, before you activate the domain.

Unable to exchange external code

Supabase Auth raises "Unable to exchange external code: " followed by the first four characters of Google's code when its request to Google's token endpoint fails. It is a 500 with error code unexpected_failure, so Supabase redirects the browser back to your app with error=server_error, error_code=unexpected_failure and the message in error_description, in the query string and again in the URL fragment. The four characters are part of the code, not the cause. The cause is in the Auth logs of your Supabase project, logged with the request. Check the client secret first: it must belong to the same Google OAuth client as the client ID saved in Supabase.

https://app.example.com/?error=server_error&error_code=unexpected_failure&error_description=Unable+to+exchange+external+code%3A+abcd

redirect_uri_mismatch and Vercel preview URLs

With Supabase Auth a new preview URL does not change what Google sees, because Google still returns to the Supabase callback. Previews need an entry in Supabase's Redirect URLs instead, where Supabase accepts wildcard patterns. When your app itself is the OAuth client, as with Auth.js, each domain's callback must be registered with Google one by one, because Google matches redirect URIs exactly.

Checklist

How to fix it, then check the fix

  1. 01Open the failing sign-in with the browser network panel and copy the redirect_uri parameter of the accounts.google.com request.
  2. 02Copy the callback URL from the Google provider page in the Supabase dashboard; with a custom domain active, note both callbacks.
  3. 03In Google Cloud Console open the OAuth client whose client ID is saved in Supabase, and add each callback to Authorized redirect URIs exactly as shown.
  4. 04Put your app's origins under Authorized JavaScript origins, and its return URLs in Supabase's Redirect URLs, not in Google's redirect list.
  5. 05Sign in again from a private window. If the app now opens with Unable to exchange external code, paste the client secret of that same Google client into Supabase and save.
  6. 06Sign in on production and on one preview, reload a signed-in page, and sign out.

Related

Sources

Official docs behind this checklist

FAQ

FAQ for builders before launch

Do I add my Vercel URL to Authorized redirect URIs?

Not for Supabase Auth's Google sign-in. Google returns to Supabase's callback; your Vercel URLs belong in Supabase's Redirect URLs and in Google's Authorized JavaScript origins.

Why does it work locally and fail in production?

Local sign-in with the Supabase CLI uses the 127.0.0.1:54321 callback and the hosted project uses its supabase.co callback. Each needs its own entry on the Google client, or its own client.