Google / Social Sign-in
Google OAuth on Firebase Auth gives one-tap sign-in with a Google account. Two modes: popup (web), redirect (web + mobile). The provider returns an ID token; everything else flows the same as email/password.
Web + mobile + scopes + linking
EXAMPLE
// 1) Web — popup
import { initializeApp } from 'firebase/app';
import {
getAuth,
GoogleAuthProvider,
signInWithPopup,
signInWithRedirect,
getRedirectResult,
signOut,
onAuthStateChanged,
} from 'firebase/auth';
const app = initializeApp(firebaseConfig);
const auth = getAuth(app);
const provider = new GoogleAuthProvider();
provider.addScope('email');
provider.addScope('profile');
provider.setCustomParameters({ prompt: 'select_account' });
async function googleLoginPopup() {
try {
const result = await signInWithPopup(auth, provider);
const credential = GoogleAuthProvider.credentialFromResult(result);
return { user: result.user, accessToken: credential.accessToken };
} catch (e) {
if (e.code === 'auth/popup-closed-by-user') return null;
if (e.code === 'auth/account-exists-with-different-credential') {
// Same email already linked to another provider
// Surface a 'sign in with X first, then link Google' UI
return { needLinking: e.customData.email };
}
throw e;
}
}
// 2) Web — redirect (better UX on mobile browsers)
async function googleLoginRedirect() {
await signInWithRedirect(auth, provider); // navigates away
}
// On return to your /auth/callback page
async function handleRedirect() {
const result = await getRedirectResult(auth);
if (result) {
const credential = GoogleAuthProvider.credentialFromResult(result);
return { user: result.user, accessToken: credential.accessToken };
}
return null;
}
// 3) Read what came back
onAuthStateChanged(auth, (user) => {
if (user) {
console.log({
uid: user.uid,
displayName:user.displayName,
email: user.email,
verified: user.emailVerified, // always true from Google
photoURL: user.photoURL,
providerId: user.providerData[0]?.providerId, // 'google.com'
});
}
});
// 4) Get an ID token for your backend
const idToken = await auth.currentUser.getIdToken();
await fetch('/api/me', {
method: 'POST',
headers: { 'content-type': 'application/json', Authorization: `Bearer ${idToken}` },
});
// 5) Backend — verify the token (Admin SDK, Node)
import { getAuth as adminAuth } from 'firebase-admin/auth';
app.post('/api/me', async (req, res) => {
const token = req.headers.authorization?.replace('Bearer ', '');
try {
const decoded = await adminAuth().verifyIdToken(token);
req.uid = decoded.uid;
// decoded.firebase.identities['google.com'] = ['<google sub>']
} catch {
return res.status(401).end();
}
res.json({ uid: req.uid });
});
// 6) Sign out
await signOut(auth);
// 7) Link Google to an existing email account
import { linkWithPopup, linkWithRedirect } from 'firebase/auth';
await linkWithPopup(auth.currentUser, provider);
// Now the user can sign in with either email/password OR Google.
// 8) Unlink
import { unlink } from 'firebase/auth';
await unlink(auth.currentUser, 'google.com');
// 9) Account-exists collision — common pattern
// User signed up with 'a@example.com' via email/password.
// Later tries Google with the same email — Firebase blocks unless emailLinkSignIn is on.
// Flow:
// 1. Detect 'auth/account-exists-with-different-credential'
// 2. Get the pending credential: GoogleAuthProvider.credentialFromError(e)
// 3. Ask user to sign in with the existing provider (email/password)
// 4. After sign-in, link the pending Google credential:
// await linkWithCredential(auth.currentUser, pendingCred);
// 10) Additional Google scopes (Drive, Calendar, etc.)
provider.addScope('https://www.googleapis.com/auth/drive.readonly');
// After signInWithPopup, use credential.accessToken to call Google APIs
const { credential } = await signInWithPopup(auth, provider);
const token = GoogleAuthProvider.credentialFromResult(credential).accessToken;
// 11) React Native — react-native-firebase + @react-native-google-signin/google-signin
import { GoogleSignin } from '@react-native-google-signin/google-signin';
import auth from '@react-native-firebase/auth';
GoogleSignin.configure({ webClientId: 'YOUR_WEB_CLIENT_ID.apps.googleusercontent.com' });
async function googleSignIn() {
await GoogleSignin.hasPlayServices();
const { idToken } = await GoogleSignin.signIn();
const credential = auth.GoogleAuthProvider.credential(idToken);
return auth().signInWithCredential(credential);
}
// 12) iOS (native) — Firebase iOS SDK + GoogleSignIn pod
// Android — Firebase Android SDK + Google Sign-In dependency
// 13) Best practices
// • Always require email scope — you need it to deduplicate accounts
// • Verify ID tokens server-side; never trust client-passed uid
// • Combine with custom claims for roles + tenant info
// • For migrating users from another auth system, use Firebase Auth Importer
// • For 'Sign in with Apple' — add the AppleAuthProvider (similar shape)
// 14) Hardening
// • App Check on the client to reject bot traffic
// • Rate limit + lock at the IP / device level for suspicious activity
// • Email user about new device sign-ins (Firebase doesn't do this automatically)
// • For high-risk apps, add MFA on top of Google sign-in (Firebase MFA: TOTP)
// • Don't store the Google access token longer than you need — refresh tokens live on Firebase
Why it matters
For most apps, Google OAuth via Firebase is two API calls + one backend token verify. The piece that bites teams: same-email collision between email/password and Google — build the link flow up-front, not after a user complaint.
Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.
Example
Example
import { GoogleAuthProvider, signInWithPopup } from 'firebase/auth';
const provider = new GoogleAuthProvider();
const { user } = await signInWithPopup(auth, provider);
console.log(user.displayName);
Try it Yourself »
Discussion
Loading…