PAYATHON 2026

Xero OAuth 2.0 报错 unauthorized_client:Invalid scope

排障赵师傅

结论

accounting.budgets.read 和 accounting.reports.tenninetynine.read 的名称均正确,属于标准 OAuth 2.0 应用可以申请的 scope,并非仅向特定合作伙伴开放的 restricted scope。申请这两个权限无需加入 Xero partner program,也不存在需要开发者自行调用的 scope 解锁接口。

如果出现以下错误:

unauthorized_client : Invalid scope for client

通常是因为 Xero 授权服务器未能为当前 client_id 正确识别这些 scope。如果其他 scope 可以正常使用,问题更可能出在该应用的服务端配置或权限状态,而非 Authorization Code flow。Xero 对这一问题的公开回复是联系 Developer Platform Support,并提供应用的 client_id 以便检查。相关问题及 Xero 回复

先确认 scope 拼写和传参格式

两个 scope 的正确写法如下:

accounting.budgets.read
accounting.reports.tenninetynine.read

根据 Xero 官方 SDK 文档,/Budgets 和 /Budgets/{BudgetID} 使用 accounting.budgets.read,/Reports/TenNinetyNine 所需权限则包括 accounting.reports.tenninetynine.read。Xero Accounting API SDK 文档

scope 参数应使用空格分隔,不能使用逗号:

openid profile email offline_access accounting.budgets.read accounting.reports.tenninetynine.read

构造 URL 时,交给 URL 编码库处理空格和特殊字符。例如:

const scopes = [
  'openid',
  'profile',
  'email',
  'offline_access',
  'accounting.budgets.read',
  'accounting.reports.tenninetynine.read',
];

const params = new URLSearchParams({
  response_type: 'code',
  client_id: process.env.XERO_CLIENT_ID,
  redirect_uri: 'https://example.com/oauth/callback',
  scope: scopes.join(' '),
  state: 'RANDOM_CSRF_VALUE',
});

const authorizationUrl =
  `https://login.xero.com/identity/connect/authorize?${params.toString()}`;

console.log(authorizationUrl);

编码后,scope 中的空格可能显示为 + 或 %20,这两种形式都正常。

建议的排查顺序

先分别测试两个 scope,避免一次加入全部权限。首先测试:

openid profile email offline_access accounting.budgets.read

再单独测试:

openid profile email offline_access accounting.reports.tenninetynine.read

如果两个 scope 都触发相同错误,继续检查:

  1. Authorization URL 中的 client_id 是否属于当前检查的 Xero 应用。
  2. 是否混用了开发、测试或生产环境的应用凭据。
  3. redirect_uri 是否与 Developer Console 中登记的地址完全一致。
  4. scope 中是否混入隐藏换行、逗号、重复引号或尾随标点。
  5. OAuth 库是否自动追加了其他无效 scope。
  6. 应用创建时间是否受到 Xero granular scopes 迁移规则影响。

Xero 已在 2026 年调整 Accounting API 的 scope 模型,新旧应用可用的 broad scopes 和 granular scopes 有所不同。具体应以当前的 OAuth 2.0 scopes 页面及应用创建时间对应的规则为准。新权限模型仍将 accounting.budgets.read 作为独立 scope,旧的 accounting.reports.read 则在逐步由更细分的报表权限取代。Xero granular scopes FAQ

Developer Console 中是否需要开启权限

目前没有公开资料显示这两个 scope 需要申请权限提升或加入合作伙伴计划,也没有公开的 API endpoint 能够绕过授权服务器,强制为应用开启 scope。

如果 Developer Console 中没有相应开关,或 scope 字符串正确却在进入 consent screen 前被拒绝,修改 token 请求或伪造权限无法解决问题。此时授权服务器已在第一阶段拒绝 scope,token exchange 还没有发生。

应通过 Xero Developer Platform Support 提交工单,并提供:

  • 应用的 client_id
  • 错误发生的大致时间和时区
  • 已脱敏的完整 Authorization URL
  • 分别测试两个 scope 的结果
  • 错误页面截图或响应内容
  • 应用创建时间及应用类型
  • 响应中如有 request ID、correlation ID 或 trace ID,也应一并提供

不要提交 client_secret、authorization code、access token 或 refresh token。

修复后需要重新授权

scope 状态修复后,需要重新启动 Authorization Code flow。修改 Authorization URL 不会让已有 access token 自动获得新增权限,用户必须重新进入 consent screen 完成授权。

成功换取 token 后,先检查 token 响应中的 scope 字段,确认目标权限已经获授,再调用对应端点。OAuth 授权成功仅表示应用取得了访问许可。/Reports/TenNinetyNine 能否返回有效数据,还可能取决于所连接组织的国家、产品功能和数据情况。

确认请求格式无误后,应联系 Xero Support 检查当前 client_id 的服务端 scope 配置,无需申请特殊合作伙伴资格。

备注:内容仅供参考。