使用Google API PHP Client复用用户登录时获取的JWT凭证调用MyBusinessAccountManagement API遇未授权错误的问题
我来帮你拆解这个问题的根源,以及给出可落地的解决办法:
问题出在哪?
你现在存在数据库里的google_token是ID Token(JWT格式),它的核心作用只是验证用户身份——简单说就是证明“这个用户确实是他自己”,但它完全没有被授权用来调用像MyBusinessAccountManagement这类需要特定权限的Google API。
而你在OAuth Playground拿到的是Access Token,这才是Google API认可的“访问通行证”,只有它能带着授权信息去调用受保护的API接口。这两种Token的用途、结构完全不同,直接把ID Token当Access Token用,肯定会触发unauthorized_client错误。
为什么会报这个错?
- ID Token的受众(
aud字段)是你的客户端ID,Google API的服务端只接受带有正确权限范围(scope)和授权信息的Access Token; - 你保存的ID Token根本不包含
business.manage这个权限的授权记录,所以哪怕你硬把它塞给客户端当Access Token,Google的API服务器也会直接拒绝。
正确的解决流程
你需要在用户首次登录时,不仅完成身份验证,还要获取到能访问MyBusiness API的Access Token和Refresh Token(用于离线刷新过期的Access Token),具体调整步骤如下:
1. 调整登录流程,获取授权凭证
你需要切换到标准的OAuth2授权码流程(因为One Tap默认只返回ID Token,无法直接获取带权限的Access Token),让用户主动授权你的应用访问其MyBusiness数据:
前端/登录入口调整
替换原来的One Tap代码,改为引导用户走OAuth2授权流程:
<!-- 生成授权链接,client_id和redirect_uri替换为你自己的配置 --> <a href="https://accounts.google.com/o/oauth2/v2/auth?client_id=YOUR_CLIENT_ID&redirect_uri=YOUR_REDIRECT_URI&response_type=code&scope=openid%20https://www.googleapis.com/auth/business.manage&access_type=offline&prompt=consent"> 登录并授权访问我的商家账户 </a>
后端回调/登录脚本调整
当用户授权后,Google会跳转到你配置的redirect_uri,并携带授权码code,后端用这个code交换Access Token和Refresh Token:
// 回调脚本,获取授权码 $code = $_GET['code']; $client = new \Google_Client(); $client->setAuthConfig(realpath('google.json')); // 必须和你在Google Console配置的redirect_uri完全一致 $client->setRedirectUri('YOUR_REDIRECT_URI'); // 请求必要的权限范围 $client->addScope('openid'); $client->addScope('https://www.googleapis.com/auth/business.manage'); // 启用离线访问,确保能拿到Refresh Token $client->setAccessType('offline'); // 强制用户确认授权,确保首次授权时能拿到Refresh Token $client->setPrompt('consent'); // 用授权码交换Token凭证 $tokenData = $client->fetchAccessTokenWithAuthCode($code); // 验证ID Token,获取用户唯一标识 $payload = $client->verifyIdToken($tokenData['id_token']); if ($payload) { $userId = $payload['sub']; // 从数据库获取用户 $user = getUser($userId); // 保存关键凭证到数据库 $user->google_id = $userId; $user->google_refresh_token = $tokenData['refresh_token']; // 核心:离线刷新用 $user->google_access_token = $tokenData['access_token']; // 临时访问凭证,会过期 $user->google_token_expires = time() + $tokenData['expires_in']; // 过期时间戳 // 保存用户数据 saveUser($user); }
2. 后续调用API时,用Refresh Token获取有效Access Token
当你需要调用MyBusiness API时,先检查当前的Access Token是否过期,如果过期就用Refresh Token刷新,再发起API请求:
// 获取当前登录用户 $user = getUser(); $client = new \Google_Client(); $client->setAuthConfig(realpath('google.json')); $client->addScope('https://www.googleapis.com/auth/business.manage'); // 配置Refresh Token $client->setRefreshToken($user->google_refresh_token); // 检查Access Token是否过期,过期则刷新 if (time() > $user->google_token_expires) { $newTokenData = $client->fetchAccessTokenWithRefreshToken($user->google_refresh_token); // 更新数据库里的凭证信息 $user->google_access_token = $newTokenData['access_token']; $user->google_token_expires = time() + $newTokenData['expires_in']; saveUser($user); // 给客户端设置新的Access Token $client->setAccessToken($newTokenData['access_token']); } else { // 直接使用未过期的Access Token $client->setAccessToken($user->google_access_token); } // 现在可以正常调用API了 $service = new \Google_Service_MyBusinessAccountManagement($client); $accounts = $service->accounts->listAccounts()->getAccounts();
额外注意事项
- 确保你的Google Cloud项目已经启用了MyBusiness Account Management API(在API库中搜索并启用);
- 确保你的OAuth同意屏幕里已经添加了
https://www.googleapis.com/auth/business.manage这个权限范围,如果是面向外部用户的应用,还需要提交Google审核; - 永远不要把ID Token当成Access Token使用,两者的设计目的完全不同:ID Token用于身份验证,Access Token用于API授权。
内容来源于stack exchange

