firebase 登录方法封装


firebase 登录方法封装

在现代 Web 应用中,用户登录与身份验证是不可或缺的功能。Firebase 提供了完整的 Authentication 解决方案,支持多种登录方式(如 Google、Facebook、Apple、邮箱密码等),并与前端框架(如 React、Vue、Next.js)完美集成。

本文将封装一个通用的 Firebase 登录方法,支持多种登录方式、灵活的凭证输入,以及统一的类型定义。

一、准备工作

在开始之前,需要确保你已经完成以下步骤:

  1. 已在 Firebase 控制台创建项目;
  2. 启用了 Authentication 服务;
  3. 添加了需要的登录提供商(如 Google、Facebook 等);
  4. 安装 Firebase SDK:
npm install firebase

二、封装

import {
  getAuth,
  FacebookAuthProvider,
  signInWithPopup,
  GoogleAuthProvider,
  fetchSignInMethodsForEmail,
  signInWithEmailAndPassword,
  createUserWithEmailAndPassword,
  OAuthProvider,
  linkWithPopup,
  EmailAuthProvider,
  linkWithCredential,
  AuthCredential,
} from "firebase/auth";

type ProviderId = "google.com" | "facebook.com" | "apple.com" | "password";
// 支持两种传参:一是 OAuthTokens,二是已经构造好的 AuthCredential
type CredentialInput = OAuthTokens | AuthCredential;
interface OAuthTokens {
  idToken: string;
  accessToken?: string;
}
/**
 * 使用 Google 异步登录函数
 *
 * 此函数通过 Google 身份验证提供程序初始化登录流程,尝试使用弹出窗口让用户登录
 * 如果登录成功,返回包含用户信息的结果如果登录失败,返回错误信息
 *
 * @returns {Promise} 返回一个 Promise 对象,包含登录结果或错误信息
 */
export async function loginWithGoogle() {
  const auth = getAuth();
  // auth.languageCode = 'it';
  // To apply the default browser preference instead of explicitly setting it.
  // auth.useDeviceLanguage();
  const provider = new GoogleAuthProvider();
  try {
    const result = await signInWithPopup(auth, provider);
    return { data: result };
  } catch (error: any) {
    const errorInfo = {
      code: error.code,
      message: error.message,
      email: error.customData?.email,
      credential: GoogleAuthProvider.credentialFromError(error),
    };
    return {
      error,
      errorInfo,
    };
  }
}

export async function loginWithPassword(email: string, password: string) {
  const auth = getAuth();
  const methods = await fetchSignInMethodsForEmail(auth, email);
  let result = null;
  try {
    if (methods.length) {
      result = await signInWithEmailAndPassword(auth, email, password);
    } else {
      result = await createUserWithEmailAndPassword(auth, email, password);
    }

    return { data: result };
  } catch (error: any) {
    const errorInfo = {
      code: error.code,
      message: error.message,
      email: error.customData?.email,
      credential: { providerId: "password" },
    };
    return { error, errorInfo };
  }
}

export async function loginWithFacebook() {
  const auth = getAuth();
  const provider = new FacebookAuthProvider();
  try {
    const result = await signInWithPopup(auth, provider);
    return { data: result };
  } catch (error: any) {
    const errorInfo = {
      code: error.code,
      message: error.message,
      email: error.customData?.email,
      credential: FacebookAuthProvider.credentialFromError(error),
    };
    return {
      error,
      errorInfo,
    };
  }
}

export async function loginWithApple() {
  const auth = getAuth();
  const provider = new OAuthProvider("apple.com");
  try {
    const result = await signInWithPopup(auth, provider);
    return { data: result };
  } catch (error: any) {
    const errorInfo = {
      code: error.code,
      message: error.message,
      email: error.customData?.email,
      credential: OAuthProvider.credentialFromError(error),
    };
    return {
      error,
      errorInfo,
    };
  }
}

export async function linkWithGoogle() {
  const provider = new GoogleAuthProvider();
  const auth = getAuth();
  if (!auth.currentUser) {
    return { error: "No user currently signed in" };
  }
  try {
    const result = await linkWithPopup(auth.currentUser, provider);
    return { data: result };
  } catch (error) {
    return { error };
  }
}

export async function linkWithFacebook() {
  const provider = new FacebookAuthProvider();
  const auth = getAuth();
  if (!auth.currentUser) {
    return { error: "No user currently signed in" };
  }
  try {
    const result = await linkWithPopup(auth.currentUser, provider);
    return { data: result };
  } catch (error) {
    return { error };
  }
}

export async function linkWithApple() {
  const provider = new OAuthProvider("apple.com");
  const auth = getAuth();
  if (!auth.currentUser) {
    return { error: "No user currently signed in" };
  }
  try {
    const result = await linkWithPopup(auth.currentUser, provider);
    return { data: result };
  } catch (error) {
    return { error };
  }
}

export async function linkWithPassword(email: string, password: string) {
  const auth = getAuth();
  const user = auth.currentUser;

  if (!user) return { error: "No user is currently signed in" };

  try {
    // 构造 email/password 凭证
    const credential = EmailAuthProvider.credential(email, password);

    // 链接凭证到当前用户
    const result = await linkWithCredential(user, credential);
    return { data: result };
  } catch (error) {
    return { error };
  }
}

export async function linkWithOAuthToken(
  providerId: ProviderId,
  credentialInput: CredentialInput
) {
  const auth = getAuth();
  const user = auth.currentUser;

  if (!user) {
    return { error: "No user currently signed in" };
  }

  let credential: AuthCredential;

  // 如果传入的是 AuthCredential,直接用
  if (
    "providerId" in credentialInput &&
    typeof (credentialInput as AuthCredential).signInMethod === "string"
  ) {
    credential = credentialInput as AuthCredential;
  } else {
    // 否则根据 providerId 和 tokens 生成凭证
    const tokens = credentialInput as OAuthTokens;

    switch (providerId) {
      case "google.com":
        credential = GoogleAuthProvider.credential(
          tokens.idToken!,
          tokens.accessToken
        );
        break;

      case "facebook.com":
        credential = FacebookAuthProvider.credential(tokens.accessToken!);
        break;

      case "apple.com":
        const appleProvider = new OAuthProvider("apple.com");
        credential = appleProvider.credential({
          idToken: tokens.idToken,
          accessToken: tokens.accessToken,
        });
        break;

      default:
        return { error: "Unsupported provider" };
    }
  }

  try {
    const result = await linkWithCredential(user, credential);
    return { data: result };
  } catch (error) {
    return { error };
  }
}

文章作者: fullsize
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 fullsize !
  目录