firebase-auth

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Firebase Authentication

Firebase Authentication

Status: Production Ready Last Updated: 2026-01-25 Dependencies: None (standalone skill) Latest Versions: firebase@12.8.0, firebase-admin@13.6.0

状态:已就绪可用于生产环境 最后更新时间:2026-01-25 依赖项:无(独立技能) 最新版本:firebase@12.8.0,firebase-admin@13.6.0

Quick Start (5 Minutes)

快速入门(5分钟)

1. Enable Auth Providers in Firebase Console

1. 在Firebase控制台中启用认证提供商

  1. Go to Firebase Console > Authentication > Sign-in method
  2. Enable desired providers (Email/Password, Google, etc.)
  3. Configure OAuth providers with client ID/secret
  1. 进入Firebase控制台 > 身份认证 > 登录方法
  2. 启用所需的提供商(邮箱/密码、Google等)
  3. 使用客户端ID/密钥配置OAuth提供商

2. Initialize Firebase Auth (Client)

2. 初始化Firebase Auth(客户端)

typescript
// src/lib/firebase.ts
import { initializeApp } from 'firebase/app';
import { getAuth } from 'firebase/auth';

const firebaseConfig = {
  apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY,
  authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN,
  projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID,
  // ... other config
};

const app = initializeApp(firebaseConfig);
export const auth = getAuth(app);
typescript
// src/lib/firebase.ts
import { initializeApp } from 'firebase/app';
import { getAuth } from 'firebase/auth';

const firebaseConfig = {
  apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY,
  authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN,
  projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID,
  // ... 其他配置
};

const app = initializeApp(firebaseConfig);
export const auth = getAuth(app);

3. Initialize Firebase Admin (Server)

3. 初始化Firebase Admin(服务端)

typescript
// src/lib/firebase-admin.ts
import { initializeApp, cert, getApps } from 'firebase-admin/app';
import { getAuth } from 'firebase-admin/auth';

if (!getApps().length) {
  initializeApp({
    credential: cert({
      projectId: process.env.FIREBASE_PROJECT_ID,
      clientEmail: process.env.FIREBASE_CLIENT_EMAIL,
      privateKey: process.env.FIREBASE_PRIVATE_KEY?.replace(/\\n/g, '\n'),
    }),
  });
}

export const adminAuth = getAuth();

typescript
// src/lib/firebase-admin.ts
import { initializeApp, cert, getApps } from 'firebase-admin/app';
import { getAuth } from 'firebase-admin/auth';

if (!getApps().length) {
  initializeApp({
    credential: cert({
      projectId: process.env.FIREBASE_PROJECT_ID,
      clientEmail: process.env.FIREBASE_CLIENT_EMAIL,
      privateKey: process.env.FIREBASE_PRIVATE_KEY?.replace(/\\n/g, '\n'),
    }),
  });
}

export const adminAuth = getAuth();

Email/Password Authentication

邮箱/密码认证

Sign Up

注册

typescript
import { createUserWithEmailAndPassword, sendEmailVerification, updateProfile } from 'firebase/auth';
import { auth } from './firebase';

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

    // Update display name
    await updateProfile(user, { displayName });

    // Send verification email
    await sendEmailVerification(user);

    return user;
  } catch (error: any) {
    switch (error.code) {
      case 'auth/email-already-in-use':
        throw new Error('Email already registered');
      case 'auth/invalid-email':
        throw new Error('Invalid email address');
      case 'auth/weak-password':
        throw new Error('Password must be at least 6 characters');
      default:
        throw new Error('Sign up failed');
    }
  }
}
typescript
import { createUserWithEmailAndPassword, sendEmailVerification, updateProfile } from 'firebase/auth';
import { auth } from './firebase';

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

    // 更新显示名称
    await updateProfile(user, { displayName });

    // 发送验证邮件
    await sendEmailVerification(user);

    return user;
  } catch (error: any) {
    switch (error.code) {
      case 'auth/email-already-in-use':
        throw new Error('该邮箱已注册');
      case 'auth/invalid-email':
        throw new Error('无效的邮箱地址');
      case 'auth/weak-password':
        throw new Error('密码长度至少为6位');
      default:
        throw new Error('注册失败');
    }
  }
}

Sign In

登录

typescript
import { signInWithEmailAndPassword } from 'firebase/auth';
import { auth } from './firebase';

