Agentient

firebase-authentication-patterns

Firebase Authentication implementation patterns including sign-in flows, OAuth, and session management. PROACTIVELY activate for: (1) implementing sign-in/sign-up flows, (2) setting up OAuth providers like Google, (3) creating protected routes with middleware. Triggers: "auth", "sign-in", "oauth"

Agentient 2 1 Updated 4mo ago
GitHub

Install

npx skillscat add agentient/vibekit/firebase-authentication-patterns

Install via the SkillsCat registry.

SKILL.md

Firebase Authentication Patterns

Overview

Firebase Authentication provides backend services for authenticating users with email/password, OAuth providers (Google, GitHub, etc.), and anonymous authentication.

Authentication Providers

Email/Password Authentication

Sign Up:

import { createUserWithEmailAndPassword } from 'firebase/auth';
import { auth } from '@/lib/firebase/client';

export async function signUp(email: string, password: string, displayName: string) {
  try {
    const userCredential = await createUserWithEmailAndPassword(auth, email, password);

    // Update profile with display name
    await updateProfile(userCredential.user, { displayName });

    // Create user document in Firestore
    await setDoc(doc(db, 'users', userCredential.user.uid), {
      email,
      displayName,
      createdAt: serverTimestamp(),
      role: 'user',
    });

    return userCredential.user;
  } catch (error) {
    // Handle Firebase-specific errors
    if (error instanceof FirebaseError) {
      switch (error.code) {
        case 'auth/email-already-in-use':
          throw new Error('Email already registered');
        case 'auth/weak-password':
          throw new Error('Password must be at least 6 characters');
        case 'auth/invalid-email':
          throw new Error('Invalid email address');
        default:
          throw new Error('Sign up failed');
      }
    }
    throw error;
  }
}

Sign In:

import { signInWithEmailAndPassword } from 'firebase/auth';

export async function signIn(email: string, password: string) {
  try {
    const userCredential = await signInWithEmailAndPassword(auth, email, password);
    return userCredential.user;
  } catch (error) {
    if (error instanceof FirebaseError) {
      switch (error.code) {
        case 'auth/user-not-found':
        case 'auth/wrong-password':
          throw new Error('Invalid email or password');
        case 'auth/too-many-requests':
          throw new Error('Too many failed attempts. Try again later.');
        default:
          throw new Error('Sign in failed');
      }
    }
    throw error;
  }
}

OAuth Authentication

Google Sign-In:

import { signInWithPopup, GoogleAuthProvider } from 'firebase/auth';

export async function signInWithGoogle() {
  const provider = new GoogleAuthProvider();
  provider.addScope('profile');
  provider.addScope('email');

  try {
    const result = await signInWithPopup(auth, provider);
    const user = result.user;

    // Check if user document exists, create if not
    const userDoc = await getDoc(doc(db, 'users', user.uid));
    if (!userDoc.exists()) {
      await setDoc(doc(db, 'users', user.uid), {
        email: user.email,
        displayName: user.displayName,
        photoURL: user.photoURL,
        createdAt: serverTimestamp(),
        role: 'user',
      });
    }

    return user;
  } catch (error) {
    if (error instanceof FirebaseError) {
      switch (error.code) {
        case 'auth/popup-closed-by-user':
          throw new Error('Sign in cancelled');
        case 'auth/popup-blocked':
          throw new Error('Popup blocked. Please allow popups for this site.');
        default:
          throw new Error('Google sign in failed');
      }
    }
    throw error;
  }
}

Session Management

React Auth Provider

'use client';

import { createContext, useContext, useEffect, useState } from 'react';
import { User } from 'firebase/auth';
import { onAuthChange } from '@/lib/firebase/auth';

interface AuthContextType {
  user: User | null;
  loading: boolean;
}

const AuthContext = createContext<AuthContextType>({
  user: null,
  loading: true,
});

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

  useEffect(() => {
    const unsubscribe = onAuthChange((user) => {
      setUser(user);
      setLoading(false);

      // Store token in cookie for middleware
      if (user) {
        user.getIdToken().then((token) => {
          document.cookie = `firebase-token=${token}; path=/; secure; samesite=strict; max-age=3600`;
        });
      } else {
        document.cookie = 'firebase-token=; path=/; expires=Thu, 01 Jan 1970 00:00:01 GMT';
      }
    });

    return unsubscribe;
  }, []);

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

export function useAuth() {
  return useContext(AuthContext);
}

Protected Routes

Next.js Middleware

// middleware.ts
import { NextRequest, NextResponse } from 'next/server';
import { adminAuth } from '@/lib/firebase/admin';

export async function middleware(request: NextRequest) {
  const token = request.cookies.get('firebase-token')?.value;

  if (!token) {
    const url = new URL('/login', request.url);
    url.searchParams.set('redirect', request.nextUrl.pathname);
    return NextResponse.redirect(url);
  }

  try {
    await adminAuth.verifyIdToken(token);
    return NextResponse.next();
  } catch (error) {
    const response = NextResponse.redirect(new URL('/login', request.url));
    response.cookies.delete('firebase-token');
    return response;
  }
}

export const config = {
  matcher: ['/dashboard/:path*', '/profile/:path*', '/admin/:path*'],
};

Error Handling Reference

Firebase Error Code User-Friendly Message
auth/email-already-in-use Email already registered
auth/weak-password Password must be at least 6 characters
auth/invalid-email Invalid email address
auth/user-not-found Invalid email or password
auth/wrong-password Invalid email or password
auth/too-many-requests Too many attempts. Try again later.
auth/popup-closed-by-user Sign in cancelled
auth/popup-blocked Please allow popups
auth/requires-recent-login Please sign in again

Best Practices

Do:

  • Store tokens in HttpOnly cookies (not localStorage)
  • Verify tokens server-side with Admin SDK
  • Implement email verification for production
  • Use secure password requirements (min 8 chars, complexity)
  • Handle all Firebase error codes gracefully
  • Create user profile in Firestore after sign-up

Don't:

  • Store tokens in localStorage (XSS risk)
  • Trust client-side auth state alone
  • Reveal if email exists during password reset
  • Allow weak passwords
  • Skip email verification in production

Related Skills: firebase-admin-sdk-server-integration, firestore-security-rules-generation