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
/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 sometimesuser
is stillnull
when the guard redirects.
Questions
- Where exactly should I call
getRedirectResult(auth)
when usingsignInWithRedirect
in Next.js (App Router)? - How should I structure guards to avoid redirecting while
loading === true
? - Is there anything about hosting on
*.cloudworkstations.dev
that breaks Firebase cookies/session? - 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!