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

PHP使用OAuth授权Azure应用访问Business Central API无返回问题

问题根因

你的代码存在多个逻辑和接口使用错误,所以拿不到授权码、返回NULL:

  • 混淆了OAuth2授权码流程的两个核心步骤:授权码(code)是用户完成登录授权后Azure通过重定向回传的参数,/authorize是供浏览器访问的交互端点,你用后端curl直接发POST请求根本无法触发用户授权交互,不可能拿到有效返回。
  • 端点和参数错配:你往授权端点/authorize发POST时携带了仅用于令牌端点的grant_type=authorization_code、client_secret参数,完全不符合Azure AD的接口规范。
  • 权限范围配置错误:你要访问Business Central API,却配置了Microsoft Graph的scope,即使拿到令牌也没有对应API的访问权限。
  • 缺失必填参数:所有OAuth2请求都必须携带和Azure应用注册后台配置完全一致的redirect_uri(重定向地址),你的代码里完全没加这个参数。
  • 缺少错误排查逻辑:代码没有捕获curl执行错误,当请求失败时curl_exec会返回false,json_decode后自然得到NULL,无法定位具体错误。
修正方案

前置配置

先在Azure应用注册页面完成基础配置:

  • 添加Web类型的重定向URI,地址填写你PHP应用的回调地址(例如https://你的应用域名/callback.php),后续所有请求里的redirect_uri必须和这个值完全一致。
  • 在API权限板块添加Business Central对应的委托权限,完成管理员授权(组织内部应用必须操作这一步)。

第一步:跳转用户到授权页拿授权码

这一步不需要用curl发POST,直接返回302跳转让用户浏览器访问Azure授权页即可,示例代码(login.php):

session_start();
$tenant = "你的Azure租户ID/域名";
$clientId = "你的应用客户端ID";
$redirectUri = urlencode("https://你的应用域名/callback.php");
$scope = urlencode("https://api.businesscentral.dynamics.com/.default offline_access");
// 生成随机state做CSRF校验,存入session
$state = bin2hex(random_bytes(5));
$_SESSION['oauth_state'] = $state;

$authUrl = "https://login.microsoftonline.com/{$tenant}/oauth2/v2.0/authorize?client_id={$clientId}&response_type=code&redirect_uri={$redirectUri}&response_mode=query&scope={$scope}&state={$state}";
header("Location: {$authUrl}");
exit;

用户完成登录授权后,Azure会自动重定向到你配置的callback.php,URL的query参数中会携带code,也就是你需要的授权码。

第二步:在回调页用授权码换访问令牌

在callback.php中先做安全校验,再用curl请求令牌端点换取可调用API的access_token,示例代码:

session_start();
// 校验state防CSRF
if (!isset($_GET['state']) || $_GET['state'] !== $_SESSION['oauth_state']) {
    die("非法请求:state校验不通过");
}
// 捕获授权阶段返回的错误
if (isset($_GET['error'])) {
    die("授权失败:" . $_GET['error_description']);
}
$authCode = $_GET['code']; // 这里就是获取到的授权码

$tenant = "你的Azure租户ID/域名";
$clientId = "你的应用客户端ID";
$clientSecret = "你的应用客户端密钥";
$redirectUri = "https://你的应用域名/callback.php";

$tokenEndpoint = "https://login.microsoftonline.com/{$tenant}/oauth2/v2.0/token";
$ch = curl_init($tokenEndpoint);
curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => ["Content-Type: application/x-www-form-urlencoded"],
    CURLOPT_POSTFIELDS => http_build_query([
        'grant_type' => 'authorization_code',
        'client_id' => $clientId,
        'client_secret' => $clientSecret,
        'code' => $authCode,
        'redirect_uri' => $redirectUri,
        'scope' => 'https://api.businesscentral.dynamics.com/.default offline_access'
    ])
]);
$response = curl_exec($ch);
// 捕获curl层错误
if (curl_errno($ch)) {
    die("请求失败:" . curl_error($ch));
}
curl_close($ch);
$tokenRes = json_decode($response, true);
// 捕获令牌接口返回的业务错误
if (isset($tokenRes['error'])) {
    die("换取令牌失败:" . $tokenRes['error_description']);
}
// 从返回结果中取access_token,即可携带该令牌调用Business Central API
$accessToken = $tokenRes['access_token'];
注意事项
  • 废弃老的login.windows.net端点,统一使用login.microsoftonline.com的v2.0版本OAuth端点,兼容性和稳定性更好。
  • 如果是无用户交互的后台服务场景,不需要走授权码流程,直接使用客户端凭据流(grant_type=client_credentials)请求令牌端点即可拿到应用权限的访问令牌。
  • 所有URL传参必须做urlencode处理,避免特殊字符导致请求解析失败。
  • 所有HTTP请求必须增加错误捕获逻辑,禁止直接对返回值做json_decode,否则请求出错时只会得到NULL,无法排查问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 06:00:59