Partager via


Ajouter l’authentification utilisateur aux applications PHP pour Microsoft Graph

Dans cet article, vous ajoutez l’authentification utilisateur à l’application que vous avez créée dans Générer des applications PHP avec Microsoft Graph. Vous utilisez ensuite l’API utilisateur Microsoft Graph pour obtenir l’utilisateur authentifié.

Ajouter l’authentification utilisateur

Le Kit de développement logiciel (SDK) Microsoft Graph inclut des fournisseurs d’authentification basés sur le client OAuth2 de la ligue PHP. Toutefois, pour ce didacticiel, vous utilisez le flux de code de l’appareil pour obtenir des jetons d’accès. Les fournisseurs d’authentification inclus n’implémentent pas ce flux. Vous implémentez donc un fournisseur de jetons d’accès personnalisé.

Créer un fournisseur de jetons d’accès

Créez un fichier dans le répertoire racine de votre projet nommé DeviceCodeTokenProvider.php. Ajoutez le code suivant.

<?php
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.

use GuzzleHttp\Client;
use Http\Promise\FulfilledPromise;
use Http\Promise\Promise;
use Http\Promise\RejectedPromise;
use Microsoft\Kiota\Abstractions\Authentication\AccessTokenProvider;
use Microsoft\Kiota\Abstractions\Authentication\AllowedHostsValidator;

class DeviceCodeTokenProvider implements AccessTokenProvider {

    private string $clientId;
    private string $tenantId;
    private string $scopes;
    private AllowedHostsValidator $allowedHostsValidator;
    private string $accessToken;
    private Client $tokenClient;

    public function __construct(string $clientId, string $tenantId, string $scopes) {
        $this->clientId = $clientId;
        $this->tenantId = $tenantId;
        $this->scopes = $scopes;
        $this->allowedHostsValidator = new AllowedHostsValidator();
        $this->allowedHostsValidator->setAllowedHosts([
            "graph.microsoft.com",
            "graph.microsoft.us",
            "dod-graph.microsoft.us",
            "graph.microsoft.de",
            "microsoftgraph.chinacloudapi.cn"
        ]);
        $this->tokenClient = new Client();
    }

    public function getAuthorizationTokenAsync(string $url, array $additionalAuthenticationContext = []): Promise {
        $parsedUrl = parse_url($url);
        $scheme = $parsedUrl["scheme"] ?? null;

        if ($scheme !== 'https' || !$this->getAllowedHostsValidator()->isUrlHostValid($url)) {
            return new FulfilledPromise(null);
        }

        // If we already have a user token, just return it
        // Tokens are valid for one hour, after that it needs to be refreshed
        if (isset($this->accessToken)) {
            return new FulfilledPromise($this->accessToken);
        }

        // https://learn.microsoft.com/azure/active-directory/develop/v2-oauth2-device-code
        $deviceCodeRequestUrl = 'https://login.microsoftonline.com/'.$this->tenantId.'/oauth2/v2.0/devicecode';
        $tokenRequestUrl = 'https://login.microsoftonline.com/'.$this->tenantId.'/oauth2/v2.0/token';

        // First POST to /devicecode
        $deviceCodeResponse = json_decode($this->tokenClient->post($deviceCodeRequestUrl, [
            'form_params' => [
                'client_id' => $this->clientId,
                'scope' => $this->scopes
            ]
        ])->getBody()->getContents());

        // Display the user prompt
        print($deviceCodeResponse->message.PHP_EOL);

        // Response also indicates how often to poll for completion
        // And gives a device code to send in the polling requests
        $interval = (int)$deviceCodeResponse->interval;
        $device_code = $deviceCodeResponse->device_code;

        // Do polling - if attempt times out the token endpoint
        // returns an error
        while (true) {
            sleep($interval);

            // POST to the /token endpoint
            $tokenResponse = $this->tokenClient->post($tokenRequestUrl, [
                'form_params' => [
                    'client_id' => $this->clientId,
                    'grant_type' => 'urn:ietf:params:oauth:grant-type:device_code',
                    'device_code' => $device_code
                ],
                // These options are needed to enable getting
                // the response body from a 4xx response
                'http_errors' => false,
                'curl' => [
                    CURLOPT_FAILONERROR => false
                ]
            ]);

            if ($tokenResponse->getStatusCode() == 200) {
                // Return the access_token
                $responseBody = json_decode($tokenResponse->getBody()->getContents());
                $this->accessToken = $responseBody->access_token;
                return new FulfilledPromise($responseBody->access_token);
            } else if ($tokenResponse->getStatusCode() == 400) {
                // Check the error in the response body
                $responseBody = json_decode($tokenResponse->getBody()->getContents());
                if (isset($responseBody->error)) {
                    $error = $responseBody->error;
                    // authorization_pending means we should keep polling
                    if (strcmp($error, 'authorization_pending') != 0) {
                        return new RejectedPromise(
                            new Exception('Token endpoint returned '.$error, 100));
                    }
                }
            }
        }
    }