async function signIn(email: string, password: string) {
  try {
    const userCredential = await signInWithEmailAndPassword(auth, email, password);
    return userCredential.user;
  } catch (error: any) {
    switch (error.code) {
      case 'auth/user-not-found':
      case 'auth/wrong-password':
      case 'auth/invalid-credential':
        throw new Error('Invalid email or password');
      case 'auth/user-disabled':
        throw new Error('Account has been disabled');
      case 'auth/too-many-requests':
        throw new Error('Too many attempts. Try again later.');
      default:
        throw new Error('Sign in failed');
    }
  }
}
typescript
import { signInWithEmailAndPassword } from 'firebase/auth';
import { auth } from './firebase';

async function signIn(email: string, password: string) {
  try {
    const userCredential = await signInWithEmailAndPassword(auth, email, password);
    return userCredential.user;
  } catch (error: any) {
    switch (error.code) {
      case 'auth/user-not-found':
      case 'auth/wrong-password':
      case 'auth/invalid-credential':
        throw new Error('邮箱或密码无效');
      case 'auth/user-disabled':
        throw new Error('账户已被禁用');
      case 'auth/too-many-requests':
        throw new Error('尝试次数过多,请稍后再试');
      default:
        throw new Error('登录失败');
    }
  }
}

Sign Out

登出

typescript
import { signOut } from 'firebase/auth';
import { auth } from './firebase';

async function handleSignOut() {
  await signOut(auth);
  // Redirect to login page
}
typescript
import { signOut } from 'firebase/auth';
import { auth } from './firebase';

async function handleSignOut() {
  await signOut(auth);
  // 重定向到登录页面
}

Password Reset

重置密码

typescript
import { sendPasswordResetEmail, confirmPasswordReset } from 'firebase/auth';
import { auth } from './firebase';

// Send reset email
async function resetPassword(email: string) {
  await sendPasswordResetEmail(auth, email);
}

// Confirm reset (from email link)
async function confirmReset(oobCode: string, newPassword: string) {
  await confirmPasswordReset(auth, oobCode, newPassword);
}

typescript
import { sendPasswordResetEmail, confirmPasswordReset } from 'firebase/auth';
import { auth } from './firebase';

// 发送重置邮件
async function resetPassword(email: string) {
  await sendPasswordResetEmail(auth, email);
}

// 确认重置(通过邮件链接)
async function confirmReset(oobCode: string, newPassword: string) {
  await confirmPasswordReset(auth, oobCode, newPassword);
}

OAuth Providers (Google, GitHub, etc.)

OAuth提供商(Google、GitHub等)

Google Sign-In

Google登录

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

const googleProvider = new GoogleAuthProvider();
googleProvider.addScope('email');
googleProvider.addScope('profile');

// Popup method (recommended for desktop)
async function signInWithGoogle() {
  try {
    const result = await signInWithPopup(auth, googleProvider);
    const credential = GoogleAuthProvider.credentialFromResult(result);
    const token = credential?.accessToken;
    return result.user;
  } catch (error: any) {
    if (error.code === 'auth/popup-closed-by-user') {
      // User closed popup - not an error
      return null;
    }
    if (error.code === 'auth/popup-blocked') {
      // Fallback to redirect
      await signInWithRedirect(auth, googleProvider);
      return null;
    }
    throw error;
  }
}

// Redirect method (for mobile or popup-blocked)
async function signInWithGoogleRedirect() {
  await signInWithRedirect(auth, googleProvider);
}
typescript
import { signInWithPopup, signInWithRedirect, GoogleAuthProvider } from 'firebase/auth';
import { auth } from './firebase';

const googleProvider = new GoogleAuthProvider();
googleProvider.addScope('email');
googleProvider.addScope('profile');

// 弹窗方式(推荐桌面端使用)
async function signInWithGoogle() {
  try {
    const result = await signInWithPopup(auth, googleProvider);
    const credential = GoogleAuthProvider.credentialFromResult(result);
    const token = credential?.accessToken;
    return result.user;
  } catch (error: any) {
    if (error.code === 'auth/popup-closed-by-user') {
      // 用户关闭弹窗 - 不属于错误
      return null;
    }
    if (error.code === 'auth/popup-blocked') {
      // 回退到重定向方式
      await signInWithRedirect(auth, googleProvider);
      return null;
    }
    throw error;
  }
}

