基于React和Node.js:从Google JWT获取Access与Refresh令牌的问题
解决MERN应用中通过新版Google Identity Services获取API访问令牌的方案
首先明确:你当前使用@react-oauth/google的GoogleLogin默认返回的是ID Token(即你拿到的JWT),这个令牌仅用于验证用户身份,不包含访问Google Calendar等API所需的Access/Refresh令牌。要获取API授权令牌,需要切换到授权码流程,以下是具体实现步骤:
一、前端调整:获取授权码而非ID Token
修改GoogleLogin组件的配置,指定responseType="code"和所需的API权限scope,同时开启offline模式以获取Refresh Token:
<GoogleOAuthProvider clientId="MY_CLIENT_ID"> <GoogleLogin onSuccess={this.onSuccess} onFailure={this.onFailure} responseType="code" scope="https://www.googleapis.com/auth/calendar" // 按需添加API权限,多个用空格分隔 accessType="offline" // 必须添加此参数才能获取长期有效的Refresh Token /> </GoogleOAuthProvider>
此时onSuccess回调返回的对象会包含授权码:
{ code: "AUTHORIZATION_CODE_STRING", clientId: "MY_CLIENT_ID", select_by: "btn" }
将这个code发送到你的Node.js后端即可。
二、后端处理:用授权码兑换Access/Refresh令牌
使用谷歌官方的google-auth-library包完成令牌兑换,步骤如下:
- 安装依赖:
npm install google-auth-library
- 后端兑换令牌的代码示例:
const { OAuth2Client } = require('google-auth-library'); const User = require('./models/User'); // 你的用户模型 // 初始化OAuth2客户端 const oAuth2Client = new OAuth2Client( process.env.GOOGLE_CLIENT_ID, process.env.GOOGLE_CLIENT_SECRET, 'postmessage' // SPA场景下固定填这个,需和Google Cloud控制台配置一致 ); // 处理前端发送的授权码 async function exchangeCodeForTokens(req, res) { const { code } = req.body; try { // 兑换授权码获取令牌 const { tokens } = await oAuth2Client.getToken(code); /* tokens结构示例: { access_token: "ACCESS_TOKEN_STRING", refresh_token: "REFRESH_TOKEN_STRING", expires_in: 3600, // Access Token有效期1小时 token_type: "Bearer" } */ // 解码ID Token获取用户邮箱(可选,用于关联用户) const ticket = await oAuth2Client.verifyIdToken({ idToken: tokens.id_token, audience: process.env.GOOGLE_CLIENT_ID, }); const userEmail = ticket.getPayload().email; // 将令牌与用户关联后持久化存储(比如存入MongoDB) await User.findOneAndUpdate( { email: userEmail }, { googleTokens: tokens }, { upsert: true, new: true } ); res.status(200).json({ message: '令牌存储成功' }); } catch (error) { console.error('兑换令牌失败:', error); res.status(500).json({ error: '令牌兑换失败' }); } }
三、令牌使用与持久化注意事项
- Access Token:有效期1小时,过期后需用Refresh Token刷新,无需用户重新授权。
- Refresh Token:长期有效(除非用户撤销授权或令牌被吊销),需加密存储(比如用AES加密后存入数据库),禁止明文存储。
- 后续访问Google API时,若Access Token已过期,可调用以下方法刷新:
async function refreshAccessToken(refreshToken) { oAuth2Client.setCredentials({ refresh_token: refreshToken }); const { credentials } = await oAuth2Client.refreshAccessToken(); // 更新数据库中的令牌 await User.findOneAndUpdate(..., { googleTokens: credentials }); return credentials.access_token; }
四、为什么不用你当前拿到的JWT?
你当前获取的JWT是ID Token,解码后仅包含用户基本信息(邮箱、姓名等),不具备访问Google API的权限。ID Token的作用是验证用户身份,而非授权API访问,因此必须走授权码流程获取专用的Access/Refresh令牌。
内容的提问来源于stack exchange,提问作者gianlucazani
相关产品推荐
相关产品推荐