    public function getAllowedHostsValidator(): AllowedHostsValidator {
        return $this->allowedHostsValidator;
    }
}
?>

Configurer le client Graph pour l’authentification utilisateur

Utilisez maintenant la DeviceCodeTokenProvider classe pour demander un jeton d’accès à l’aide du flux de code de l’appareil.

  1. Créez un fichier dans le répertoire racine de votre projet nommé GraphHelper.php. Ajoutez le code suivant.

    <?php
    class GraphHelper {
    }
    ?>
    
  2. Ajoutez les instructions suivantes using dans les balises PHP.

    use Microsoft\Graph\Generated\Models;
    use Microsoft\Graph\Generated\Users\Item\MailFolders\Item\Messages\MessagesRequestBuilderGetQueryParameters;
    use Microsoft\Graph\Generated\Users\Item\MailFolders\Item\Messages\MessagesRequestBuilderGetRequestConfiguration;
    use Microsoft\Graph\Generated\Users\Item\SendMail\SendMailPostRequestBody;
    use Microsoft\Graph\Generated\Users\Item\UserItemRequestBuilderGetQueryParameters;
    use Microsoft\Graph\Generated\Users\Item\UserItemRequestBuilderGetRequestConfiguration;
    use Microsoft\Graph\GraphRequestAdapter;
    use Microsoft\Graph\GraphServiceClient;
    use Microsoft\Kiota\Abstractions\Authentication\BaseBearerTokenAuthenticationProvider;
    
    require_once 'DeviceCodeTokenProvider.php';
    
  3. Ajoutez ce code à la classe GraphHelper.

    private static string $clientId = '';
    private static string $tenantId = '';
    private static string $graphUserScopes = '';
    private static DeviceCodeTokenProvider $tokenProvider;
    private static GraphServiceClient $userClient;
    
    public static function initializeGraphForUserAuth(): void {
        GraphHelper::$clientId = $_ENV['CLIENT_ID'];
        GraphHelper::$tenantId = $_ENV['TENANT_ID'];
        GraphHelper::$graphUserScopes = $_ENV['GRAPH_USER_SCOPES'];
    
        GraphHelper::$tokenProvider = new DeviceCodeTokenProvider(
            GraphHelper::$clientId,
            GraphHelper::$tenantId,
            GraphHelper::$graphUserScopes);
        $authProvider = new BaseBearerTokenAuthenticationProvider(GraphHelper::$tokenProvider);
        $adapter = new GraphRequestAdapter($authProvider);
        GraphHelper::$userClient = GraphServiceClient::createWithRequestAdapter($adapter);
    }
    
  4. Remplacez la fonction vide initializeGraph dans main.php par ce qui suit.

    function initializeGraph(): void {
        GraphHelper::initializeGraphForUserAuth();
    }
    

Ce code charge des informations à partir du fichier .env et initialise deux propriétés, un DeviceCodeTokenProvider objet et un GraphServiceClient objet. L’objet DeviceCodeTokenProvider est utilisé pour demander un jeton d’accès, et l’objet GraphServiceClient est utilisé pour effectuer des appels à Microsoft Graph.

Tester le flux de code de l’appareil

