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
- Click “Sign in with Google”.
- Google OAuth screen completes.
- Browser returns to my app.
- Immediately redirected to
/loginagain.
What I Tried
- Ensured
onAuthStateChangedis set. - Tried adding
getRedirectResult(auth)but not sure where exactly. - Checked
isFirebaseConfiguredand env vars. - Confirmed middleware excludes
/login,/api,/_next. - Added logs:
onAuthStateChangedfires, but sometimesuseris stillnullwhen the guard redirects.
Questions
- Where exactly should I call
getRedirectResult(auth)when usingsignInWithRedirectin Next.js (App Router)? - How should I structure guards to avoid redirecting while
loading === true? - Is there anything about hosting on
*.cloudworkstations.devthat breaks Firebase cookies/session? - Would switching to
signInWithPopupavoid 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!