发布于 2026-01-06 0 阅读
0

使用 Firebase 身份验证保护您的 express/Node.js API

使用 Firebase 身份验证保护您的 express/Node.js API

很多应用,无论是移动应用还是网页应用,都需要某种形式的身份验证。如果你开发过各种应用,就会发现处理身份验证是一项相当重复且枯燥的任务,因此我喜欢使用像 Auth0 或 Firebase 这样的外部服务来简化身份验证流程。这些服务还可以处理社交身份验证,从而节省大量代码。我们只需要专注于集成即可。

本文将介绍如何使用 Firebase 来保护我们的 API,确保只有授权用户才能访问我们的资源。一种常见的 API 安全保护方法是使用 JWT 令牌,该令牌在用户提供有效的身份验证凭据后生成,并在每次请求时进行验证。这与我们将要使用 Firebase 实现的功能非常相似。我们将利用 Firebase 来处理令牌的生成和验证。

请注意,本文并非旨在教您如何创建/启动 Express 服务器。如果您不熟悉 Node.js 或 Express,建议您在阅读本文之前先了解相关知识。

是时候深入了解一下代码了。

访问您的Firebase 控制台,如果您尚未创建新项目,请创建一个新项目。

服务器端

服务器端方面,我们将使用 Firebase 管理 SDK,因为它更适合我们想要实现的目标。

使用以下命令在服务器上安装管理 SDK:

npm i firebase-admin

为了验证您是从受信任的环境调用 API,Google 建议您为项目生成并下载服务帐号密钥,并将其添加到环境变量中的某个路径。因此,请前往 Google 控制台,生成服务帐号密钥,下载它(最好是 JSON 格式),并将其位置添加到运行服务器的环境变量中的某个路径(GOOGLE_APPLICATION_CREDENTIALS)中。

exports GOOGLE_APPLICATION_CREDENTIALS=/path/to/service-account-file.json

点击这里查看更多相关信息。

现在我们可以在项目中创建一个服务,在该服务中,我们将使用我们的凭据初始化 SDK 并将其导出。

import * as admin from 'firebase-admin';

admin.initializeApp(
  credential: admin.credential.applicationDefault(),
  databaseURL: 'https://<DATABASE_NAME>.firebaseio.com'
);

export default admin

接下来,我们将编写处理创建新用户的逻辑。我们可以将此逻辑放在身份验证控制器中,或者任何您认为合适的地方。

import admin from './firebase-service';

export const createUser = async (req, res) => {
const {
      email,
      phoneNumber,
      password,
      firstName,
      lastName,
      photoUrl
    } = req.body;

    const user = await admin.auth().createUser({
      email,
      phoneNumber,
      password,
      displayName: `${firstName} ${lastName}`,
      photoURL: photoUrl
    });

    return res.send(user);
}

现在创建用户的逻辑已经实现。接下来,我们需要确保所有传入的请求都来自已认证的用户。我们可以通过创建中间件来保护那些我们希望保持私密的路由,从而实现这一点。

我们将创建一个身份验证中间件,以确保请求标头中存在有效的 Firebase 令牌。

import admin from './firebase-service';


const getAuthToken = (req, res, next) => {
  if (
    req.headers.authorization &&
    req.headers.authorization.split(' ')[0] === 'Bearer'
  ) {
    req.authToken = req.headers.authorization.split(' ')[1];
  } else {
    req.authToken = null;
  }
  next();
};


export const checkIfAuthenticated = (req, res, next) => {
 getAuthToken(req, res, async () => {
    try {
      const { authToken } = req;
      const userInfo = await admin
        .auth()
        .verifyIdToken(authToken);
      req.authId = userInfo.uid;
      return next();
    } catch (e) {
      return res
        .status(401)
        .send({ error: 'You are not authorized to make this request' });
    }
  });
};


有了这套中间件,用户每次尝试在未经过身份验证的情况下访问私有资源时,都会收到“未授权”错误。

现在我们已经创建了中间件,让我们用它来保护我们的私有路由。

import {Router} from 'express';
import {createUser} from './controllers/authcontroller';
import {checkIfAuthenticated} from './middlewares/auth-middleware';
import {articles} from from './data';

const router = Router();


router.post('/auth/signup', createUser);

router.get('/articles', checkIfAuthenticated, async (_, res) => {
  return res.send(articles);
});  

export default router;


上面的代码定义了两条路由。一条用于创建用户,另一条仅当用户通过身份验证后才用于获取文章。现在让我们转到客户端,看看如何使用这个 API。

客户端

我们可以使用任何用于 Web 或移动应用的 JavaScript 客户端库或框架来调用我们的 API,因此我不会具体说明任何库或框架,而是重点介绍 Firebase JavaScript SDK。尽管不同的 JavaScript 库/框架在 SDK 中可能存在一些差异,但它们的 API 与官方 Web SDK 仍然非常相似。

因此,我们在客户端安装 Firebase。

