Expo(React Native)应用无用户登录调用Google Drive API求助
在Expo(React Native)应用中无需用户登录调用Google Drive API的问题
我是Expo(React Native)开发新手,正在开发一款私有应用,需要访问Google Drive中特定共享文件夹内的文件。已配置具有编辑权限的Google服务账号,并将目标文件夹共享给该账号,当前使用Expo开发构建(非Expo Go)。
希望无需用户登录即可调用Google Drive API,尝试使用google-auth-library编写认证代码,但在浏览器测试时始终报错:"Uncaucht TypeError: prototype may only be an Object or null: undefined",移除相关代码后无报错。Expo官方推荐的@react-native-google-signin/google-signin仅适用于用户登录场景,不符合需求。
当前代码如下:
import Constants from 'expo-constants'; import { GoogleAuth } from 'google-auth-library'; import axios from 'axios'; // 从app.json提取服务账号信息 const { GOOGLE_PROJECT_ID, GOOGLE_CLIENT_EMAIL, GOOGLE_PRIVATE_KEY, GOOGLE_PRIVATE_KEY_ID, GOOGLE_CLIENT_ID, } = Constants.expoConfig?.extra || {}; // 修复可能转义的私钥换行符 const privateKeyFormatted = GOOGLE_PRIVATE_KEY.replace(/\\n/g, '\n'); /** * 认证并获取Google Drive访问令牌 * @returns {Promise<string>} 用于API调用的访问令牌 */ export const authenticateGoogleDrive = async (): Promise<string> => { try { if (!GOOGLE_CLIENT_EMAIL || !GOOGLE_PRIVATE_KEY || !GOOGLE_PROJECT_ID) { throw new Error('Les informations du compte de service sont manquantes'); } console.log("Before GoogleAuth"); console.log("GOOGLE_CLIENT_EMAIL: ", GOOGLE_CLIENT_EMAIL); console.log("privateKeyFormatted: ", privateKeyFormatted); console.log("GOOGLE_PROJECT_ID: ", GOOGLE_PROJECT_ID); // 创建认证对象 const auth = new GoogleAuth({ credentials: { type: 'service_account', project_id: GOOGLE_PROJECT_ID, private_key: privateKeyFormatted, client_email: GOOGLE_CLIENT_EMAIL, }, scopes: ['https://www.googleapis.com/auth/drive'], }); const client = await auth.getClient(); const token = await client.getAccessToken(); if (!token.token) { throw new Error('Impossible d’obtenir le jeton d’accès'); } return token.token; } catch (error) { console.error('Erreur d’authentification Google:', error); throw error; } };
问题原因
google-auth-library是为Node.js环境设计的,依赖大量Node.js核心模块(如crypto、fs),无法直接适配React Native/Expo的移动端或浏览器环境,这就是出现原型错误的根本原因。
解决方法:手动生成JWT令牌兑换访问令牌
绕过google-auth-library,基于服务账号信息直接生成JWT令牌,再兑换成Google Drive API的访问令牌,步骤如下:
- 安装适配依赖
使用jsonwebtoken生成JWT,或更适配Expo的expo-jwt:
# 可选:使用jsonwebtoken npx expo install jsonwebtoken axios # 或使用expo-jwt(推荐) npx expo install expo-jwt axios
- 替换认证代码
以下是使用jsonwebtoken的实现(若用expo-jwt,可参考官方文档调整签名逻辑):
import Constants from 'expo-constants'; import jwt from 'jsonwebtoken'; import axios from 'axios'; const { GOOGLE_PROJECT_ID, GOOGLE_CLIENT_EMAIL, GOOGLE_PRIVATE_KEY, GOOGLE_PRIVATE_KEY_ID, } = Constants.expoConfig?.extra || {}; const privateKeyFormatted = GOOGLE_PRIVATE_KEY?.replace(/\\n/g, '\n'); export const authenticateGoogleDrive = async (): Promise<string> => { if (!GOOGLE_CLIENT_EMAIL || !privateKeyFormatted || !GOOGLE_PROJECT_ID) { throw new Error('服务账号信息不完整'); } // 1. 构建JWT payload const now = Math.floor(Date.now() / 1000); const payload = { iss: GOOGLE_CLIENT_EMAIL, scope: 'https://www.googleapis.com/auth/drive', aud: 'https://oauth2.googleapis.com/token', exp: now + 3600, // 令牌有效期1小时 iat: now, }; // 2. 签名生成JWT const jwtToken = jwt.sign(payload, privateKeyFormatted, { algorithm: 'RS256', keyid: GOOGLE_PRIVATE_KEY_ID, }); // 3. 兑换成访问令牌 const response = await axios.post('https://oauth2.googleapis.com/token', { grant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer', assertion: jwtToken, }); if (!response.data.access_token) { throw new Error('无法获取访问令牌'); } return response.data.access_token; };
- 调用Google Drive API示例
拿到令牌后即可调用API,比如列出目标文件夹内的文件:
export const listDriveFolderFiles = async (folderId: string) => { const accessToken = await authenticateGoogleDrive(); const response = await axios.get('https://www.googleapis.com/drive/v3/files', { headers: { Authorization: `Bearer ${accessToken}` }, params: { q: `'${folderId}' in parents`, fields: 'files(id,name,mimeType)', }, }); return response.data.files; };
注意事项
- 确保服务账号已被添加到目标Google Drive文件夹的共享列表,权限至少为“查看者”(如需编辑则设为“编辑者”)。
- 切勿将服务账号私钥硬编码在代码中,始终通过
app.json的extra字段配置,并将该文件加入.gitignore避免泄露。 - 访问令牌有效期为1小时,建议在调用API前检查令牌是否过期,必要时重新生成。
内容的提问来源于stack exchange,提问作者William Atzel
相关产品推荐
相关产品推荐

