如何在React项目中使用带权限范围的新版Google API?
解决React中Google Fit API授权与数据获取问题
核心问题排查
你遇到的加scope登录失败、无法获取Fit权限的问题,大多和Google Cloud控制台配置、授权库的正确使用方式有关,以下是分步解决方案:
第一步:先确认Google Cloud控制台的关键配置
这些是所有授权流程的基础,没配置对再怎么写代码都没用:
- 启用Google Fit API:在Cloud控制台的API库中搜索并启用「Google Fit API」
- 配置OAuth同意屏幕:
- 切换到「测试」或「生产」模式,添加测试用户(你的Google邮箱)
- 在「已授权的范围」中添加所需的Fit权限,比如:
https://www.googleapis.com/auth/fitness.activity.read(读取活动数据)profile、email(获取基础用户信息)
- 验证重定向URI:在OAuth客户端ID的设置中,确保
http://localhost:3000已添加到「已授权的重定向URI」列表
方案一:用@react-oauth/google正确获取授权(推荐)
你之前的代码加scope报错,大概率是控制台没配置对应scope,或者ux_mode的处理问题。以下是修正后的完整代码:
方式1:Popup模式(无需页面跳转)
import { useGoogleLogin } from '@react-oauth/google'; import { useState } from 'react'; const GoogleFitLogin = () => { const [fitData, setFitData] = useState(null); const signIn = useGoogleLogin({ clientId: process.env.REACT_APP_GOOGLE_CLIENT_ID, ux_mode: 'popup', scope: 'profile email https://www.googleapis.com/auth/fitness.activity.read', access_type: 'offline', // 可选,需要刷新token时添加 onSuccess: async (response) => { console.log('获取到的AccessToken:', response.access_token); // 调用Google Fit API获取活动数据 try { const res = await fetch('https://www.googleapis.com/fitness/v1/users/me/dataset:aggregate', { method: 'POST', headers: { 'Authorization': `Bearer ${response.access_token}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ aggregateBy: [{ dataTypeName: 'com.google.activity.segment' }], bucketByTime: { durationMillis: 86400000 }, // 按天聚合 startTimeMillis: Date.now() - 7 * 86400000, // 最近7天 endTimeMillis: Date.now(), }), }); const data = await res.json(); setFitData(data); } catch (err) { console.error('调用Fit API失败:', err); } }, onError: (err) => console.error('登录授权失败:', err), }); return ( <div> <button onClick={signIn}>登录并授权Google Fit</button> {fitData && <pre>{JSON.stringify(fitData, null, 2)}</pre>} </div> ); }; export default GoogleFitLogin;
方式2:Redirect模式(需处理回调)
如果必须用redirect模式,需要在重定向后的页面处理授权码:
// App.js 主页面 import { useGoogleOAuth } from '@react-oauth/google'; import { useEffect, useState } from 'react'; function App() { const { code } = useGoogleOAuth(); const [tokens, setTokens] = useState(null); useEffect(() => { if (code) { // 推荐通过后端交换token,避免暴露Client Secret fetch('http://localhost:5000/api/google/token', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ code }), }) .then(res => res.json()) .then(data => setTokens(data.tokens)) .catch(err => console.error('交换token失败:', err)); } }, [code]); return ( <div> {!tokens ? ( <a href={`https://accounts.google.com/o/oauth2/v2/auth?client_id=${process.env.REACT_APP_GOOGLE_CLIENT_ID}&redirect_uri=http://localhost:3000&response_type=code&scope=profile%20email%20https://www.googleapis.com/auth/fitness.activity.read`}> 登录并授权Google Fit </a> ) : ( <div> <p>已获取授权,Token: {tokens.access_token.slice(0, 20)}...</p> {/* 这里可以调用Fit API */} </div> )} </div> ); } export default App;
方案二:用Google官方OAuth2库直接实现
你之前用的google.accounts.id是身份认证(获取ID Token),要获取授权scope需用google.accounts.oauth2:
import { useEffect, useState } from 'react'; const GoogleFitAuth = () => { const [fitData, setFitData] = useState(null); useEffect(() => { // 加载Google OAuth2脚本 const script = document.createElement('script'); script.src = 'https://accounts.google.com/gsi/client'; script.onload = () => { /* global google */ const tokenClient = google.accounts.oauth2.initTokenClient({ client_id: process.env.REACT_APP_GOOGLE_CLIENT_ID, scope: 'profile email https://www.googleapis.com/auth/fitness.activity.read', callback: async (response) => { if (response.error) { console.error('授权失败:', response.error); return; } // 调用Fit API try { const res = await fetch('https://www.googleapis.com/fitness/v1/users/me/dataset:aggregate', { method: 'POST', headers: { 'Authorization': `Bearer ${response.access_token}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ aggregateBy: [{ dataTypeName: 'com.google.activity.segment' }], bucketByTime: { durationMillis: 86400000 }, startTimeMillis: Date.now() - 7 * 86400000, endTimeMillis: Date.now(), }), }); const data = await res.json(); setFitData(data); } catch (err) { console.error('调用Fit API失败:', err); } }, }); // 绑定按钮点击事件 document.querySelector('#fit-auth-btn').addEventListener('click', () => { tokenClient.requestAccessToken({ prompt: 'consent' }); // 强制弹出授权窗口 }); }; document.body.appendChild(script); return () => document.body.removeChild(script); }, []); return ( <div> <button id="fit-auth-btn">授权Google Fit获取数据</button> {fitData && <pre>{JSON.stringify(fitData, null, 2)}</pre>} </div> ); }; export default GoogleFitAuth;
方案三:后端处理Token交换(生产环境必备)
生产环境不能在前端暴露Client Secret,用Node.js+Express+googleapis实现后端:
// server.js const express = require('express'); const { google } = require('googleapis'); const cors = require('cors'); require('dotenv').config(); const app = express(); app.use(cors()); app.use(express.json()); const oauth2Client = new google.auth.OAuth2( process.env.GOOGLE_CLIENT_ID, process.env.GOOGLE_CLIENT_SECRET, 'http://localhost:3000' ); // 交换授权码获取Token app.post('/api/google/token', async (req, res) => { const { code } = req.body; try { const { tokens } = await oauth2Client.getToken(code); oauth2Client.setCredentials(tokens); // 调用Google Fit API const fitness = google.fitness({ version: 'v1', auth: oauth2Client }); const fitResponse = await fitness.users.dataset.aggregate({ userId: 'me', requestBody: { aggregateBy: [{ dataTypeName: 'com.google.activity.segment' }], bucketByTime: { durationMillis: 86400000 }, startTimeMillis: Date.now() - 7 * 86400000, endTimeMillis: Date.now(), }, }); res.json({ tokens, fitData: fitResponse.data }); } catch (err) { res.status(500).json({ error: err.message }); } }); app.listen(5000, () => console.log('后端服务运行在 http://localhost:5000'));
关键注意事项
- 测试阶段必须在OAuth同意屏幕添加自己的邮箱作为测试用户,否则会提示「应用未验证」
- Google Fit API的部分接口需要特定的请求格式(比如
dataset:aggregate必须用POST),要严格参考官方文档的参数要求 - 若遇到「invalid_scope」错误,检查控制台的OAuth同意屏幕是否已添加对应scope,且scope拼写完全正确
内容的提问来源于stack exchange,提问作者nurlantig
相关产品推荐
相关产品推荐

