One of the most expensive bugs we ever shipped was a bug in no code we wrote. A customer would open our site from a link shared inside LINE, tap the friendly "Sign in with Google" button, and instead of a Google account picker they would hit a brick wall: a full-page Google error reading "Access blocked: This request is blocked by Google's policy" with the code 403: disallowed_useragent. No fallback fired. No retry helped. The signup simply died — silently, invisibly, for a share of mobile users we couldn't even see in our funnel. This article is the full anatomy of that failure and the fix we shipped in our own TrueLink product. It has nothing to do with your DNS, your Firebase config, or your code quality. It is about in-app browsers, and almost every site with social login has this leak open right now.
In this article
- The symptom: a 403 nobody can explain
- Root cause: Google blocks OAuth in WebViews
- Why the popup-to-redirect fallback also fails
- The real fix is UX, not auth
- Detecting the in-app browser by User-Agent
- The open-in-external-browser playbook
- What you must NOT break
- Accessibility: announce the guidance
- Why this is a critical conversion leak
The symptom: a 403 nobody can explain
Here is the exact sequence we watched happen in production. A user is scrolling inside LINE (it could just as easily be Facebook, Instagram, or Messenger). They tap a link to our site — maybe shared by a friend, maybe from one of our own posts. The page opens, looks perfectly normal, and they decide to create an account. They tap "Sign in with Google."
Instead of the Google account chooser, the entire screen is replaced by a Google-branded error page:
Access blocked: This request is blocked by Google's policy.
You can't sign in to this app because it doesn't comply with Google's policy for keeping your account secure. Error 403:disallowed_useragent
The first time this lands in a support inbox, every instinct points the wrong way. Is the OAuth client ID wrong? Did someone misconfigure the consent screen? Is a redirect URI missing? Is it a Firebase Authentication problem? We chased all of those. Every one of them was fine. The login worked perfectly in Safari, in Chrome, on desktop, on a plain mobile browser. It only failed for users who arrived by tapping a link inside a social app.
That last detail is the entire story — and it is also why the bug is so hard to reproduce. When you test your own site, you open it in a real browser, where everything works. The users hitting the wall never file a ticket; they just give up and close the tab. You lose the signup and never learn why.
Root cause: Google blocks OAuth inside embedded WebViews
When you tap a link inside LINE, Facebook, Instagram, Messenger, WeChat, or TikTok, the app does not hand you off to Safari or Chrome. It opens the page in an in-app browser — an embedded WebView rendered inside the app itself. To the user it looks like a browser. Technically it is a stripped-down web view that the host app controls and can inspect.
Since around 2021, Google has refused to process OAuth sign-in flows inside embedded WebViews, and Apple's Sign in with Apple behaves the same way. The reasoning is security: a host app that owns the WebView could, in principle, read what you type — including your Google password and one-time codes — or inject script into the login page. To protect accounts, Google detects the embedded-WebView User-Agent and refuses outright, returning the disallowed_useragent error. There is no setting on your end that turns this off. It is enforced on Google's side, for every website's Google login, inside these apps. It is working as intended.
The single most important thing to internalise is this: it is not a bug in your site. Not your DNS, not your Firebase project, not your OAuth credentials, not your front-end code. The exact same button works flawlessly the moment the page is open in a real browser. You cannot "fix the auth" because the auth was never broken.
The second thing to internalise is that your users have no idea they are in an in-app browser. Nobody taps a link thinking "I am now in LINE's embedded WebView." They tapped a link inside an app and a page appeared. From their point of view, your site's Google login is simply broken — and that impression sticks to your brand, not to LINE.
Why the popup-to-redirect fallback also fails
If you build social login with the Firebase Authentication SDK (or most OAuth libraries), the standard pattern is to try a popup first and fall back to a full-page redirect when popups are blocked:
signInWithPopup()— opens Google's auth in a popup window.signInWithRedirect()— the usual fallback, redirecting the whole page to Google and back.
This is a sensible pattern in a normal browser. Inside an in-app WebView it is a trap, because both code paths run inside the same blocked WebView. The popup either never opens or opens as another WebView surface; the redirect navigates the WebView to Google's auth URL, where Google inspects the User-Agent and returns the same disallowed_useragent 403. There is no escape hatch within the WebView. Falling back from popup to redirect just gives you a second flavour of the identical failure.
This is why "just add a redirect fallback" — the advice you'll find in half the forum threads — does nothing here. You cannot solve a WebView problem with more code that still runs inside the WebView. The flow has to leave the WebView entirely.
The real fix is UX, not auth
Once we accepted that the auth layer was healthy and the WebView was the constraint, the fix became obvious: stop attempting a doomed OAuth call, and instead guide the user out of the in-app browser into a real one. The shape of the solution is three steps:
- Detect that the page is running inside a known in-app browser, using the User-Agent string.
- Intercept the Google and Apple sign-in buttons when that detection is positive — do not let them fire the OAuth flow that is guaranteed to 403.
- Guide the user to open the page in their device's real browser (Chrome, Safari, Samsung Internet), where the same button will work perfectly.
That is the whole strategy. No backend change, no new auth provider, no Firebase reconfiguration. It is a small, defensive layer of front-end UX that turns an invisible dead end into a clear, two-tap recovery. We shipped exactly this in our own product, and the silent drop-offs stopped.
Detecting the in-app browser by User-Agent
Detection keys off the navigator.userAgent string, because each host app injects a recognisable signature. The signatures we match in production:
- LINE —
Line/ - Facebook —
FBAN,FBAV, orFB_IAB(Facebook In-App Browser) - Instagram —
Instagram - Messenger —
Messenger - WeChat —
MicroMessenger - TikTok —
BytedanceWebviewormusical_ly - KakaoTalk —
KAKAOTALK - Threads —
Barcelona(Threads' internal codename) - Generic Android System WebView — the
; wv)token in the UA
The single most important rule about detection is to be conservative. A false positive — flagging a real browser as an in-app WebView — is worse than the original bug, because you would block working logins for people whose Google sign-in was about to succeed. So the detector must never trip on Safari, Chrome, Firefox, or Samsung Internet.
iOS deserves special care. On iOS, only match the named apps above. Do not use generic "is this an iOS WebView" heuristics, because Chrome for iOS and Firefox for iOS are themselves built on WebKit web views and will misfire on those heuristics — flagging two perfectly capable real browsers as blocked. Match Line/, FBAN, Instagram, and the rest by name; if none match, assume it is a real browser and do nothing. The generic ; wv) Android-WebView token is reasonable to include on Android, but pair the whole approach with the principle: when in doubt, treat it as a real browser and leave the buttons alone.
Design heuristic we follow: a detector that occasionally misses an obscure in-app browser is acceptable; a detector that ever flags a real browser is not. Optimise for zero false positives.
The open-in-external-browser playbook
Once you've detected the WebView and intercepted the button, you show guidance instead of attempting the login. The guidance has to do real work, because asking a non-technical user to "open this in your browser" is meaningless if you don't show them how. Our playbook has three parts:
- A "Copy link" button. The most universal recovery. The user copies the URL, switches to their real browser, and pastes. One tap to copy, with a clear "Copied!" confirmation, removes most of the friction.
- An
intent://launch on Android. Android supports anintent://URL scheme that can explicitly launch Chrome with your page. Offering an "Open in Chrome" button that fires this intent gives Android users a genuine one-tap escape out of the WebView. - Per-app hints. Most in-app browsers have an "open in external browser" command tucked behind a menu. Because you've already detected which app you're in, you can give exact instructions: for example, "Tap the ⋯ menu in the top-right corner, then choose Open in external browser." A tiny app-specific hint converts far better than generic copy.
The goal of all three is the same: get the page open in Chrome, Safari, or Samsung Internet, where the very same "Sign in with Google" button works on the first tap. You are not fixing the login; you are routing the user to where the login already works.
What you must NOT break
It is tempting, once you detect a WebView, to slam the door on everything. Don't. Two paths still work inside the in-app browser, and blocking them would create a new dead end:
- Email-and-password login. This is a plain form submission to your own backend. It never touches Google's OAuth machinery, so the WebView block does not apply. Leave it fully functional. A user who can't use Google sign-in should still be able to create an account and log in without leaving the app at all.
- LINE Login. LINE's own login is a top-level redirect within the LINE ecosystem, not an embedded Google OAuth flow, so it is not subject to the WebView block. If you offer LINE login (and inside LINE's own in-app browser this is often the smoothest option for the user), keep it enabled.
So the interception is surgical: intercept only the Google and Apple buttons when a WebView is detected, and only then. Every other sign-in path stays untouched. The user is never stranded — they always have at least one working way to get in.
Accessibility: announce the guidance
Because the guidance appears dynamically — it replaces or supplements a button the user just tapped — it has to be perceivable by people using assistive technology, not just sighted users watching the screen change. Two practices we hold to:
- Announce it. Render the guidance in a container with
role="status"so screen readers announce it when it appears, and move keyboard focus into the guidance so the next interaction lands there rather than on the now-inert button. A visual change that is silent to a screen reader is, for that user, the same invisible dead end we set out to fix. - Respect motion preferences. If the guidance animates in, honour
prefers-reduced-motionand drop the animation for users who've asked for less motion. The information must arrive regardless of whether the animation plays.
Accessibility here isn't a nice-to-have bolted on at the end. The whole point of the fix is to make a hidden failure visible and recoverable — and "visible" has to mean visible to everyone.
Why this is a critical, invisible conversion leak
Step back and look at how people actually reach a website on a phone today. An enormous share of mobile traffic arrives by tapping a link inside a social app — a message in LINE, a post in Facebook or Instagram, a bio link in TikTok or Threads. Every one of those taps opens your site in an in-app browser. If social login is your primary signup path and you haven't handled the WebView case, then for that entire slice of traffic your "Sign in with Google" button is a 403 wall.
What makes it so dangerous is that it is silent. These users don't email support — they don't know there's anyone to email, and they assume the broken button is just how your site is. They don't show up as errors in most analytics. They simply don't convert, and you have no signal telling you why. It is a leak in the exact spot — the front door — where you can least afford one.
For us at TrueLink, this sits squarely under digital trust. A login that fails on the first tap doesn't just cost a signup; it tells the visitor that the product is unreliable before they've even seen it. Frictionless, trustworthy sign-in is part of the trust you're asking the customer to extend. A small, conservative layer of WebView detection and clear guidance turns the worst possible first impression into a smooth two-tap recovery — and recovers signups you currently don't even know you're losing.
Losing signups you can't see?
We hit this exact in-app browser wall in our own product and fixed it. If your mobile social login is quietly failing — or you're not sure whether it is — we can help you audit and close the leak.
Talk to us See how we help