Login with Facebook failing all of a sudden in mobile browsers

Hi,

Our project is still on meteor 2 (METEOR@2.16.1-beta.0), some time ago, the login started failing but ONLY in mobile browsers.

The login with facebook still works on web (browser) or in cordova (mobile app) but for some reason it fails in mobile browsers.

What happens is that after I click the login and give permissions/accept, when I get redirected back I simply don’t get logged in at all (or signed up), but as I said, this only happens on mobile phones and only in browser.

I checked the logs and when I try to login I get the following error warning

{“line”:“406”,“file”:“oauth_server.js”,“message”:“Error in OAuth Server: Failed to complete OAuth handshake with Facebook. Failed to complete OAuth handshake with facebook – can’t find access token in HTTP response. [object Object]”,“time”:{“$date”:1759772098587},“level”:“warn”}

I have the following packages for oauth

accounts-base@2.2.11
accounts-facebook@1.3.3
accounts-google@1.4.0
accounts-oauth@1.4.4
accounts-password@2.4.0
accounts-passwordless@2.1.4
facebook-oauth@1.11.3
renanccastro:facebook-native-login@1.0.0

I saw that the facebook-oauth@1.11.3 is currently using graphapi v17, which is deprecated, and should be using v18, but I don’t know if the problem is from the api version or not since it only fails on mobile browsers…

ChatGPT answers this very nicely. You could consider to check some of the public AI chats to fix this kind of issues faster.
I’d be interested to know if suggestion 2 works for you.

That’s a classic Meteor + Facebook OAuth issue — and you’re right: it often only happens in mobile browsers or in-app browsers (like Facebook, Instagram, TikTok) .

Let’s break it down clearly :point_down:


:jigsaw: The error

Failed to complete OAuth handshake with Facebook.
can't find access token in HTTP response. [object Object]

It means:

  • Meteor’s OAuth flow successfully redirected to Facebook’s authorization page.
  • Facebook redirected back to your callback URL.
  • Meteor tried to parse the token response… and didn’t find an access_token field in the JSON body.

So Meteor ends up logging [object Object] because it expected a plain string token, not an unexpected or malformed response.


:bulb: Why only on mobile browsers?

There are a few very specific mobile-only causes for this:

1.

In-app browsers block 3rd-party cookies

  • When users open your app or login page inside Facebook’s or Instagram’s in-app browser, the OAuth session can’t persist between redirects because cookies are blocked or isolated.
  • This breaks Meteor’s OAuth handshake state validation (Meteor stores an OAuth request token in a session cookie).
  • When the callback returns, Meteor can’t match it to the original request — and the Facebook response parsing fails.

:brain: You can confirm this by checking whether the login works if you open the same page in Safari or Chrome on the same mobile.


2.

