jdehais / oauth2-azure
为 PHP League OAuth2-Client 提供的 Azure Active Directory OAuth 2.0 客户端提供程序
Requires
- php: ^7.1|^8.0
- ext-json: *
- ext-openssl: *
- firebase/php-jwt: ~3.0||~4.0||~5.0||~6.0
- league/oauth2-client: ~2.0
Requires (Dev)
- phpunit/phpunit: ^9.6
This package is not auto-updated.
Last update: 2024-09-19 18:29:30 UTC
README
此软件包为 PHP League 的 OAuth 2.0 客户端 提供了对 Azure Active Directory OAuth 2.0 的支持。
目录
- 安装
- 使用方法
- 制作 API 请求
- 资源所有者
- 更新 - Microsoft Graph
- 新增 - 保护您的 API - 实验性
- Azure Active Directory B2C - 实验性
- 多用途刷新令牌 - 实验性
- 已知用户
- 贡献
- 鸣谢
- 支持
- 许可
安装
要安装,请使用 composer
composer require thenetworg/oauth2-azure
使用方法
使用方法与 The League 的 OAuth 客户端相同,使用 \TheNetworg\OAuth2\Client\Provider\Azure
作为提供程序。
授权代码流
$provider = new TheNetworg\OAuth2\Client\Provider\Azure([ 'clientId' => '{azure-client-id}', 'clientSecret' => '{azure-client-secret}', 'redirectUri' => 'https://example.com/callback-url', //Optional using key pair instead of secret 'clientCertificatePrivateKey' => '{azure-client-certificate-private-key}', //Optional using key pair instead of secret 'clientCertificateThumbprint' => '{azure-client-certificate-thumbprint}', //Optional 'scopes' => ['openid'], //Optional 'defaultEndPointVersion' => '2.0' ]); // Set to use v2 API, skip the line or set the value to Azure::ENDPOINT_VERSION_1_0 if willing to use v1 API $provider->defaultEndPointVersion = TheNetworg\OAuth2\Client\Provider\Azure::ENDPOINT_VERSION_2_0; $baseGraphUri = $provider->getRootMicrosoftGraphUri(null); $provider->scope = 'openid profile email offline_access ' . $baseGraphUri . '/User.Read'; if (isset($_GET['code']) && isset($_SESSION['OAuth2.state']) && isset($_GET['state'])) { if ($_GET['state'] == $_SESSION['OAuth2.state']) { unset($_SESSION['OAuth2.state']); // Try to get an access token (using the authorization code grant) /** @var AccessToken $token */ $token = $provider->getAccessToken('authorization_code', [ 'scope' => $provider->scope, 'code' => $_GET['code'], ]); // Verify token // Save it to local server session data return $token->getToken(); } else { echo 'Invalid state'; return null; } } else { // // Check local server's session data for a token // // and verify if still valid // /** @var ?AccessToken $token */ // $token = // token cached in session data, null if not found; // // if (isset($token)) { // $me = $provider->get($provider->getRootMicrosoftGraphUri($token) . '/v1.0/me', $token); // $userEmail = $me['mail']; // // if ($token->hasExpired()) { // if (!is_null($token->getRefreshToken())) { // $token = $provider->getAccessToken('refresh_token', [ // 'scope' => $provider->scope, // 'refresh_token' => $token->getRefreshToken() // ]); // } else { // $token = null; // } // } //} // // If the token is not found in // if (!isset($token)) { $authorizationUrl = $provider->getAuthorizationUrl(['scope' => $provider->scope]); $_SESSION['OAuth2.state'] = $provider->getState(); header('Location: ' . $authorizationUrl); exit; // } return $token->getToken(); }
高级流
Azure Active Directory 的 授权代码授权流 与其他 Azure Active Directory 有所不同。您不是指定作用域,而是指定您想访问的资源 - 有一个参数 $provider->authWithResource
,它将自动将请求的 resource
参数填充为 $provider->resource
或 $provider->urlAPI
的值。此功能主要用于 Azure AD 的 v2.0 端点(更多信息请参阅 此处)。
使用自定义参数
使用 oauth2-client 版本 1.3.0 及更高版本,现在可以指定授权 URL 的自定义参数,因此您现在可以使用如 prompt
、login_hint
和类似选项。以下是一个获取将强制用户重新进行身份验证的授权 URL 的示例
$authUrl = $provider->getAuthorizationUrl([ 'prompt' => 'login' ]);
您可以在 此处 找到更多参数。
使用证书密钥对而不是共享密钥
- 例如,使用以下方式生成密钥对
openssl genrsa -out private.key 2048 openssl req -new -x509 -key private.key -out publickey.cer -days 365
- 将
publickey.cer
上传到 Azure 门户中的应用程序 - 注意显示的证书指纹(它看起来像
B4A94A83092455AC4D3AC827F02B61646EAAC43D
) - 将那个指纹放入
clientCertificateThumbprint
构造函数选项中 - 将
private.key
的内容放入clientCertificatePrivateKey
构造函数选项中 - 您可以省略
clientSecret
构造函数选项
注销
如果您需要快速生成用户的注销 URL,您可以这样做
// Assuming you have provider properly initialized. $post_logout_redirect_uri = 'https://www.msn.com'; // The logout destination after the user is logged out from their account. $logoutUrl = $provider->getLogoutUrl($post_logout_redirect_uri); header('Location: '.$logoutUrl); // Redirect the user to the generated URL
代表其他应用程序提供的令牌进行调用
// Use token provided by the other app // Make sure the other app mentioned this app in the scope when requesting the token $suppliedToken = ''; $provider = xxxxx;// Initialize provider // Call this to get claims // $claims = $provider->validateAccessToken($suppliedToken); /** @var AccessToken $token */ $token = $provider->getAccessToken('jwt_bearer', [ 'scope' => $provider->scope, 'assertion' => $suppliedToken, 'requested_token_use' => 'on_behalf_of', ]);
制作 API 请求
此库还提供了一个简单的界面,以便更容易与 Azure Graph API 和 Microsoft Graph 交互,以下方法可在 provider
对象上使用(它还处理在请求过程中可能需要的自动令牌刷新流)
get($ref, $accessToken, $headers = [])
post($ref, $body, $accessToken, $headers = [])
put($ref, $body, $accessToken, $headers = [])
delete($ref, $body, $accessToken, $headers = [])
patch($ref, $body, $accessToken, $headers = [])
getObjects($tenant, $ref, $accessToken, $headers = [])
用于列出大量数据,例如列出所有用户,它将自动跟随odata.nextLink
直至结束。- 由于
odata.nextLink
中不包含它,因此必须提供$tenant
租户。
- 由于
request($method, $ref, $accessToken, $options = [])
请参阅 #36 以了解用例。
请注意,如果您需要创建自定义请求,则仍可以使用 getAuthenticatedRequest 和 getResponse 方法。
变量
$ref
不包含前导/
的 URL 引用,例如myOrganization/groups
$body
请求的内容,内容必须是字符串(因此请确保使用json_encode
对请求进行编码)或流(请参阅 Guzzle HTTP)$accessToken
使用getAccessToken
方法获得的访问令牌对象$headers
可以为请求设置自定义头信息(请参阅 Guzzle HTTP)
资源所有者
从版本 1.1.0 开始,资源所有者信息将从通过 Azure Active Directory 传入的 access_token
中解析出来。它公开了一些属性和一个函数。
示例
$resourceOwner = $provider->getResourceOwner($token); echo 'Hello, '.$resourceOwner->getFirstName().'!';
公开的属性和函数包括
getId()
- 获取用户的对象 ID - 对每个用户都是唯一的getFirstName()
- 获取用户的首字母getLastName()
- 获取用户的姓氏getTenantId()
- 获取用户所属的租户 IDgetUpn()
- 获取用户的用户主体名称,它也可以用作用户的电子邮件地址claim($name)
- 从 JWT 获取任何其他声明(指定为$name
),完整列表可在 此处 找到
Microsoft Graph
使用此库调用 Microsoft Graph 非常简单。在提供程序初始化后,只需按以下方式更改 API URL(将 v1.0
替换为您想要的版本)
// Mention Microsoft Graph scope when initializing the provider $baseGraphUri = $provider->getRootMicrosoftGraphUri(null); $provider->scope = 'your scope ' . $baseGraphUri . '/User.Read'; // Call a query $provider->get($provider->getRootMicrosoftGraphUri($token) . '/v1.0/me', $token);
之后,在请求访问令牌、刷新令牌等时,请将 resource
的值提供为 https://graph.microsoft.com/
以能够调用 Graph(有关 resource
的更多信息,请参阅 此处)。
保护您的 API - 实验性
从版本 1.2.0 开始,您现在可以使用此库通过 Azure Active Directory 身份验证轻松地保护您的 API。提供程序现在还公开了 validateAccessToken(string $token)
,它允许您传递包含在 API 请求的 Authorization
标头中的访问令牌。您可以使用以下方式使用该函数(在纯 PHP 中)
// Assuming you have already initialized the $provider // Obtain the accessToken - in this case, we are getting it from Authorization header $headers = getallheaders(); // Assuming you got the value of Authorization header as "Bearer [the_access_token]" we parse it $authorization = explode(' ', $headers['Authorization']); $accessToken = $authorization[1]; try { $claims = $provider->validateAccessToken($accessToken); } catch (Exception $e) { // Something happened, handle the error } // The access token is valid, you can now proceed with your code. You can also access the $claims as defined in JWT - for example roles, group memberships etc.
您可能还需要从 API 访问其他资源,如 Microsoft Graph 以获取更多信息。为此,有 urn:ietf:params:oauth:grant-type:jwt-bearer
授权类型(请参阅 RFC)。以下是一个示例(假设上述代码正常工作,并且您在 Azure AD 应用程序中正确配置了所需的权限)
$graphAccessToken = $provider->getAccessToken('jwt_bearer', [ 'resource' => 'https://graph.microsoft.com/v1.0/', 'assertion' => $accessToken, 'requested_token_use' => 'on_behalf_of' ]); $me = $provider->get('https://graph.microsoft.com/v1.0/me', $graphAccessToken); print_r($me);
为了简化,您不需要记住整个 grant_type
名称(urn:ietf:params:oauth:grant-type:jwt-bearer
),您只需使用简短的 jwt_bearer
代替。
Azure Active Directory B2C - 实验性
您现在也可以非常简单地使用 Azure Active Directory B2C。在认证之前,使用 pathAuthorize
、pathToken
和 scope
修改端点,并另外指定您的 登录策略。请注意,B2C 支持目前仍处于实验性阶段,并未进行全面测试。
$provider->pathAuthorize = "/oauth2/v2.0/authorize"; $provider->pathToken = "/oauth2/v2.0/token"; $provider->scope = ["idtoken"]; // Specify custom policy in our authorization URL $authUrl = $provider->getAuthorizationUrl([ 'p' => 'b2c_1_siup' ]);
多用途刷新令牌 - 实验性
如果您需要访问多个资源(例如您的 API 和 Microsoft Graph),可以使用多功能的 刷新令牌。一旦为第一个资源获取了令牌,您可以简单地请求另一个不同资源的令牌,如下所示
$accessToken2 = $provider->getAccessToken('refresh_token', [ 'refresh_token' => $accessToken1->getRefreshToken(), 'resource' => 'http://urlOfYourSecondResource' ]);
目前存在一个问题:当您调用您的 API 并且令牌已过期时,它将具有 $provider->urlAPI
的值,这对于 $accessToken2
显然是不正确的。解决方案非常简单 - 将 $provider->urlAPI
设置为您要调用的资源。这个问题将在未来的版本中解决。请注意,这仍然是实验性的,并未进行全面测试。
已知用户
如果您正在使用此库并且希望在此列出,请告诉我们!
贡献
我们通过 GitHub 的拉取请求 接受贡献。
鸣谢
- Jan Hajek (TheNetw.org)
- Vittorio Bertocci (Microsoft)
- 感谢在实施 #16 时的出色支持
- Martin Cetkovský (cetkovsky.eu]
- 所有贡献者
支持
如果您发现错误或遇到任何问题,或与此库有任何问题/疑问,请创建一个 新问题。
许可
MIT 许可证 (MIT)。有关更多信息,请参阅 许可文件。