npm i firebase

注意:您的平台可能需要不同的 SDK 和安装方法,例如 angular-fire 和 react-native-firebase。

为了保持代码的简洁性,我们还可以在客户端创建一个服务,用于使用我们的配置初始化 Firebase。

import * as firebase from 'firebase/app';
import 'firebase/auth';

const config = {
  apiKey: "api-key",
  authDomain: "project-id.firebaseapp.com",
  databaseURL: "https://project-id.firebaseio.com",
  projectId: "project-id",
  storageBucket: "project-id.appspot.com",
  messagingSenderId: "sender-id",
  appID: "app-id",
}

firebase.initializeApp(config);

export const auth = firebase.auth

export default firebase;

您的凭据可在Firebase 控制台中找到。如果您未在 Web 端使用 JavaScript,则应查看如何在您的特定平台上初始化 Firebase。

我们将创建一个身份验证服务,用于调用注册端点和用户登录。

import axios from 'axios';
import {auth} from './firebase-service';


export const createUserAccount = (data) => {
  return axios.post('https://your-api-url/auth/signup', data)
    .then(res => res.data)
}


export const loginUser = (email, password) => {
  return auth().signInWithEmailAndPassword(email, password);
}

我们已经定义了创建用户并将其登录到我们应用的逻辑。接下来,我们可以通过 Firebase 检查用户是否已登录。


firebase.auth().onAuthStateChanged(user => {
   if (user) {
     return user;
   }
});

现在我们已经完成了注册和登录流程,接下来让我们在客户端生成一个令牌,用于服务器端的身份验证。这只需一行代码即可轻松完成。没错!你没听错,只需一行代码。


const token = await firebase.auth.currentUser.getIdToken();

您可以像上面那样在异步函数中使用它,或者解析 Promise 来获取令牌值。我们将向 API 发送请求,并将令牌附加到请求头中,以访问文章资源。

import {auth} from './firebase-service';

export const getArticles = async () => {
const token = await auth.currentUser.getIdToken();

return axios.get('https://your-api-url/articles', {headers:  
  { authorization: `Bearer ${token}` }})
  .then(res => res.data);
}

我们只需将 Firebase 令牌添加到授权标头中即可。服务器端会提取该令牌并用于验证用户身份。这一切都将由我们之前创建的中间件处理。

用户角色

用户身份验证中一个非常重要的环节是角色管理。如果我们想要设置不同的授权级别,并限制具有特定角色的用户访问某些资源,该怎么办?使用 Firebase 身份验证也很容易实现这一点。

我们将管理服务器上的角色,以下是具体操作方法。

import admin from './firebase-service';

export const makeUserAdmin = async (req, res) => {
  const {userId} = req.body; // userId is the firebase uid for the user

  await admin.auth().setCustomUserClaims(userId, {admin: true});

  return res.send({message: 'Success'})
}

既然我们已经可以为用户分配角色,那么如何检查用户是否拥有特定角色呢?很简单,当我们在中间件中验证用户令牌时,就可以轻松地从返回的数据中获取此信息。我们将添加一个中间件来检查用户是否拥有管理员角色。

import admin from './firebase-service';

const getAuthToken = (req, res, next) => {
  if (
    req.headers.authorization &&
    req.headers.authorization.split(' ')[0] === 'Bearer'
  ) {
    req.authToken = req.headers.authorization.split(' ')[1];
  } else {
    req.authToken = null;
  }
  next();
};

export const checkIfAdmin = (req, res, next) => {
 getAuthToken(req, res, async () => {
    try {
      const { authToken } = req;
      const userInfo = await admin
        .auth()
        .verifyIdToken(authToken);

      if (userInfo.admin === true) {
        req.authId = userInfo.uid;
        return next();
      }

      throw new Error('unauthorized')
    } catch (e) {
      return res
        .status(401)
        .send({ error: 'You are not authorized to make this request' });
    }
  });
};

现在我们可以使用此中间件来保护我们的管理资源。以下是我们更新后的路由。

import {Router} from 'express';
import {createUser} from './controllers/authcontroller';
import {checkIfAuthenticated, checkifAdmin} from './middlewares/auth-middleware';
import {articles} from from './data';
import {records} from './data/admin';

const router = Router();

router.post('/auth/signup', createUser);

router.get('/stories', checkIfAuthenticated, async (_, res) => {
  return res.send(articles);
});  

router.get('/admin/records', checkIfAdmin, async (_, res) => {
  return res.send(records);
});

export default router;


任何未分配管理员角色的令牌,如果尝试访问我们的管理资源,将会收到“未授权”错误。

还有很多内容可以探讨,但本文就只介绍这些。希望这些内容足以帮助您在服务器端开始使用 Firebase 身份验证。您可以通过查阅Firebase 文档了解更多功能。

文章来源:https://dev.to/emeka/secure-your-express-node-js-api-with-firebase-auth-4b5f