Firebase Google Auth in Next.js (App Router) keeps redirecting back to `/login` after successful sign-in

Hello Everyone,
I’m using Firebase Auth (Google provider) in a Next.js 14 App Router project. After clicking “Login with Google”, the OAuth flow completes, but I’m immediately redirected back to /login. No error is shown, just a loop.

Environment / Stack

Item Value
Next.js 14 (App Router)
Firebase JS SDK v10.x (modular)
Auth method signInWithRedirect + GoogleAuthProvider
Hosting Google Cloud Workstations (*.cloudworkstations.dev)
Auth context Custom AuthProvider with onAuthStateChanged
Middleware Possibly redirecting unauthenticated users

Relevant Code

src/context/auth-context.tsx (trimmed):

'use client';

import React, { createContext, useContext, useEffect, useState } from 'react';
import { onAuthStateChanged, getRedirectResult, GoogleAuthProvider, signInWithRedirect } from 'firebase/auth';
import { doc, getDoc, setDoc } from 'firebase/firestore';
import { auth, db, isFirebaseConfigured } from '@/lib/firebase';

const AuthContext = createContext<any>(null);

export const AuthProvider = ({ children }: { children: React.ReactNode }) => {
  const [user, setUser] = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    if (!isFirebaseConfigured) {
      setLoading(false);
      return;
    }

    // (Not sure if I should handle this result?)
    // (async () => { await getRedirectResult(auth); })();

    const unsub = onAuthStateChanged(auth, async (currentUser) => {
      if (currentUser) {
        const ref = doc(db, 'users', currentUser.uid);
        const snap = await getDoc(ref);
        if (!snap.exists()) {
          await setDoc(ref, { email: currentUser.email, createdAt: Date.now() });
        }
        setUser(currentUser);
      } else {
        setUser(null);
      }
      setLoading(false);
    });

    return () => unsub();
  }, []);

  const signInWithGoogle = async () => {
    const provider = new GoogleAuthProvider();
    await signInWithRedirect(auth, provider);
  };

  return (
    <AuthContext.Provider value={{ user, loading, signInWithGoogle }}>
      {children}
    </AuthContext.Provider>
  );
};

export const useAuth = () => useContext(AuthContext);

src/app/login/page.tsx (simplified):

"use client";
import { useAuth } from "@/context/auth-context";
import { useRouter } from "next/navigation";
import { useEffect } from "react";

export default function LoginPage() {
  const { user, loading, signInWithGoogle } = useAuth();
  const router = useRouter();

  useEffect(() => {
    if (!loading && user) router.replace("/dashboard");
  }, [loading, user, router]);

  return (
    <button onClick={signInWithGoogle}>Sign in with Google</button>
  );
}

Steps I follow

  1. Click “Sign in with Google”.
  2. Google OAuth screen completes.
  3. Browser returns to my app.
  4. Immediately redirected to /login again.

What I Tried

  • Ensured onAuthStateChanged is set.
  • Tried adding getRedirectResult(auth) but not sure where exactly.
  • Checked isFirebaseConfigured and env vars.
  • Confirmed middleware excludes /login, /api, /_next.
  • Added logs: onAuthStateChanged fires, but sometimes user is still null when the guard redirects.

Questions

  1. Where exactly should I call getRedirectResult(auth) when using signInWithRedirect in Next.js (App Router)?
  2. How should I structure guards to avoid redirecting while loading === true?
  3. Is there anything about hosting on *.cloudworkstations.dev that breaks Firebase cookies/session?
  4. Would switching to signInWithPopup avoid this issue, or is my guard the real problem?

Any pointers or working examples for this pattern (Next.js App Router + Firebase redirect auth) would be greatly appreciated!

Any pointers or working examples for this pattern (Next.js App Router + Firebase redirect auth) would be greatly appreciated!

Feel free to check out some code the App Prototyper agent generated for Google Sign-in for me here:

  1. src/app/login/page.tsx
  2. src/app/signup/page.tsx
  3. src/firebase/non-blocking-login.tsx

Thank you, @tianzi it worked like charm! I am able to use this for my sign in/signup process.