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

如何通过Client Credentials流查询租户日历?解决受众无效问题

解决Microsoft Graph API "Invalid Audience" 错误与客户端凭证流访问租户所有用户日历

首先,你的问题核心出在获取访问令牌时指定的Resource参数错误,加上一些不必要的配置步骤,导致令牌的受众(Audience)与Microsoft Graph API不匹配,进而引发权限验证失败。下面一步步帮你解决:

1. 修正访问令牌的Resource参数

你当前获取令牌时指定的resource是https://graph.windows.net,这是旧版的Azure AD Graph API的资源标识符,而你要调用的是新版的Microsoft Graph API,正确的Resource应该是https://graph.microsoft.com。

这个参数直接决定了令牌的受众(aud字段),如果令牌的aud是https://graph.windows.net,调用https://graph.microsoft.com的API自然会提示"Invalid Audience"。

修正后的PHP代码如下:

$provider = new \TheNetworg\OAuth2\Client\Provider\Azure([
    'clientId' => env('OAUTH_APP_ID'),
    'clientSecret' => env('OAUTH_APP_PASSWORD'),
    'tenant' => env('OAUTH_TENANT_ID'), // 这里填你的Directory (tenant) ID
]);

// 客户端凭证流不需要redirectUri,所以可以去掉这个参数
$token = $provider->getAccessToken('client_credentials', [
    'resource' => 'https://graph.microsoft.com', // 重点修正这里
]);

// 设置API基础地址为Microsoft Graph v1.0
$provider->urlAPI = "https://graph.microsoft.com/v1.0/";
$provider->resource = "https://graph.microsoft.com/";

return $token;

获取到的令牌解码后(可以用任意JWT解码工具查看),aud字段应该是https://graph.microsoft.com,这就匹配了API的受众要求。

2. 移除不必要的"暴露API"配置

你步骤4中"暴露API"并设置Application ID URI的操作是多余的——这一步是用于让其他应用调用你的应用提供的API,而不是让你的应用调用Microsoft Graph API。完全可以删除这部分配置,避免混淆。

3. 确认应用权限已获得管理员同意

你已经给应用授予了https://graph.microsoft.com/Calendars.ReadWrite的应用权限,但要注意:应用权限必须由租户管理员明确授予同意才能生效。你提到"无需管理员同意即可查询"是错误的,只有委派权限(用户上下文)才可能不需要管理员同意,而客户端凭证流使用的是应用权限,必须完成管理员同意步骤:

  • 回到Azure AD门户的应用注册页面,进入"API权限"
  • 点击"授予管理员同意"按钮,完成权限激活

4. 正确调用API访问所有用户日历

客户端凭证流没有用户上下文,所以不能使用/me/calendars(这个端点需要当前登录用户的上下文)。要访问租户所有用户的日历,需要分两步:

步骤A:获取租户内所有用户列表

调用GET /users端点获取所有用户的ID或用户主体名称(UPN):

$users = $provider->get('users', $token);
$userList = $users->getBody()->getContents();
// 解析JSON得到用户列表
$usersArray = json_decode($userList, true);

步骤B:遍历用户,获取每个用户的日历

对每个用户,调用GET /users/{user-id}/calendars或GET /users/{user-upn}/calendars端点:

$allCalendars = [];
foreach ($usersArray['value'] as $user) {
    $userId = $user['id'];
    $calendarsResponse = $provider->get("users/$userId/calendars", $token);
    $calendars = json_decode($calendarsResponse->getBody()->getContents(), true);
    $allCalendars[$userId] = $calendars['value'];
}

// 输出所有用户的日历
echo json_encode($allCalendars);

批量请求优化(可选)

如果用户数量较多,逐个请求效率较低,可以使用Microsoft Graph的批量请求,一次性发送多个请求:

$batchRequest = [
    'requests' => []
];

foreach ($usersArray['value'] as $index => $user) {
    $batchRequest['requests'][] = [
        'id' => (string)($index + 1),
        'method' => 'GET',
        'url' => "/users/{$user['id']}/calendars"
    ];
}

$batchResponse = $provider->post('batch', $token, ['json' => $batchRequest]);
$batchResult = json_decode($batchResponse->getBody()->getContents(), true);

5. 修改日历的示例

要修改某个用户的日历,调用PATCH /users/{user-id}/calendars/{calendar-id}端点:

$userId = '用户ID';
$calendarId = '日历ID';
$updateData = [
    'name' => '更新后的日历名称'
];

$updateResponse = $provider->patch("users/$userId/calendars/$calendarId", $token, ['json' => $updateData]);
// 成功返回204 No Content
echo $updateResponse->getStatusCode();

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.13 09:06:47