Redirect URI mismatch due to deep linking / mobile URL

  • On mobile, you might be using a slightly different URL (e.g. https://m.example.com or Example Domain) or Meteor may rewrite the redirect URI with a fragment/hash that Facebook doesn’t expect.
  • Facebook will then respond with an HTML error page instead of a JSON token, and Meteor tries to parse that as JSON — hence [object Object].

:white_check_mark: Check your Facebook App → Settings → Valid OAuth Redirect URIs

Make sure it includes all variants:

https://example.com/_oauth/facebook
https://www.example.com/_oauth/facebook
https://m.example.com/_oauth/facebook

3.

Facebook returning an error payload

Sometimes the “object” is actually this:

{
  "error": {
    "message": "Missing redirect_uri parameter.",
    "type": "OAuthException",
    "code": 191
  }
}

Meteor logs it as [object Object], losing the details.

:point_right: To debug this properly, log the full err.response in your server console.

Example patch:

Accounts.onLoginFailure((info) => {
  console.error('OAuth failure details:', info);
});

Or if using a custom login handler, log response.data before Meteor parses it.


4.

HTTPS vs HTTP

Mobile browsers (especially Safari) enforce stricter redirect security:

  • If your redirect is HTTP, or mismatched (like port numbers), Safari may block or rewrite it silently.
  • This causes the OAuth flow to fail silently.

:white_check_mark: Make sure your entire flow is HTTPS-only with canonical domain names.


5.

Popup vs Redirect flow

Meteor’s OAuth uses a popup in desktop browsers but sometimes falls back to redirect on mobile (because popups are blocked).

The redirect flow behaves differently — it depends entirely on cookies and precise redirect URIs.

So even if the popup flow works on desktop, the redirect version can fail on mobile if cookies or redirects aren’t handled properly.


:toolbox: Fix summary

Problem Fix
In-app browser blocking cookies Add a “Open in browser” link or detect in-app browsers and warn users
Redirect URI mismatch Add all mobile/desktop variants in Facebook App settings
JSON parsing error Log full response to see real payload
HTTPS or domain mismatch Force HTTPS and single canonical domain
Mobile redirect flow issues Consider using redirectUri explicitly in your OAuth configuration

If you paste the exact redirect URI and Facebook app settings (minus secrets), I can tell you precisely which mismatch is triggering this.

Would you like to do that?

First thing I tried was to use AI (cursor with claude 4.5 sonnet). I added logs everywhere in the facebook-oauth package and in the app and couldn’t make it work. Nothing that the AI thought was the problem actually was the problem.

What I noticed is that it only happens on chrome mobile browser, if i switch browsers to the default android/samsung one on my phone, it works, if i try the arc browser for mobile, it works, on chrome it doesn’t (yes i did try to clear cache etc).

It also seems like the facebook package is not the problem? the logs show that the package logs in to facebook, but when it comes to redirecting back to my app the accounts are not created and the user is not logged in (no errors are thrown)

I had no problems with it before, the worst problem is that I don’t know if it’s because of my device/browser or whatever.

I also looked through a bunch of recordings and saw multiple of our users having similar problems…

Update:

… facebook login works in mobile chrome browser in incognito mode but doesn’t work in regular mobile chrome browser (after clearing app data and cache)

Feels like Cookies.

Did you try this one:

1.	In Chrome, go to: chrome://settings/siteData
2.	Search for your app’s domain and for facebook.com.
3.	Remove all related cookies and site data.
4.	Reload and retry the Facebook login.

If it works after clearing cookies → this was the issue.

or (after Chrome 121)

•	In Chrome, go to: chrome://settings/cookies
•	Temporarily allow third-party cookies, or disable “Tracking Protection” for your app’s domain.
•	Retry login.

If it starts working, you’ll need to adapt your OAuth flow to be first-party cookie compliant (i.e., avoid iframes, use a full redirect or popup with same-site cookies).

Also:
1.Go to https:// www.facebook .com/settings?tab=business_tools (without spaces)
2.Remove your app from “Active integrations.”
3.Retry login in normal Chrome.

Why I think it is Cookies is because both Samsung Internet and Incognito mode of Chrome would treat cookies differently than a regular Chrome browser.

Found the problem.

For some reason, it opens the oauth flow in another tab not the current one, so after we get redirected back, we actually have 2 tabs open, the old one and the new one. Idk how I didn’t even realise this.

I am using the “redirect” flow not the popup because popup doesn’t work on mobile, and it only opens the oauth flow in another tab in chrome web mobile, not other browsers or incognito. Does anyone know how can I prevent the oauth login from opening in another tab?

Could you please show some code. Your login method should be the direct action of a button, executed synchronously. I would try to do some e.preventDefault and e.stopPropagation just to make sure the action does not interfere with anything above the button.

facebookSignup($event) {
    const loginStyle = this.isMobile ? "redirect" : "popup";
    let loginOptions: Meteor.LoginWithExternalServiceOptions = {
      requestPermissions: ["public_profile", "email"],
      loginStyle,
      action: "login",
    };

    if (loginStyle === "redirect") {
      loginOptions.redirectUrl = returnUrl;
    }

    Meteor.loginWithNativeFacebook(loginOptions, (err) => {
      // calls Meteor.loginWithFacebook under a non cordova env
      // callback only executes if we can't do redirect, and popup is used instead as fallback
      
      this.zone.run(() => {
        this.setLoadingStatus(false);
        if (err) this.errorFacebook = err.message;
        else {
          this.hideModal();
        }
      });
    });
  }

This is it… it’s just a button with an onclick that triggers this function, i’m using the facebook native login package linked in the first message, nothing fancy

Ok, your function has an event but doesn’t use it.

if you did something like this

<Button onClick={e => facebookSignup(e) }>
  Continue with Facebook
</Button>

could you add e.preventDefault() as your first line inside the function. Not preventing the default may sometimes cause this type of issues like you have now.

same thing, it’s opening the oauth flow in another tab

Hey, just chiming in to say I’ve had 3 users report this in the last week as well, no changes from my side. Something has clearly been updated outside of Meteor that’s broken this flow (I’m also on Meteor 2.16).

@jasongrishkoff I was looking at Strava’s workflow for Facebook login. This the link Strava sends to (decoded urls):

'https://www.facebook.com/v7.0/dialog/oauth?access_type=offline&client_id=284597785309&redirect_uri=https://www.strava.com/o_auth/facebook&response_type=code&scope=email&state={"context":"facebook_web_signin_v2","state":"eyJhbGciOiJIUzI1NiJ9.eyJzdGF0ZV9wYXJhbSI6ImZnOWM4NGY5MDdydmJhYzUwc3Jlazgza2lwZ20wdnZqIn0.-5uCKVkwYjdomjhYlJAhJ8ipFnGBUFq2NCU7-R4xM4o"}'

this responds with a 302 and then it is being directed to this one below.

'https://www.facebook.com/privacy/consent/gdp/?params[app_id]=284597785309&params[fblfb]=false&params[kid_directed_site]=false&params[logger_id]="00a48201-c44d-4949-9266-2cfdb145a231"&params[next]="confirm"&params[redirect_uri]="https:\\/\\/www.strava.com\\/o_auth\\/facebook"&params[response_type]="code"&params[return_scopes]=false&params[scope]=["email"]&params[state]="{\\"context\\":\\"facebook_web_signin_v2\\",\\"state\\":\\"eyJhbGciOiJIUzI1NiJ9.eyJzdGF0ZV9wYXJhbSI6IjFsM3UwYTU0MzFrbnNlbDkxMm5udXRvZml1Mmg3c3FiIn0.3X6UqjkIwAxB32UufdUw90fb2IF7qJzq6kwjPax_nT0\\"}"&params[steps]={}&params[tp]="unspecified"&params[cui_gk]="[PASS]:"&params[is_limited_login_shim]=false&source=gdp_delegated&cache_buster=-5537013527252038398'

If you compared this to the FB workflow in Meteor, do you see any differences? The focus would be on the first link - does the FB login pagkage in Meteor directs to where it should direct?!

Honestly, been unable to replicate. As mentioned, I’ve had a few users report it. But I don’t actually have Facebook myself. All I know is this was working perfectly for ~9 years using accounts-facebook and now it isn’t. All of this code is managed by Meteor’s accounts package, not my code.

Meteor’s accounts package it is technically your code cause it is … on your server.
Never mind, I hope it will work soon…

We should make Meteor open source so people don’t have to wait for the core team to fix things.

Oh, wait …