You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

刷新Xero API令牌遇unsupported_grant_type错误及offline_access使用咨询

Xero OAuth2 offline_access 与刷新Token问题解决

用户问题

我不清楚如何使用offline_access,能否提供使用示例?另外,我该从哪里获取新的Refresh token?我已为所有用户添加了offline_access权限,想了解如何通过它生成新的Refresh token。我尝试了两种方法:

方法一:

$provider = new \League\OAuth2\Client\Provider\GenericProvider([
'clientId' => $client,
'clientSecret' => $secret,
'redirectUri' => 'https://site/xeroreports',
'urlAuthorize' => 'https://login.xero.com/identity/connect/authorize',
'urlAccessToken' => 'https://identity.xero.com/connect/token',
'urlResourceOwnerDetails' => 'https://identity.xero.com/resources'
]);

$newAccessToken = $provider->getAccessToken('refresh_token', [
'refresh_token' => $xero_refresh
]);

但仅获取到access token。

方法二:

$client = new \GuzzleHttp\Client();

$response1 = $client->request('POST', 'https://identity.xero.com/connect/token', [
'body' => '{"grant_type":"refresh_token", "refresh_token":"'. $xero_refresh .'"}',
'headers' => [
'Authorization' => 'Basic ' . $authpas,
'Content-Type' => "application/x-www-form-urlencoded",
],
]); 
echo $newRefreshToken = $response1->getBody();

此时出现错误:

Type: GuzzleHttp\Exception\ClientException Code: 400 Message: Client error: `POST https://identity.xero.com/connect/token` resulted in a `400 Bad Request` response: {error: unsupported_grant_type}

问题解决与说明

一、offline_access权限的正确使用

offline_access不是在用户授权后添加,而是必须在首次授权请求的scope参数中包含,这样Xero才会返回有效的Refresh Token。示例代码(授权跳转环节):

$authorizationUrl = $provider->getAuthorizationUrl([
    'scope' => ['openid', 'profile', 'email', 'accounting.transactions', 'offline_access'] // 必须包含offline_access
]);
// 引导用户跳转到$authorizationUrl完成授权

只有首次授权(或用户重新授权)时,Xero会返回初始Refresh Token;后续刷新操作中,服务端会返回新的Refresh Token用于下一次刷新。

二、两种方法的错误修正

方法一:提取隐藏的Refresh Token

你用League\OAuth2\Client获取的$newAccessToken对象里其实包含Refresh Token,只是没提取出来。修改代码:

$newAccessToken = $provider->getAccessToken('refresh_token', [
    'refresh_token' => $xero_refresh
]);

// 提取新的Refresh Token
$newRefreshToken = $newAccessToken->getRefreshToken();
// 同时获取新的Access Token
$newAccessTokenValue = $newAccessToken->getToken();

方法二:修复请求格式错误

错误原因是Content-Type设为application/x-www-form-urlencoded,但请求体用了JSON格式,导致服务端无法识别grant_type。改成表单格式即可:

$client = new \GuzzleHttp\Client();

$response1 = $client->request('POST', 'https://identity.xero.com/connect/token', [
    'form_params' => [ // 用form_params自动处理表单编码
        'grant_type' => 'refresh_token',
        'refresh_token' => $xero_refresh
    ],
    'headers' => [
        'Authorization' => 'Basic ' . $authpas,
        'Content-Type' => 'application/x-www-form-urlencoded'
    ]
]); 

// 解析响应体提取Token
$responseData = json_decode($response1->getBody(), true);
$newRefreshToken = $responseData['refresh_token'];
$newAccessToken = $responseData['access_token'];

如果坚持用body参数,需要把内容转为URL编码字符串:

'body' => http_build_query([
    'grant_type' => 'refresh_token',
    'refresh_token' => $xero_refresh
]),

三、新Refresh Token的获取渠道

  1. 首次授权:用户完成授权跳转后,调用getAccessToken('authorization_code')获取的Token对象中包含初始Refresh Token。
  2. 刷新操作:每次用有效的旧Refresh Token调用刷新接口,响应里会返回新的Refresh Token,需要替换存储的旧Token,避免过期失效。

内容的提问来源于stack exchange,提问作者Banyman

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 22:57:26