好的,我终于设法做到了,所以我将发布一些内容以希望对其他人有所帮助。
快速概览:
- 我已经在 Azure 门户中设置了我的应用程序,确保授予它对“密钥”部分下的 Office API 图形 API 和 Active Directory API 的访问权限,并确保单击“授予权限”按钮。
- 使用 Auth 端点 https://login.windows.net/#tenant#/oauth2/authorize 我可以正确授权应用程序,确保将“prompt=admin_consent”添加到 Auth URL 的末尾以获得管理员同意,而不是用户同意 - 此时我正在使用 client_id 和 client_secret 进行身份验证
- 我可以访问 Active Directory 端点以获取 Active Directory 中的用户列表。
我遇到的主要问题是我试图通过 Outlook API 访问用户的电子邮件,我可以正常阅读我的电子邮件,但尝试阅读其他人的电子邮件会导致 401 错误。
事实证明,正如预期的那样。如果您获得带有 client_secret 值(即密码)的身份验证令牌,则您只能在 Outlook API 中访问您自己的详细信息。在您尝试访问其他任何人的那一刻,您会收到拒绝访问错误。
解决这个问题的方法是创建一个 X.509 密钥并使用它来进行身份验证,而不是 client_secret。
但是关于如何在 PHP 中执行此操作的信息非常少,我就是这样做的:
好的,首先创建 X.509 证书。我在这里遵循了本指南:https://github.com/Azure/azure-iot-sdk-c/blob/master/tools/CACertificates/CACertificateOverview.md。
然后我需要弄清楚如何为您在获取身份验证令牌时传递的 client_assertion 参数创建 JWT 代码。
有一个出色的 PHP 库,名为 Firebase,其中包含一个 JWT 编码器 - https://github.com/firebase/php-jwt 可通过 composer 安装,非常容易安装。
然后我需要从 Azure SDK 破解一个类来管理证书,但首先我必须将 pem 证书转换为 pfx,我使用以下命令(来自与 cert_gen 相同的目录.sh 文件)
openssl pkcs12 -export -out certs/azure-iot-test-only.chain.pfx -inkey private/azure-iot-test-only.intermediate.key.pem -in certs/azure-iot-test-only.chain.ca.cert.pem -certfile certs/azure-iot-test-only.chain.ca.cert.pem
https://github.com/Azure/azure-sdk-for-php 是您需要的 SDK,文件是 AzureAdClientAsymmetricKey.php
因此,将所有这些整合到一些代码中 - 这不是为可运行而设计的,它已从我的系统中删除,但它应该有望为您指明正确的方向。
在我的应用程序中,我创建了两个身份验证令牌,一个用于 Outlook API,一个用于图形 API,因此您会看到两个不同的范围在使用中。
$result = [
'uri' => str_replace('#tenant#',$this->tenantId,'https://login.windows.net/#tenant#/oauth2/authorize'),
'params' => [
'response_type' => 'code',
'client_id' => $this->clientId, // the app client id
'grant_type' => 'client_credentials',
'scope' => $this->getScopeParam($scope),
],
];
$result['params']['tenant'] = $this->tenantId;
$result['params']['code'] = $this->azureAuthCode; // THe code returned from the admin authorisation
$pfxFileName = '/path/to/certs/azure-iot-test-only.chain.pfx';
$pfxPassword = '1234';
if ((!$cert_store = file_get_contents($pfxFileName)) ||
(!openssl_pkcs12_read($cert_store, $cert_info, $pfxPassword))) {
$this->logger->addError("Unable to read the cert file");
return $result;
}
$result['params']['resource'] = $scope == 'outlook' ? 'https://outlook.office.com' : 'https://graph.microsoft.com';
$credentials = new AdClientAsymmetricKey($this->clientId,$cert_info);
// We need to create the JWT for the authentication
$head = [];
$head['x5t'] = $credentials->getFingerprint();
$head['x5c'] = [ $credentials->getCertificate() ];
$token = [];
$token['aud'] = $result['uri'];
$token['sub'] = $credentials->getClientId();
$token['iss'] = $credentials->getClientId();
$token['nbf'] = (string)((new \DateTime("now", new \DateTimeZone('UTC')))->getTimestamp() - 60);
$token['exp'] = (string)((new \DateTime("now", new \DateTimeZone('UTC')))->getTimestamp() + 520);
$result['params']['client_assertion_type'] = 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer';
$result['params']['client_assertion'] = JWT::encode($token, $credentials->getPrivateKey(), 'RS256', null, $head);
return $result;
我遇到的最后一个问题是当我在成功获取令牌后尝试访问邮件时出现资源无效错误。事实证明,这非常简单而且很有帮助,没有记录在微软的文档中。你在上面的代码中看到,有一行...
$result['params']['resource'] = $scope == 'outlook' ? 'https://outlook.office.com' : 'https://graph.microsoft.com';
这是关键参数,因为它设置令牌可以访问的资源,$scope 被传递到上面的函数中,并且是 Outlook 或图形,以设置对相关 API 端点的请求。
无论如何,我希望这对某人有所帮助,我花了大约 8 个小时才弄清楚这一点!