// 重定向方式(适用于移动端或弹窗被拦截的情况)
async function signInWithGoogleRedirect() {
  await signInWithRedirect(auth, googleProvider);
}

Handle Redirect Result

处理重定向结果

typescript
import { getRedirectResult, GoogleAuthProvider } from 'firebase/auth';
import { auth } from './firebase';

// Call on page load
async function handleRedirectResult() {
  try {
    const result = await getRedirectResult(auth);
    if (result) {
      const credential = GoogleAuthProvider.credentialFromResult(result);
      return result.user;
    }
  } catch (error) {
    console.error('Redirect sign-in error:', error);
  }
  return null;
}
typescript
import { getRedirectResult, GoogleAuthProvider } from 'firebase/auth';
import { auth } from './firebase';

// 在页面加载时调用
async function handleRedirectResult() {
  try {
    const result = await getRedirectResult(auth);
    if (result) {
      const credential = GoogleAuthProvider.credentialFromResult(result);
      return result.user;
    }
  } catch (error) {
    console.error('重定向登录错误:', error);
  }
  return null;
}

Other OAuth Providers

其他OAuth提供商

typescript
import {
  GithubAuthProvider,
  TwitterAuthProvider,
  FacebookAuthProvider,
  OAuthProvider,
} from 'firebase/auth';

// GitHub
const githubProvider = new GithubAuthProvider();
githubProvider.addScope('read:user');

// Microsoft
const microsoftProvider = new OAuthProvider('microsoft.com');
microsoftProvider.addScope('email');
microsoftProvider.addScope('profile');

// Apple
const appleProvider = new OAuthProvider('apple.com');
appleProvider.addScope('email');
appleProvider.addScope('name');

typescript
import {
  GithubAuthProvider,
  TwitterAuthProvider,
  FacebookAuthProvider,
  OAuthProvider,
} from 'firebase/auth';

// GitHub
const githubProvider = new GithubAuthProvider();
githubProvider.addScope('read:user');

// Microsoft
const microsoftProvider = new OAuthProvider('microsoft.com');
microsoftProvider.addScope('email');
microsoftProvider.addScope('profile');

// Apple
const appleProvider = new OAuthProvider('apple.com');
appleProvider.addScope('email');
appleProvider.addScope('name');

Auth State Management

认证状态管理

Listen to Auth State Changes

监听认证状态变化

typescript
import { onAuthStateChanged, User } from 'firebase/auth';
import { auth } from './firebase';

