Google Sign-In · 4 min read

Google sign-in fails with disallowed_useragent in a Capacitor app.

Google will not sign anyone in inside an embedded webview. You have to leave it. Here are two setups that work with Supabase, and the small configs that break them.

What you see

Google login inside your Capacitor app is refused with disallowed_useragent. Or it spins forever. Or it seems to work, but the user ends up looking at your website in Safari while the app stays on its sign-in screen.

If you try a native setup, you may instead get tokens rejected with "Unacceptable audience".

Why it happens

Google blocks its web sign-in inside embedded webviews. It is an anti phishing policy, and there is no setting to turn it off. Your app's webview counts as embedded.

Leaving the webview brings two more problems. A plain https redirect at the end of OAuth is handed to Safari, not your app. And if your custom URL scheme is missing from the Supabase redirect allow list, Supabase falls back to your Site URL. Either way the user lands on the website.

Even when the deep link does reach the app, Supabase does not read tokens from a custom URL scheme by itself. So getSession() returns nothing.

The fix, step by step

Option A: system browser and a deep link

Step 1. Add your custom scheme to Supabase under Authentication, URL Configuration, before you build. Use a scheme equal to your bundle id.

Step 2. Register the scheme on iOS in Info.plist:

<key>CFBundleURLTypes</key>
<array>
  <dict>
    <key>CFBundleURLSchemes</key>
    <array><string>com.yourcompany.app</string></array>
  </dict>
</array>

Step 3. Register it on Android with an intent filter in AndroidManifest.xml. Without it, login opens in the browser and never comes back:

<intent-filter>
  <action android:name="android.intent.action.VIEW"/>
  <category android:name="android.intent.category.DEFAULT"/>
  <category android:name="android.intent.category.BROWSABLE"/>
  <data android:scheme="com.yourcompany.app" android:host="oauth"/>
</intent-filter>

Step 4. Open the OAuth URL in the system browser with @capacitor/browser, catch the return with @capacitor/app, and set the session yourself:

import { Browser } from '@capacitor/browser';
import { App } from '@capacitor/app';

const { data } = await sb.auth.signInWithOAuth({
  provider: 'google',
  options: {
    redirectTo: 'com.yourcompany.app://oauth',
    skipBrowserRedirect: true
  }
});
await Browser.open({ url: data.url });

App.addListener('appUrlOpen', async ({ url }) => {
  const p = new URLSearchParams(url.split('#')[1]);
  await sb.auth.setSession({
    access_token: p.get('access_token'),
    refresh_token: p.get('refresh_token')
  });
});

On Android, pass the Google web client ID, not the iOS one.

If the user still lands on your website in Safari, check Step 1 again. A scheme missing from the Supabase redirect allow list makes Supabase fall back to the Site URL, and the deep link never fires.

Option B: the native Google SDK

Step 1. Use a plugin that wraps the native SDK, such as @capgo/capacitor-social-login. It returns an ID token.

Step 2. Pass that token to Supabase with signInWithIdToken.

Step 3. Keep the reversed client id in CFBundleURLTypes.

Step 4. Fix "Unacceptable audience" by adding your iOS client id under Supabase Authentication, Providers, Google, Authorized Client IDs. Supabase has to trust the native client id.

Do any Google consent screens in a real browser, not in the webview.

Before you submit to Apple

If the iOS app offers Google sign-in, Guideline 4.8 means it must also offer Sign in with Apple. Add it in the same change, show the Apple button on iOS only, and exchange its token with signInWithIdToken.

How to prevent it

Related traps

Field Notes

This fix is one of 228 in Field Notes: What Broke and How We Fixed It.

Every entry follows the same pattern: the symptom you see, the real cause, the fix that worked, and the rule that stops it coming back. App Store, Google Play, Capacitor, Netlify, Supabase, Stripe, n8n and more.

Get Field Notes

Building a lot? The Builder Bundle has Field Notes plus the launch playbook, the prompt book and the Claude Code skills kit. See the bundle.

Related fixes

Keep reading.