You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在Firebase云函数中实现Google OAuth:如何通过access_token识别用户

在Firebase Cloud Functions中实现Google OAuth

我已经让整体流程正常运行,但遇到一个核心问题:无法识别用户,没法把令牌保存到对应的Firestore用户对象中。

我用Google API Node.js库,通过OAuth2客户端生成授权URL并设置权限范围,之后重定向用户到该URL,这部分没问题:

const {google} = require('googleapis');
const oauth2Client = new google.auth.OAuth2(
  YOUR_CLIENT_ID,
  YOUR_CLIENT_SECRET,
  YOUR_REDIRECT_URL
);

const scopes = [
    'https://www.googleapis.com/auth/calendar'
];

const authorizationUrl = oauth2Client.generateAuthUrl({
    // 'online' (默认) 或 'offline' (获取refresh_token)
    access_type: 'offline',
    state: 'state_parameter_gets_passed_back',
    scope: scopes,
    // 启用增量授权,推荐最佳实践
    include_granted_scopes: true
  });
console.log('created an authorizationUrl: ' + authorizationUrl);
res.redirect(authorizationUrl);

我把一个HTTPS云函数端点设为回调URL,用来接收授权响应。收到响应后能成功获取授权码并兑换令牌,但执行saveUserToken时卡壳了:这个令牌到底属于哪个用户? 我的云函数只负责监听响应,没有用户身份信息:

exports.recieveGoogleCodeFromResponseURL = functions.https.onRequest(async (req, res) => {     
  const code = req.query.code;
  console.log('got a code, it is:' + code);

  const url = require('url');
  if (req.query.code != null) {

    let userCredential;
    console.log('we have a code, trading it for tokens');

    let { tokens } = await oauth2Client.getToken(code);
    console.log({ tokens });
    oauth2Client.setCredentials(tokens);
      
// 问题就在这里:要把令牌保存给哪个用户?
    saveUserToken(tokens, uid); // 保存到Firestore
  }

  res.json({result: `Got a response from Google`, code: code, scope: req.query.scope});
});

令牌的响应格式如下:

{
access_token: "longtoken",
expiry_date: 166...,
refresh_token: "anothertoken",
scope: "https://www.googleapis.com/auth/calendar",
token_type: "Bearer"
}

我知道access_token和refresh_token都不是JWT令牌,没法解码出用户信息。我查的Firebase云函数示例都提到“生产环境要把令牌存到安全的持久化数据库”,我可以用Firestore实现,但就是没法确定回调里的授权码对应哪个用户。

其他平台(比如Instagram、LinkedIn、Twitch)的OAuth示例里,要么兑换授权码的响应直接返回用户ID,要么能用access_token调用API获取用户信息:

  • Instagram示例:兑换授权码后直接拿到用户ID
const oauth2 = instagramOAuth2Client();
const results = await oauth2.authorizationCode.getToken({
        code: req.query.code,
        redirect_uri: OAUTH_REDIRECT_URI,
      });
      functions.logger.log('Auth code exchange result received:', results);

        // 现在我们有了Instagram的access_token和用户身份
const accessToken = results.access_token;
const instagramUserID = results.user.id;
  • LinkedIn示例:用access_token调用API获取用户信息
const linkedin = Linkedin.init(results.access_token);
linkedin.people.me(async (error, userResults) => {
  if (error) {
    throw error;
  }
  functions.logger.log(
    'Auth code exchange result received:',
    userResults
  );

  // 现在我们有了LinkedIn的access_token和用户身份
  const linkedInUserID = userResults.id;

我知道有个Google库可以验证ID令牌,但这次OAuth流程根本没返回ID令牌。我感觉自己漏了个简单的方法,想问:有没有可以传入access_token来识别用户的Google API?


解决方法

你可以通过以下两种方式获取用户身份:

  1. 调用Google People API获取用户信息
    在拿到access_token后,用Google API调用People API的people.get接口,获取用户的基本信息(比如邮箱、用户ID)。需要先在授权范围里加上https://www.googleapis.com/auth/userinfo.email或https://www.googleapis.com/auth/userinfo.profile(根据需求选择)。

示例代码:

const {google} = require('googleapis');
const people = google.people({version: 'v1', auth: oauth2Client});

const userInfo = await people.people.get({
  resourceName: 'people/me',
  personFields: 'emailAddresses,metadata'
});

const userEmail = userInfo.data.emailAddresses[0].value;
// 之后可以用这个邮箱去Firebase Auth里查找对应的用户UID
const userRecord = await admin.auth().getUserByEmail(userEmail);
const uid = userRecord.uid;
saveUserToken(tokens, uid);
  1. 利用state参数传递用户身份
    在生成授权URL时,把当前已登录用户的UID加密后放到state参数里(注意要加密防止篡改),回调时从req.query.state里取出解密,就能拿到用户UID。

示例修改:
生成授权URL时:

const uid = "当前登录用户的UID";
// 简单加密(生产环境建议用更安全的加密方式,比如JWT)
const encryptedState = Buffer.from(uid).toString('base64');

const authorizationUrl = oauth2Client.generateAuthUrl({
    access_type: 'offline',
    state: encryptedState,
    scope: [...scopes, 'https://www.googleapis.com/auth/userinfo.email'],
    include_granted_scopes: true
  });

回调函数里:

exports.recieveGoogleCodeFromResponseURL = functions.https.onRequest(async (req, res) => {     
  const code = req.query.code;
  const encryptedState = req.query.state;
  // 解密
  const uid = Buffer.from(encryptedState, 'base64').toString('utf8');

  if (code != null) {
    let { tokens } = await oauth2Client.getToken(code);
    oauth2Client.setCredentials(tokens);

    saveUserToken(tokens, uid);
  }

  res.json({result: `Got a response from Google`, code: code, scope: req.query.scope});
});

注意:第二种方法需要确保用户在触发授权流程时已经通过Firebase Auth登录,这样你才能拿到用户的UID。如果用户未登录,还是需要用第一种方法通过API获取用户信息,再关联到Firebase用户。

内容的提问来源于stack exchange,提问作者Joshua Dance

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.16 18:05:33