// React hook example
function useAuth() {
  const [user, setUser] = useState<User | null>(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    const unsubscribe = onAuthStateChanged(auth, (user) => {
      setUser(user);
      setLoading(false);
    });

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

  return { user, loading };
}
typescript
import { onAuthStateChanged, User } from 'firebase/auth';
import { auth } from './firebase';

// React Hook示例
function useAuth() {
  const [user, setUser] = useState<User | null>(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    const unsubscribe = onAuthStateChanged(auth, (user) => {
      setUser(user);
      setLoading(false);
    });

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

  return { user, loading };
}

Auth Context Provider (React)

认证上下文提供者(React)

typescript
// src/contexts/AuthContext.tsx
import { createContext, useContext, useEffect, useState, ReactNode } from 'react';
import { onAuthStateChanged, User } from 'firebase/auth';
import { auth } from '@/lib/firebase';

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

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

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

  useEffect(() => {
    const unsubscribe = onAuthStateChanged(auth, (user) => {
      setUser(user);
      setLoading(false);
    });

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

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

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

typescript
// src/contexts/AuthContext.tsx
import { createContext, useContext, useEffect, useState, ReactNode } from 'react';
import { onAuthStateChanged, User } from 'firebase/auth';
import { auth } from '@/lib/firebase';

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

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

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

  useEffect(() => {
    const unsubscribe = onAuthStateChanged(auth, (user) => {
      setUser(user);
      setLoading(false);
    });

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

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

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

Token Management

令牌管理

Get ID Token (for API calls)

获取ID令牌(用于API调用)

typescript
import { auth } from './firebase';

async function getIdToken() {
  const user = auth.currentUser;
  if (!user) throw new Error('Not authenticated');

  // Force refresh to get fresh token
  const token = await user.getIdToken(/* forceRefresh */ true);
  return token;
}

// Use in API calls
async function callProtectedAPI() {
  const token = await getIdToken();

  const response = await fetch('/api/protected', {
    headers: {
      Authorization: `Bearer ${token}`,
    },
  });

  return response.json();
}
typescript
import { auth } from './firebase';

async function getIdToken() {
  const user = auth.currentUser;
  if (!user) throw new Error('未认证');

  // 强制刷新以获取最新令牌
  const token = await user.getIdToken(/* forceRefresh */ true);
  return token;
}

// 在API调用中使用
async function callProtectedAPI() {
  const token = await getIdToken();

  const response = await fetch('/api/protected', {
    headers: {
      Authorization: `Bearer ${token}`,
    },
  });

  return response.json();
}

Verify ID Token (Server-side)

验证ID令牌(服务端)

typescript
// API route (Next.js example)
import { adminAuth } from '@/lib/firebase-admin';

export async function GET(request: Request) {
  const authHeader = request.headers.get('authorization');
  if (!authHeader?.startsWith('Bearer ')) {
    return Response.json({ error: 'Unauthorized' }, { status: 401 });
  }

  const token = authHeader.split('Bearer ')[1];

  try {
    const decodedToken = await adminAuth.verifyIdToken(token);
    const uid = decodedToken.uid;

    // User is authenticated, proceed with request
    return Response.json({ uid, message: 'Authenticated' });
  } catch (error) {
    return Response.json({ error: 'Invalid token' }, { status: 401 });
  }
}
typescript
// API路由(Next.js示例)
import { adminAuth } from '@/lib/firebase-admin';

export async function GET(request: Request) {
  const authHeader = request.headers.get('authorization');
  if (!authHeader?.startsWith('Bearer ')) {
    return Response.json({ error: '未授权' }, { status: 401 });
  }

  const token = authHeader.split('Bearer ')[1];

  try {
    const decodedToken = await adminAuth.verifyIdToken(token);
    const uid = decodedToken.uid;

    // 用户已认证,继续处理请求
    return Response.json({ uid, message: '已认证' });
  } catch (error) {
    return Response.json({ error: '无效令牌' }, { status: 401 });
  }
}

Session Cookies (Server-Side Rendering)

会话Cookie(服务端渲染)

typescript
import { adminAuth } from '@/lib/firebase-admin';

// Create session cookie
async function createSessionCookie(idToken: string) {
  const expiresIn = 60 * 60 * 24 * 5 * 1000; // 5 days

  const sessionCookie = await adminAuth.createSessionCookie(idToken, {
    expiresIn,
  });

  return sessionCookie;
}

// Verify session cookie
async function verifySessionCookie(sessionCookie: string) {
  try {
    const decodedClaims = await adminAuth.verifySessionCookie(sessionCookie, true);
    return decodedClaims;
  } catch (error) {
    return null;
  }
}

// Revoke session
async function revokeSession(uid: string) {
  await adminAuth.revokeRefreshTokens(uid);
}

typescript
import { adminAuth } from '@/lib/firebase-admin';

// 创建会话Cookie
async function createSessionCookie(idToken: string) {
  const expiresIn = 60 * 60 * 24 * 5 * 1000; // 5天

  const sessionCookie = await adminAuth.createSessionCookie(idToken, {
    expiresIn,
  });

  return sessionCookie;
}

// 验证会话Cookie
async function verifySessionCookie(sessionCookie: string) {
  try {
    const decodedClaims = await adminAuth.verifySessionCookie(sessionCookie, true);
    return decodedClaims;
  } catch (error) {
    return null;
  }
}

// 撤销会话
async function revokeSession(uid: string) {
  await adminAuth.revokeRefreshTokens(uid);
}

Custom Claims & Roles

自定义声明与角色

Set Custom Claims (Admin SDK)

设置自定义声明(Admin SDK)

typescript
import { adminAuth } from '@/lib/firebase-admin';

// Set admin role
async function setAdminRole(uid: string) {
  await adminAuth.setCustomUserClaims(uid, {
    admin: true,
    role: 'admin',
  });
}

// Set custom permissions
async function setUserPermissions(uid: string, permissions: string[]) {
  await adminAuth.setCustomUserClaims(uid, {
    permissions,
  });
}
typescript
import { adminAuth } from '@/lib/firebase-admin';

// 设置管理员角色
async function setAdminRole(uid: string) {
  await adminAuth.setCustomUserClaims(uid, {
    admin: true,
    role: 'admin',
  });
}

// 设置自定义权限
async function setUserPermissions(uid: string, permissions: string[]) {
  await adminAuth.setCustomUserClaims(uid, {
    permissions,
  });
}

Check Custom Claims (Client)

检查自定义声明(客户端)

typescript
import { auth } from './firebase';

async function checkAdminStatus() {
  const user = auth.currentUser;
  if (!user) return false;

  // Force token refresh to get latest claims
  const tokenResult = await user.getIdTokenResult(true);
  return tokenResult.claims.admin === true;
}

// In component
const isAdmin = await checkAdminStatus();
CRITICAL: Custom claims are cached in the ID token. After setting claims, the user must:
  1. Sign out and sign in again, OR
  2. Force refresh the token with
    getIdTokenResult(true)

typescript
import { auth } from './firebase';

async function checkAdminStatus() {
  const user = auth.currentUser;
  if (!user) return false;

  // 强制刷新令牌以获取最新声明
  const tokenResult = await user.getIdTokenResult(true);
  return tokenResult.claims.admin === true;
}

// 在组件中使用
const isAdmin = await checkAdminStatus();
重要提示: 自定义声明会被缓存到ID令牌中。设置声明后,用户必须:
  1. 登出后重新登录,或者
  2. 使用
    getIdTokenResult(true)
    强制刷新令牌

Phone Authentication

电话认证

typescript
import {
  signInWithPhoneNumber,
  RecaptchaVerifier,
  ConfirmationResult,
} from 'firebase/auth';
import { auth } from './firebase';

let confirmationResult: ConfirmationResult;

// Step 1: Setup reCAPTCHA (required)
function setupRecaptcha() {
  window.recaptchaVerifier = new RecaptchaVerifier(auth, 'recaptcha-container', {
    size: 'normal',
    callback: () => {
      // reCAPTCHA solved
    },
  });
}

// Step 2: Send verification code
async function sendVerificationCode(phoneNumber: string) {
  const appVerifier = window.recaptchaVerifier;
  confirmationResult = await signInWithPhoneNumber(auth, phoneNumber, appVerifier);
}

// Step 3: Verify code
async function verifyCode(code: string) {
  const result = await confirmationResult.confirm(code);
  return result.user;
}

typescript
import {
  signInWithPhoneNumber,
  RecaptchaVerifier,
  ConfirmationResult,
} from 'firebase/auth';
import { auth } from './firebase';

let confirmationResult: ConfirmationResult;

// 步骤1:设置reCAPTCHA(必填)
function setupRecaptcha() {
  window.recaptchaVerifier = new RecaptchaVerifier(auth, 'recaptcha-container', {
    size: 'normal',
    callback: () => {
      // reCAPTCHA验证通过
    },
  });
}

// 步骤2:发送验证码
async function sendVerificationCode(phoneNumber: string) {
  const appVerifier = window.recaptchaVerifier;
  confirmationResult = await signInWithPhoneNumber(auth, phoneNumber, appVerifier);
}

// 步骤3:验证验证码
async function verifyCode(code: string) {
  const result = await confirmationResult.confirm(code);
  return result.user;
}

Multi-Factor Authentication (MFA)

多因素认证(MFA)

typescript
import {
  multiFactor,
  PhoneAuthProvider,
  PhoneMultiFactorGenerator,
  getMultiFactorResolver,
} from 'firebase/auth';
import { auth } from './firebase';

// Enroll phone as second factor
async function enrollPhoneMFA(phoneNumber: string) {
  const user = auth.currentUser;
  if (!user) throw new Error('Not authenticated');

  const multiFactorSession = await multiFactor(user).getSession();

  const phoneAuthProvider = new PhoneAuthProvider(auth);
  const verificationId = await phoneAuthProvider.verifyPhoneNumber(
    { phoneNumber, session: multiFactorSession },
    window.recaptchaVerifier
  );

  // Return verificationId to complete enrollment after user enters code
  return verificationId;
}

// Complete enrollment
async function completeEnrollment(verificationId: string, verificationCode: string) {
  const user = auth.currentUser;
  if (!user) throw new Error('Not authenticated');

  const credential = PhoneAuthProvider.credential(verificationId, verificationCode);
  const multiFactorAssertion = PhoneMultiFactorGenerator.assertion(credential);

  await multiFactor(user).enroll(multiFactorAssertion, 'Phone Number');
}

// Handle MFA during sign-in
async function handleMFASignIn(error: any) {
  if (error.code !== 'auth/multi-factor-auth-required') {
    throw error;
  }

  const resolver = getMultiFactorResolver(auth, error);
  // Show UI to select MFA method and enter code
  return resolver;
}

typescript
import {
  multiFactor,
  PhoneAuthProvider,
  PhoneMultiFactorGenerator,
  getMultiFactorResolver,
} from 'firebase/auth';
import { auth } from './firebase';

// 注册电话作为第二因素
async function enrollPhoneMFA(phoneNumber: string) {
  const user = auth.currentUser;
  if (!user) throw new Error('未认证');

  const multiFactorSession = await multiFactor(user).getSession();

  const phoneAuthProvider = new PhoneAuthProvider(auth);
  const verificationId = await phoneAuthProvider.verifyPhoneNumber(
    { phoneNumber, session: multiFactorSession },
    window.recaptchaVerifier
  );

  // 返回verificationId,供用户输入验证码后完成注册
  return verificationId;
}

// 完成注册
async function completeEnrollment(verificationId: string, verificationCode: string) {
  const user = auth.currentUser;
  if (!user) throw new Error('未认证');

  const credential = PhoneAuthProvider.credential(verificationId, verificationCode);
  const multiFactorAssertion = PhoneMultiFactorGenerator.assertion(credential);

  await multiFactor(user).enroll(multiFactorAssertion, '电话号码');
}

// 登录时处理多因素认证
async function handleMFASignIn(error: any) {
  if (error.code !== 'auth/multi-factor-auth-required') {
    throw error;
  }

  const resolver = getMultiFactorResolver(auth, error);
  // 显示UI让用户选择多因素认证方式并输入验证码
  return resolver;
}

Protected Routes (Next.js)

受保护路由(Next.js)

Middleware Protection

中间件保护

typescript
// middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';

export function middleware(request: NextRequest) {
  const sessionCookie = request.cookies.get('session')?.value;

  // Protect /dashboard routes
  if (request.nextUrl.pathname.startsWith('/dashboard')) {
    if (!sessionCookie) {
      return NextResponse.redirect(new URL('/login', request.url));
    }
  }

  // Redirect authenticated users away from /login
  if (request.nextUrl.pathname === '/login') {
    if (sessionCookie) {
      return NextResponse.redirect(new URL('/dashboard', request.url));
    }
  }

  return NextResponse.next();
}

export const config = {
  matcher: ['/dashboard/:path*', '/login'],
};
typescript
// middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';

export function middleware(request: NextRequest) {
  const sessionCookie = request.cookies.get('session')?.value;

  // 保护/dashboard路由
  if (request.nextUrl.pathname.startsWith('/dashboard')) {
    if (!sessionCookie) {
      return NextResponse.redirect(new URL('/login', request.url));
    }
  }

  // 已认证用户重定向离开/login页面
  if (request.nextUrl.pathname === '/login') {
    if (sessionCookie) {
      return NextResponse.redirect(new URL('/dashboard', request.url));
    }
  }

  return NextResponse.next();
}

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

Client-Side Route Guard

客户端路由守卫

typescript
// components/ProtectedRoute.tsx
import { useAuth } from '@/contexts/AuthContext';
import { useRouter } from 'next/navigation';
import { useEffect } from 'react';

export function ProtectedRoute({ children }: { children: React.ReactNode }) {
  const { user, loading } = useAuth();
  const router = useRouter();

  useEffect(() => {
    if (!loading && !user) {
      router.push('/login');
    }
  }, [user, loading, router]);

  if (loading) {
    return <div>Loading...</div>;
  }

  if (!user) {
    return null;
  }

  return <>{children}</>;
}

typescript
// components/ProtectedRoute.tsx
import { useAuth } from '@/contexts/AuthContext';
import { useRouter } from 'next/navigation';
import { useEffect } from 'react';

export function ProtectedRoute({ children }: { children: React.ReactNode }) {
  const { user, loading } = useAuth();
  const router = useRouter();

  useEffect(() => {
    if (!loading && !user) {
      router.push('/login');
    }
  }, [user, loading, router]);

  if (loading) {
    return <div>加载中...</div>;
  }

  if (!user) {
    return null;
  }

  return <>{children}</>;
}

Error Handling

错误处理

Common Auth Errors

常见认证错误

Error CodeDescriptionUser-Friendly Message
auth/invalid-credential
Wrong email/passwordInvalid email or password
auth/user-not-found
Email not registeredInvalid email or password
auth/wrong-password
Incorrect passwordInvalid email or password
auth/email-already-in-use
Email registeredThis email is already registered
auth/weak-password
Password < 6 charsPassword must be at least 6 characters
auth/invalid-email
Malformed emailPlease enter a valid email
auth/user-disabled
Account disabledYour account has been disabled
auth/too-many-requests
Rate limitedToo many attempts. Try again later
auth/popup-closed-by-user
User closed popupSign-in cancelled
auth/popup-blocked
Browser blocked popupPlease allow popups
auth/requires-recent-login
Sensitive operationPlease sign in again
auth/network-request-failed
Network errorPlease check your connection
错误代码描述用户友好提示
auth/invalid-credential
邮箱/密码错误邮箱或密码无效
auth/user-not-found
邮箱未注册邮箱或密码无效
auth/wrong-password
密码错误邮箱或密码无效
auth/email-already-in-use
邮箱已注册该邮箱已被注册
auth/weak-password
密码长度不足6位密码长度至少为6位
auth/invalid-email
邮箱格式错误请输入有效的邮箱地址
auth/user-disabled
账户已被禁用您的账户已被禁用
auth/too-many-requests
请求频率受限尝试次数过多,请稍后再试
auth/popup-closed-by-user
用户关闭弹窗登录已取消
auth/popup-blocked
浏览器拦截弹窗请允许弹窗显示
auth/requires-recent-login
敏感操作需要重新登录请重新登录
auth/network-request-failed
网络错误请检查您的网络连接

Error Handler Utility

错误处理工具函数

typescript
export function getAuthErrorMessage(error: any): string {
  const errorMessages: Record<string, string> = {
    'auth/invalid-credential': 'Invalid email or password',
    'auth/user-not-found': 'Invalid email or password',
    'auth/wrong-password': 'Invalid email or password',
    'auth/email-already-in-use': 'This email is already registered',
    'auth/weak-password': 'Password must be at least 6 characters',
    'auth/invalid-email': 'Please enter a valid email address',
    'auth/user-disabled': 'Your account has been disabled',
    'auth/too-many-requests': 'Too many attempts. Please try again later',
    'auth/popup-closed-by-user': 'Sign-in was cancelled',
    'auth/network-request-failed': 'Network error. Please check your connection',
  };

  return errorMessages[error.code] || 'An unexpected error occurred';
}

typescript
export function getAuthErrorMessage(error: any): string {
  const errorMessages: Record<string, string> = {
    'auth/invalid-credential': '邮箱或密码无效',
    'auth/user-not-found': '邮箱或密码无效',
    'auth/wrong-password': '邮箱或密码无效',
    'auth/email-already-in-use': '该邮箱已被注册',
    'auth/weak-password': '密码长度至少为6位',
    'auth/invalid-email': '请输入有效的邮箱地址',
    'auth/user-disabled': '您的账户已被禁用',
    'auth/too-many-requests': '尝试次数过多,请稍后再试',
    'auth/popup-closed-by-user': '登录已取消',
    'auth/network-request-failed': '网络错误,请检查您的网络连接',
  };

  return errorMessages[error.code] || '发生了意外错误';
}

Known Issues Prevention

已知问题预防

This skill prevents 12 documented Firebase Auth errors:
Issue #Error/IssueDescriptionHow to AvoidSource
#1
auth/invalid-credential
Generic error for wrong email/passwordShow generic "invalid email or password" messageCommon
#2
auth/popup-blocked
Browser blocks OAuth popupImplement redirect fallbackDocs
#3
auth/requires-recent-login
Sensitive operations need fresh loginRe-authenticate before password change, deleteDocs
#4Custom claims not updatingClaims cached in ID tokenForce token refresh after setting claimsDocs
#5Token expirationID tokens expire after 1 hourAlways call
getIdToken()
before API calls
Common
#6Memory leak from auth listenerNot unsubscribing from
onAuthStateChanged
Return cleanup function in useEffectCommon
#7CORS errors on localhostAuth domain mismatchAdd localhost to authorized domains in consoleCommon
#8Private key newline issueEscaped
\n
in env var
Use
.replace(/\\n/g, '\n')
Common
#9Session cookie not persistingCookie settings wrongSet
secure: true
,
sameSite: 'lax'
in production
Common
#10OAuth state mismatchUser navigates away during OAuthHandle
auth/popup-closed-by-user
gracefully
Common
#11Rate limitingToo many auth attemptsImplement exponential backoffDocs
#12Email enumerationDifferent errors for existing/non-existing emailsUse same message for bothSecurity best practice

本技能可预防12种已记录的Firebase Auth错误:
问题编号错误/问题描述避免方法来源
#1
auth/invalid-credential
邮箱/密码错误的通用提示显示通用的“邮箱或密码无效”提示常见问题
#2
auth/popup-blocked
浏览器拦截OAuth弹窗实现重定向回退方案文档
#3
auth/requires-recent-login
敏感操作需要重新登录在修改密码、删除账户等操作前重新认证用户文档
#4自定义声明未更新声明被缓存到ID令牌中设置声明后强制刷新令牌文档
#5令牌过期ID令牌1小时后过期在API调用前始终调用
getIdToken()
常见问题
#6认证监听器内存泄漏未取消订阅
onAuthStateChanged
在useEffect中返回清理函数常见问题
#7本地主机CORS错误认证域名不匹配在控制台中将localhost添加到授权域名常见问题
#8私钥换行问题环境变量中的
\n
被转义
使用
.replace(/\\n/g, '\n')
处理
常见问题
#9会话Cookie未持久化Cookie设置错误生产环境中设置
secure: true
sameSite: 'lax'
常见问题
#10OAuth状态不匹配用户在OAuth过程中导航离开优雅处理
auth/popup-closed-by-user
错误
常见问题
#11请求频率限制认证尝试次数过多实现指数退避机制文档
#12邮箱枚举针对存在/不存在的邮箱返回不同错误对两种情况使用相同提示安全最佳实践

Security Best Practices

安全最佳实践

Always Do

始终遵循

  1. Use HTTPS in production - Required for secure cookies
  2. Validate tokens server-side - Never trust client claims alone
  3. Handle token expiration - Refresh before API calls
  4. Implement rate limiting - Prevent brute force attacks
  5. Use same error message for wrong email/password - Prevent enumeration
  6. Enable email verification - Verify user owns email
  7. Use session cookies for SSR - More secure than ID tokens in cookies
  8. Revoke tokens on password change - Invalidate old sessions
  1. 生产环境使用HTTPS - 安全Cookie的必要条件
  2. 服务端验证令牌 - 绝不单独信任客户端声明
  3. 处理令牌过期 - API调用前刷新令牌
  4. 实现请求频率限制 - 防止暴力破解攻击
  5. 邮箱/密码错误使用相同提示 - 防止邮箱枚举
  6. 启用邮箱验证 - 验证用户拥有该邮箱
  7. SSR使用会话Cookie - 比Cookie中的ID令牌更安全
  8. 修改密码时撤销令牌 - 使旧会话失效

Never Do

绝不做

  1. Never expose private key in client code
  2. Never trust client-provided claims - Always verify server-side
  3. Never store ID tokens in localStorage - Use httpOnly cookies
  4. Never disable email enumeration protection in production
  5. Never skip CORS configuration for your domains

  1. 绝不暴露私钥在客户端代码中
  2. 绝不信任客户端提供的声明 - 始终在服务端验证
  3. 绝不将ID令牌存储在localStorage中 - 使用HttpOnly Cookie
  4. 生产环境绝不禁用邮箱枚举保护
  5. 绝不跳过域名的CORS配置

Firebase CLI Commands

Firebase CLI命令

bash
undefined
bash
undefined

Initialize Firebase project

初始化Firebase项目

firebase init
firebase init

Enable auth emulator

启用认证模拟器

firebase emulators:start --only auth
firebase emulators:start --only auth

Export auth users

导出认证用户

firebase auth:export users.json --format=json
firebase auth:export users.json --format=json

Import auth users

导入认证用户

firebase auth:import users.json

---
firebase auth:import users.json

---

Package Versions (Verified 2026-01-25)

包版本(2026-01-25已验证)

json
{
  "dependencies": {
    "firebase": "^12.8.0"
  },
  "devDependencies": {
    "firebase-admin": "^13.6.0"
  }
}

json
{
  "dependencies": {
    "firebase": "^12.8.0"
  },
  "devDependencies": {
    "firebase-admin": "^13.6.0"
  }
}

Official Documentation

官方文档