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

React Native Expo应用Google邮箱离线授权问题求助

解决React Native Expo Google登录无法获取Refresh Token的问题

问题核心

你当前用Google.useIdTokenAuthRequest获取的是ID令牌(id_token),这个方法仅用于身份验证,不会返回授权码或刷新令牌(refresh token)。但后端代发邮件需要具备邮箱权限的access token,且需要refresh token来持久化获取新的access token,所以必须切换到授权码流程。

解决方案步骤

1. 前端修改:切换到授权码流程

使用Expo的Google.useAuthRequest方法(而非useIdTokenAuthRequest),配置离线访问权限和邮箱相关的scope,确保能拿到授权码(code)。

修改后的前端代码:

import { Google } from 'expo-auth-session';
import { useState, useEffect } from 'react';
import { getAuth, signInWithCredential, GoogleAuthProvider } from 'firebase/auth';
import jwt_decode from 'jwt-decode';

// 配置Google授权请求
const [request, response, promptAsync] = Google.useAuthRequest({
  clientId: "你的Web/Android/iOS客户端ID",
  androidClientId: "你的Android客户端ID",
  iosClientId: "你的iOS客户端ID",
  // 必须配置离线访问,才能获取refresh token
  accessType: 'offline',
  // 申请邮箱发送权限,根据需求调整scope
  scopes: ['https://www.googleapis.com/auth/gmail.send', 'openid', 'email'],
  // 强制重新请求授权,确保首次登录就能拿到refresh token
  prompt: 'consent'
});

const [user, setUser] = useState();
const sendNotification = useNotification();

useEffect(() => {
  if (response?.type === "success") {
    const { code } = response.params;
    // 将授权码code传给后端,由后端去交换access token和refresh token
    socialLoginWithCode(code);

    // 同时可以继续用id_token做Firebase身份验证(如果需要)
    const { id_token } = response.params;
    const auth = getAuth();
    const credential = GoogleAuthProvider.credential(id_token);
    signInWithCredential(auth, credential);
    let decoded = jwt_decode(id_token);
    setUser(decoded);
  }
}, [response]);

// 新增:将code传给后端的函数
const socialLoginWithCode = async (code) => {
  try {
    const res = await fetch('你的后端接口地址', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ code })
    });
    const data = await res.json();
    // 处理后端返回的结果
  } catch (err) {
    console.error('提交授权码失败:', err);
  }
};

2. 后端修改:用授权码交换Tokens并持久化

后端需要用前端传来的code去Google服务器交换access token和refresh token,之后保存refresh token,发送邮件时用refresh token获取有效的access token。

修改后的后端代码:

const { google } = require('googleapis');
const path = require('path');
const fs = require('fs');
const credentials = require('./credentials.json');

// 注意:这里要使用你在Google Cloud Console创建的对应客户端(比如Web/Android/iOS)的信息,不是installed类型
const { client_secret, client_id, redirect_uris } = credentials.web; // 或credentials.android/credentials.ios,根据你的客户端类型

// 处理前端传来的code的接口示例
app.post('/exchange-token', async (req, res) => {
  const { code } = req.body;
  const oAuth2Client = new google.auth.OAuth2(client_id, client_secret, redirect_uris[0]);

  try {
    const { tokens } = await oAuth2Client.getToken(code);
    // 保存tokens到数据库或文件(示例中保存到token.json)
    const tokenPath = path.join(__dirname, 'token.json');
    fs.writeFileSync(tokenPath, JSON.stringify(tokens));
    console.log('Access token和refresh token已保存');
    res.status(200).json({ message: 'Tokens获取成功' });
  } catch (err) {
    console.error('交换Tokens失败:', err);
    res.status(500).json({ error: '交换Tokens失败' });
  }
});

// 发送邮件的函数,使用保存的refresh token获取有效access token
const sendEmail = async () => {
  const oAuth2Client = new google.auth.OAuth2(client_id, client_secret, redirect_uris[0]);
  // 读取保存的tokens
  const tokenPath = path.join(__dirname, 'token.json');
  const tokens = JSON.parse(fs.readFileSync(tokenPath));
  oAuth2Client.setCredentials(tokens);

  // 如果access token过期,会自动用refresh token刷新
  const gmail = google.gmail({ version: 'v1', auth: oAuth2Client });
  
  // 构造邮件内容(示例)
  const rawMessage = Buffer.from(
    'To: 收件人邮箱\n' +
    'Subject: 测试邮件\n\n' +
    '这是代用户发送的测试邮件'
  ).toString('base64').replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');

  try {
    await gmail.users.messages.send({
      userId: 'me',
      requestBody: { raw: rawMessage }
    });
    console.log('邮件发送成功');
  } catch (err) {
    console.error('邮件发送失败:', err);
  }
};

关键注意事项

  • Google Cloud Console配置:
    • 确保已创建对应平台(Android/iOS/Web)的OAuth 2.0客户端ID,重定向URI要匹配Expo的回调地址(Expo默认回调是exp://localhost:19000/--/expo-auth-session)。
    • 已启用Gmail API(在Google Cloud Console的API库中搜索启用)。
  • scope权限:根据需求选择合适的scope,比如gmail.send仅允许发送邮件,gmail.modify权限更大,按需申请。
  • refresh token获取:只有首次授权或用户重新授权时才会返回refresh token,配置prompt: 'consent'可以强制用户每次登录都确认授权,确保能拿到refresh token。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 07:45:29