如何在NextAuth v4会话期间为Google Provider新增Scope调用Google Fit API?
为已登录用户扩展NextAuth Google Provider的Google Fit API权限范围
1. 配置Google Fit所需的权限范围(Scope)
首先在Google Provider的授权配置中添加Google Fit API对应的Scope,可选择以下两种方式之一:
方式一:在自定义授权URL中追加Scope
修改你的GOOGLE_AUTHORIZATION_URL定义,加入Scope参数(根据业务需求选择具体权限):
const GOOGLE_AUTHORIZATION_URL = 'https://accounts.google.com/o/oauth2/v2/auth?' + new URLSearchParams({ prompt: 'consent', access_type: 'offline', response_type: 'code', // 组合基础权限与Google Fit权限 scope: [ 'openid', 'email', 'profile', 'https://www.googleapis.com/auth/fitness.activity.read', 'https://www.googleapis.com/auth/fitness.body.read' ].join(' ') })
方式二:使用GoogleProvider自带的scope属性
若不想自定义完整授权URL,可直接在Provider配置中指定scope:
GoogleProvider({ clientId: process.env.GOOGLE_CLIENT_ID, clientSecret: process.env.GOOGLE_CLIENT_SECRET, authorization: { url: 'https://accounts.google.com/o/oauth2/v2/auth', params: { prompt: 'consent', access_type: 'offline', response_type: 'code' } }, // 直接声明所需权限范围 scope: 'openid email profile https://www.googleapis.com/auth/fitness.activity.read https://www.googleapis.com/auth/fitness.body.read' }),
2. 引导已登录用户重新授权
已登录用户此前未授权新增的Google Fit权限,需触发重新登录流程以获取包含新Scope的令牌。在前端添加授权按钮:
import { signIn, useSession } from 'next-auth/react' function FitAuthButton() { const { data: session } = useSession() if (!session) return null return ( <button onClick={() => { signIn('google', { callbackUrl: '/', authorizationParams: { prompt: 'consent', // 强制弹出权限确认窗口 } }) }} > 授权Google Fit数据访问 </button> ) } export default FitAuthButton
用户点击按钮后会跳转至Google授权页,确认新增权限后,新生成的Access Token将包含Google Fit的访问权限。
3. 修复Refresh Token逻辑的潜在问题
当前refreshAccessToken函数中使用了refreshedTokens.expires_at,但Google OAuth端点返回的是expires_in(有效期秒数),需修正过期时间计算逻辑:
async function refreshAccessToken(token) { try { const url = "https://oauth2.googleapis.com/token?" + new URLSearchParams({ client_id: process.env.GOOGLE_CLIENT_ID, client_secret: process.env.GOOGLE_CLIENT_SECRET, grant_type: "refresh_token", refresh_token: token.refreshToken, }) const response = await fetch(url, { headers: { "Content-Type": "application/x-www-form-urlencoded", }, method: "POST", }) const refreshedTokens = await response.json() if (!response.ok) { throw refreshedTokens } return { ...token, accessToken: refreshedTokens.access_token, // 修复:用expires_in计算过期时间戳 accessTokenExpires: Date.now() + refreshedTokens.expires_in * 1000, refreshToken: refreshedTokens.refresh_token ?? token.refreshToken, } } catch (error) { console.log(error) return { ...token, error: "RefreshAccessTokenError", } } }
验证权限有效性
授权完成后,可通过调用Google Fit API验证权限:
async function fetchFitActivityData(accessToken) { const response = await fetch('https://www.googleapis.com/fitness/v1/users/me/dataset:aggregate', { method: 'POST', headers: { 'Authorization': `Bearer ${accessToken}`, '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 response.json() console.log('Fit活动数据:', data) }
内容的提问来源于stack exchange,提问作者docksdocks
相关产品推荐
相关产品推荐

