Supabase Auth Deep Link Doctor · Flutter · free, client-side

Supabase OAuth won't come back to your Flutter app?

Paste your Flutter + supabase_flutter OAuth / magic-link deep link config and get exact mismatch fixes before wasting another afternoon.

Runs entirely in your browser. No account, no backend, no payment.

Three reasons your deep link never reaches the app.

Every supabase_flutter team hits the same three walls. Locally they line up by accident — in a release build, on a real device, they don't.
P01 · three files

Info.plist, AndroidManifest.xml, and Supabase all need the same scheme://host.

redirectTo, CFBundleURLSchemes, the AndroidManifest intent-filter, and the Supabase redirect allow-list are four separate places that must agree on the exact same string — miss one and the OS never hands the link back to your app.

scheme drift
P02 · underscore

An underscore in your scheme silently breaks Google sign-in.

A scheme like my_app://callback looks fine everywhere you check it — until Google's OAuth redirect handling mangles the underscore and Supabase's redirect check always fails (supabase/auth#2447), quietly falling back to Site URL.

Google-only bug
P03 · launch mode

The wrong LaunchMode or auth flow strands the browser.

LaunchMode.inAppWebView gets detected and blocked by Kakao, Discord, and Facebook's OAuth screens, and implicit flow puts the session in a URL fragment that deep links don't always preserve — both leave the user stuck mid-flow.

provider-specific

Fill in your config. Get the exact mismatches.

Nothing you type is sent anywhere. The check runs in your browser, using the same allow-list matching rules Supabase documents.
fn RedirectDoctorFlutter.diagnose(config)
client-side · no network
App
AndroidManifest intent-filter
Only meaningful for https Android App Links, not a custom scheme.
Supabase
OAuth provider
Your code
Result · idle
# fill in the form and press Diagnose or ⌘↵
status
idle

One function. Config in, diagnosis out.

No server, no SDK key, no signup. doctor-flutter.js is plain JavaScript — read it, fork it, or run it in your own scripts or CI.
doctor-flutter.js 0 dependencies
import { diagnose } from './doctor-flutter.js';
// or, loaded globally: const { diagnose } = window.RedirectDoctorFlutter;

const result = diagnose({
  app:      { scheme, host, launchMode, authFlowType, iosSchemesInPlist, androidIntentFilter },
  supabase: { projectUrl, siteUrl, allowedRedirectUrls },
  provider: { name, authorizedRedirectUris },
  code:     { redirectTo, usesSkipBrowserRedirect, listensToAuthStateChange },
});

// result
{
  "status":     "fail" | "warn" | "pass",
  "summary":    "…",
  "expected":   { redirectTo, iosPlistScheme, androidIntentFilter, allowListEntry, supabaseCallback },
  "problems":   [ { severity, code, message, where } ],
  "fixes":      [ { title, value, where } ],
  "checklist":  [ "…" ],
  "disclaimer": "…"
}

Everything runs client-side. The form above calls this exact function in your browser. There is no backend, no API key, and no request that carries your config anywhere.

It checks the deep link your app will actually register — computed once from scheme + host — against Info.plist, the AndroidManifest intent-filter, the Supabase redirect allow-list, and the exact callback your OAuth provider console expects.

// free, open, and demand-gated for what comes next.
// found a case it gets wrong? open a GitHub issue.
// no ads, no tracking beyond anonymous usage counts.

Free while in beta.

No account, no payment, no usage limit. If that ever changes, this version stays free for the people already using it.
Free — unlimited diagnoses, no signup, no rate limit.

This started as a tool I needed myself. If it saves you an afternoon, the best way to say thanks is to tell me what it got wrong — open a GitHub issue.

!

Read-only, client-side analysis. Nothing you enter is sent anywhere — the check runs entirely in your browser against the values you typed, not your live project. Always verify in your own environment. Not affiliated with Supabase, Google, Apple, GitHub, Discord, Kakao, or the Flutter project.

Questions developers actually search for.

Straight answers to the same supabase_flutter deep link questions this tool diagnoses — for when you just need the answer, not the checker.
Q01

Why doesn't Supabase OAuth redirect back to my Flutter app on Android?

The most common cause is a mismatch between the scheme/host your Dart code sends as redirectTo and the <data android:scheme android:host> values declared in the AndroidManifest.xml intent-filter — if they don't match exactly, Android has nothing registered to catch the link. The second most common cause is an underscore in the scheme: Google's OAuth redirect handling mangles underscores, so a scheme like my_app always fails Supabase's redirect-URL check and silently falls back to Site URL (supabase/auth#2447).

Q02

What redirectTo should I use with supabase_flutter?

Use the exact scheme://host form documented by Supabase, e.g. io.supabase.myapp://login-callback — no trailing slash, no extra path. That same string must appear unchanged in four places: the redirectTo argument, Info.plist's CFBundleURLSchemes, the AndroidManifest intent-filter, and the Supabase redirect allow-list.

Q03

Does an underscore in my deep link scheme break Google sign-in?

Yes. Supabase's own auth repository documents this as a known issue (supabase/auth#2447): a redirect URL containing an underscore, such as my_app://callback, is mangled by Google's OAuth handling and will always fail Supabase's redirect-URL match, falling back to Site URL instead of returning to the app. Use a hyphen instead — my-app://callback — or a dotted reverse-domain scheme like io.supabase.myapp.

Q04

Which LaunchMode should I use for signInWithOAuth in Flutter?

Set authScreenLaunchMode: LaunchMode.externalApplication. It opens the provider's OAuth screen in the system browser, which is what hands control back to the OS to fire your app's custom-scheme intent-filter / CFBundleURLSchemes entry. LaunchMode.inAppWebView embeds a WebView instead, and Kakao, Discord, and Facebook among others detect that and either refuse to complete sign-in or never trigger the redirect back out.

Q05

What goes into Info.plist and AndroidManifest for Supabase deep links?

In ios/Runner/Info.plist, add a CFBundleURLTypes array containing a dict whose CFBundleURLSchemes array lists your scheme. In android/app/src/main/AndroidManifest.xml, add an intent-filter to your launch activity with action VIEW, categories DEFAULT and BROWSABLE, and a <data android:scheme="…" android:host="…" /> element matching your redirectTo exactly. Leave android:autoVerify off — it only does anything for https Android App Links.

Q06

What redirect URI do I put in the Google console for supabase_flutter?

Google Cloud Console's "Authorized redirect URIs" needs Supabase's own callback, https://<your-project-ref>.supabase.co/auth/v1/callback — never your app's custom scheme. Google redirects to Supabase first at that URL, and only then does Supabase redirect on to your Flutter app's scheme://host; your app's deep link never goes in the provider console.

Stop debugging deep links at 2am.

Paste your config, get the exact mismatch, fix it, ship it — free, and it never leaves your browser.