thenetworg/oauth2-azure

The PHP League OAuth2-Client 的 Azure Active Directory OAuth 2.0 客户端提供程序

v2.2.2 2023-12-19 12:10 UTC

README

Latest Version Total Downloads Software License

此包为 PHP League 的 Azure Active Directory OAuth 2.0 提供支持。

目录

安装

要安装,请使用 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 中略有不同。您指定要访问的资源而不是作用域 - 有一个参数 $provider->authWithResource,它将自动用 $provider->resource$provider->urlAPI 的值填充请求的 resource 参数。此功能主要用于 Azure AD 的 2.0 端点(更多详情请见 此处)。

使用自定义参数

使用版本 1.3.0 及更高版本的 oauth2-client,现在可以指定授权 URL 的自定义参数,因此您现在可以像使用 promptlogin_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 APIMicrosoft 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的JWT解析。它公开了一些属性和一个函数。

示例

$resourceOwner = $provider->getResourceOwner($token);
echo 'Hello, '.$resourceOwner->getFirstName().'!';

公开的属性和函数包括

  • getId() - 获取用户的对象id - 每个用户都是唯一的
  • getFirstName() - 获取用户的名字
  • getLastName() - 获取用户的姓氏
  • getTenantId() - 获取用户所属租户的id
  • getUpn() - 获取用户的用户主体名称,也可以用作用户的电子邮件地址
  • 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);

之后,在请求访问令牌、刷新令牌等时,提供值https://graph.microsoft.com/resource,以便能够对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.
// If you're instead using a persisted access token you got from $provider->getAccessToken,
// you'll have to feed its id token to validateAccessToken like so: $provider->validateAccessToken($accessTokenn->getIdToken());
$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_typeurn:ietf:params:oauth:grant-type:jwt-bearer)名称,您只需使用简短的jwt_bearer即可。

Azure Active Directory B2C - 实验性

您现在可以非常简单地使用Azure Active Directory B2C。在身份验证之前,使用pathAuthorizepathTokenscope更改端点,并另外指定您的登录策略。**请注意,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上的Pull Requests的贡献。

鸣谢

支持

如果您在此库中发现错误或遇到任何问题,或对此库有任何问题/疑问,请创建一个新问题

许可证

MIT许可证(MIT)。有关更多信息,请参阅许可证文件