The symptom
signInWithRedirect would fire fine — the Google popup flow completed, the redirect came back, and then... nothing. auth.currentUser was null. No error in the console. No failed network request. The user object had just vanished somewhere between the redirect and my app reading it.
The frustrating part: it worked perfectly on localhost. It only broke in production.
The wrong turns
First guess: async timing bug. I wrapped the auth-state check in every useEffect variation I could think of, added loading states, tried onAuthStateChanged instead of reading currentUser directly. No change.
Second guess: a config mismatch. I diffed my Firebase config between local and prod character by character. Identical.
Third guess: CORS. Checked network tab, no CORS errors, no failed requests at all — which in hindsight should have been the clue. A CORS problem fails loudly. This was failing silently.
What was actually happening
Firebase's redirect-based auth flow works by briefly using an iframe on your authDomain (typically your-project.firebaseapp.com) to relay the session back to your app. That relay depends on third-party storage being accessible inside that iframe — cookies/localStorage set on firebaseapp.com, read while your actual site is on a different domain.
Browsers have been steadily tightening third-party storage partitioning (Safari's been strict about this for years, Chrome's been catching up). Production was on my real custom domain. Local dev was hitting localhost, which a lot of browsers still treat more permissively. That's why it "worked" locally and silently died in prod — the iframe's storage access was just being partitioned away, and Firebase had no clean way to surface that as an error since, from its perspective, the browser just... didn't return anything.
The fix
Switch to signInWithPopup instead of signInWithRedirect. Popup-based auth doesn't rely on that same cross-domain iframe storage relay — the popup and the opener communicate directly, sidestepping the whole partitioning problem.
// Before: silently broke in production
await signInWithRedirect(auth, provider);
// After: works consistently across browsers
await signInWithPopup(auth, provider);
Trade-off: popups get blocked more aggressively on mobile and by some ad blockers, so it's not a strictly-better default — just a better fit for what was actually failing here.
The lesson
When something works locally and silently fails in production with zero error output, my new first suspect is cross-origin storage behavior, not application logic. Silent failure is often the browser deciding not to tell you it blocked something, rather than your code doing something wrong.
Anyone else hit this exact wall with Firebase (or any auth provider using a similar iframe-relay pattern)? Curious if others landed on popup, or found a way to keep redirect working reliably.