Ensuite, ajoutez du code pour obtenir un jeton d’accès à partir de .GraphHelper

  1. Ajoutez la fonction suivante à la classe GraphHelper.

    public static function getUserToken(): string {
        return GraphHelper::$tokenProvider
            ->getAuthorizationTokenAsync('https://graph.microsoft.com')->wait();
    }
    
  2. Remplacez la fonction vide displayAccessToken dans main.php par ce qui suit.

    function displayAccessToken(): void {
        try {
            $token = GraphHelper::getUserToken();
            print('User token: '.$token.PHP_EOL.PHP_EOL);
        } catch (Exception $e) {
            print('Error getting access token: '.$e->getMessage().PHP_EOL.PHP_EOL);
        }
    }
    
  3. Générez et exécutez l’application. Entrez 1 lorsque vous êtes invité à entrer une option. L’application affiche une URL et un code d’appareil.

    $ php main.php
    
    PHP Graph Tutorial
    
    Please choose one of the following options:
    0. Exit
    1. Display access token
    2. List my inbox
    3. Send mail
    4. Make a Graph call
    1
    To sign in, use a web browser to open the page https://microsoft.com/devicelogin and
    enter the code RB2RUD56D to authenticate.
    
  4. Ouvrez un navigateur et accédez à l’URL affichée. Entrez le code fourni et connectez-vous.

    Importante

    Gardez à l’esprit tous les comptes Microsoft 365 existants qui sont connectés à votre navigateur lorsque vous accédez à https://microsoft.com/devicelogin. Utilisez les fonctionnalités du navigateur telles que les profils, le mode invité ou le mode privé pour vous assurer que vous vous authentifiez en tant que compte que vous envisagez d’utiliser pour les tests.

  5. Une fois terminé, revenez à l’application pour voir le jeton d’accès.

    Conseil

    À des fins de validation et de débogage uniquement, vous pouvez décoder les jetons d’accès utilisateur (pour les comptes professionnels ou scolaires uniquement) à l’aide de l’analyseur de jetons en ligne de Microsoft à l’adresse https://jwt.ms. L’analyse de votre jeton peut être utile si vous rencontrez des erreurs de jeton lors de l’appel de Microsoft Graph. Par exemple, vérifier que la revendication dans le scp jeton contient les étendues d’autorisation Microsoft Graph attendues.

Obtenir un utilisateur

Maintenant que l’authentification est configurée, vous pouvez effectuer votre premier appel microsoft API Graph. Ajoutez du code pour obtenir le nom et l’adresse e-mail de l’utilisateur authentifié.

  1. Ajoutez ce code à la classe GraphHelper.

    public static function getUser(): Models\User {
        $configuration = new UserItemRequestBuilderGetRequestConfiguration();
        $configuration->queryParameters = new UserItemRequestBuilderGetQueryParameters();
        $configuration->queryParameters->select = ['displayName','mail','userPrincipalName'];
        return GraphHelper::$userClient->me()->get($configuration)->wait();
    }
    
  2. Remplacez la fonction vide greetUser dans main.php par ce qui suit.

    function greetUser(): void {
        try {
            $user = GraphHelper::getUser();
            print('Hello, '.$user->getDisplayName().'!'.PHP_EOL);
    
            // For Work/school accounts, email is in Mail property
            // Personal accounts, email is in UserPrincipalName
            $email = $user->getMail();
            if (empty($email)) {
                $email = $user->getUserPrincipalName();
            }
            print('Email: '.$email.PHP_EOL.PHP_EOL);
        } catch (Exception $e) {
            print('Error getting user: '.$e->getMessage().PHP_EOL.PHP_EOL);
        }
    }
    

Si vous exécutez l’application maintenant, une fois que vous vous êtes connecté, l’application vous accueille par son nom.

Hello, Megan Bowen!
Email: MeganB@contoso.com

Explication du code

Considérez le code dans la getUser fonction . Il ne s’agit que de quelques lignes, mais il y a quelques détails clés à noter.

Accès à « moi »

La fonction génère une requête à l’API Obtenir l’utilisateur . Cette API est accessible de deux façons :

GET /me
GET /users/{user-id}

Dans ce cas, le code appelle le point de terminaison d’API GET /me . Ce point de terminaison est une méthode de raccourci permettant d’obtenir l’utilisateur authentifié sans connaître son ID d’utilisateur.

Remarque

Étant donné que le GET /me point de terminaison d’API obtient l’utilisateur authentifié, il est disponible uniquement pour les applications qui utilisent l’authentification utilisateur. Les applications d’authentification d’application uniquement ne peuvent pas accéder à ce point de terminaison.

Demande de propriétés spécifiques

La fonction utilise le paramètre de requête $select pour spécifier le jeu de propriétés dont elle a besoin.

Type de retour fortement typé

La fonction retourne un User objet désérialisé à partir de la réponse JSON de l’API. Étant donné que le code utilise $select, seules les propriétés demandées ont des valeurs dans l’objet retourné User . Toutes les autres propriétés ont des valeurs par défaut.

Étape suivante