OAuth 2.0

Ce document décrit OAuth 2.0, les cas d'utilisation, la manière d'obtenir des ID client et comment l'utiliser avec la bibliothèque cliente des API Google pour .NET.

Protocole OAuth 2.0

OAuth 2.0 est le protocole d'autorisation utilisé par les API Google. Vous devez vous familiariser avec le protocole en consultant les liens suivants:

Obtenir des ID client et des secrets

Vous pouvez obtenir des ID client et des secrets dans la console Google APIs. Il existe différents types d'ID client. Veillez donc à obtenir le type approprié pour votre application:

Dans chacun des extraits de code présentés (à l'exception de celui du compte de service), vous devez télécharger le secret client et le stocker sous forme de client_secrets.json dans votre projet.

Identifiants

Identifiants utilisateur

UserCredential est une classe d'assistance thread-safe permettant d'utiliser un jeton d'accès pour accéder aux ressources protégées. Un jeton d'accès expire généralement au bout d'une heure, après quoi vous obtiendrez une erreur si vous essayez de l'utiliser.

UserCredential et AuthorizationCodeFlow se chargent de "l'actualisation" automatique du jeton, ce qui signifie qu'ils permettent d'obtenir un nouveau jeton d'accès. Pour ce faire, vous devez utiliser un jeton d'actualisation de longue durée, que vous recevez avec le jeton d'accès si vous utilisez le paramètre access_type=offline lors du flux avec code d'autorisation.

Dans la plupart des applications, il est conseillé de stocker le jeton d'accès et le jeton d'actualisation des identifiants dans un stockage persistant. Sinon, vous devrez présenter à l'utilisateur final une page d'autorisation dans le navigateur toutes les heures, car le jeton d'accès expire une heure après l'avoir reçu.

Pour vous assurer que les jetons d'accès et d'actualisation persistent, vous pouvez fournir votre propre implémentation de IDataStore ou utiliser l'une des implémentations suivantes fournies par la bibliothèque:

  • FileDataStore pour .NET garantit que les identifiants seront persistants dans un fichier.

ServiceAccountCredential

ServiceAccountCredential est semblable à UserCredential, mais a une fonction différente. Google OAuth 2.0 est compatible avec les interactions entre serveurs, par exemple entre une application Web et Google Cloud Storage. L'application à l'origine de la requête doit prouver son identité pour accéder à une API. Un utilisateur final n'a pas besoin d'être impliqué. ServiceAccountCredential stocke une clé privée, qui est utilisée pour signer une requête afin d'obtenir un nouveau jeton d'accès.

UserCredential et ServiceAccountCredential implémentent IConfigurableHttpClientInitializer. Vous pouvez donc enregistrer chacun d'eux comme suit:

  • Un gestionnaire de réponse non réussi, il actualisera donc le jeton s'il reçoit un code d'état HTTP 401.
  • Un intercepteur, pour intercepter l'en-tête Authorization à chaque requête.

Applications installées

Exemple de code utilisant l'API Books:

using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;

using Google.Apis.Auth.OAuth2;
using Google.Apis.Books.v1;
using Google.Apis.Books.v1.Data;
using Google.Apis.Services;
using Google.Apis.Util.Store;

namespace Books.ListMyLibrary
{
    /// <summary>
    /// Sample which demonstrates how to use the Books API.
    /// https://developers.google.com/books/docs/v1/getting_started
    /// <summary>
    internal class Program
    {
        [STAThread]
        static void Main(string[] args)
        {
            Console.WriteLine("Books API Sample: List MyLibrary");
            Console.WriteLine("================================");
            try
            {
                new Program().Run().Wait();
            }
            catch (AggregateException ex)
            {
                foreach (var e in ex.InnerExceptions)
                {
                    Console.WriteLine("ERROR: " + e.Message);
                }
            }
            Console.WriteLine("Press any key to continue...");
            Console.ReadKey();
        }

        private async Task Run()
        {
            UserCredential credential;
            using (var stream = new FileStream("client_secrets.json", FileMode.Open, FileAccess.Read))
            {
                credential = await GoogleWebAuthorizationBroker.AuthorizeAsync(
                    GoogleClientSecrets.Load(stream).Secrets,
                    new[] { BooksService.Scope.Books },
                    "user", CancellationToken.None, new FileDataStore("Books.ListMyLibrary"));
            }

            // Create the service.
            var service = new BooksService(new BaseClientService.Initializer()
                {
                    HttpClientInitializer = credential,
                    ApplicationName = "Books API Sample",
                });

            var bookshelves = await service.Mylibrary.Bookshelves.List().ExecuteAsync();
            ...
        }
    }
}
  
  • Dans cet exemple de code, une nouvelle instance UserCredential est créée en appelant la méthode GoogleWebAuthorizationBroker.AuthorizeAsync. Cette méthode statique obtient les éléments suivants:

    • Le code secret du client (ou un flux vers le code secret du client).
    • Champs d'application obligatoires.
    • Identifiant de l'utilisateur.
    • Jeton d'annulation pour annuler une opération.
    • Un datastore facultatif. Si le datastore n'est pas spécifié, un FileDataStore avec un dossier Google.Apis.Auth par défaut est utilisé. Le dossier est créé dans Environment.SpecialFolder.ApplicationData.
  • L'UserCredential renvoyé par cette méthode est défini en tant que HttpClientInitializer sur le BooksService (à l'aide de l'initialiseur). Comme expliqué précédemment, UserCredential implémente un initialiseur de client HTTP.

  • Notez que dans l'exemple de code, les informations du secret client sont chargées à partir d'un fichier, mais vous pouvez également procéder comme suit:

    credential = await GoogleWebAuthorizationBroker.AuthorizeAsync(
        new ClientSecrets
        {
            ClientId = "PUT_CLIENT_ID_HERE",
            ClientSecret = "PUT_CLIENT_SECRETS_HERE"
        },
        new[] { BooksService.Scope.Books },
        "user",
        CancellationToken.None,
        new FileDataStore("Books.ListMyLibrary"));
          

Consultez notre exemple de livres.

Applications Web (ASP.NET Core 3)

Les API Google sont compatibles avec OAuth 2.0 pour les applications de serveur Web.

Google.Apis.Auth.AspNetCore3 est la bibliothèque recommandée pour la plupart des scénarios OAuth 2.0 basés sur Google dans les applications ASP.NET Core 3. Il implémente un gestionnaire d'authentification OpenIdConnect spécifique à Google. Il est compatible avec l'authentification incrémentielle et définit un IGoogleAuthProvider injectable pour fournir des identifiants Google pouvant être utilisés avec les API Google.

Cette section explique comment configurer et utiliser Google.Apis.Auth.AspNetCore3. Le code présenté ici est basé sur Google.Apis.Auth.AspNetCore3.IntegrationTests, une application ASP.NET Core 3 standard entièrement fonctionnelle.

Si vous souhaitez suivre cette documentation comme tutoriel, vous devez disposer de votre propre application ASP.NET Core 3 et suivre ces étapes en prérequis.

Prérequis

  • Installez le package Google.Apis.Auth.AspNetCore3.
  • Nous utilisons l'API Google Drive. Vous devrez donc également installer le package Google.Apis.Drive.v3.
  • Créez un projet Google Cloud si vous n'en avez pas encore. Pour ce faire, suivez ces instructions. Il s'agit du projet avec lequel votre application est identifiée.
  • Assurez-vous d'avoir activé l'API Google Drive. Pour activer les API, suivez ces instructions.
  • Créez des identifiants d'autorisation qui permettront à Google d'identifier votre application. Suivez ces instructions pour créer des identifiants d'autorisation et télécharger le fichier client_secrets.json. Voici deux points forts :
    • Notez que le type d'identifiants doit être Application Web.
    • Pour exécuter cette application, le seul URI de redirection que vous devez ajouter est https://localhost:5001/signin-oidc.

Configurer votre application pour utiliser Google.Apis.Auth.AspNetCore3

Google.Apis.Auth.AspNetCore3 est configuré dans la classe Startup ou dans une alternative similaire que vous pourriez utiliser. Les extraits suivants sont extraits de Startup.cs dans le projet Google.Apis.Auth.AspNetCore3.IntegrationTests.

  • Ajoutez la directive d'utilisation suivante à votre fichier Startup.cs.
    using Google.Apis.Auth.AspNetCore3;
  • Dans la méthode Startup.ConfigureServices, ajoutez le code suivant, en remplaçant les espaces réservés de l'ID client et du secret client par les valeurs contenues dans le fichier client_secrets.json. Vous pouvez charger ces valeurs directement à partir du fichier JSON ou les stocker de toute autre manière sécurisée. Consultez la méthode ClientInfo.Load du projet Google.Apis.Auth.AspNetCore3.IntegrationTests pour savoir comment charger ces valeurs directement à partir du fichier JSON.
    public void ConfigureServices(IServiceCollection services)
    {
        ...
    
        // This configures Google.Apis.Auth.AspNetCore3 for use in this app.
        services
            .AddAuthentication(o =>
            {
                // This forces challenge results to be handled by Google OpenID Handler, so there's no
                // need to add an AccountController that emits challenges for Login.
                o.DefaultChallengeScheme = GoogleOpenIdConnectDefaults.AuthenticationScheme;
                // This forces forbid results to be handled by Google OpenID Handler, which checks if
                // extra scopes are required and does automatic incremental auth.
                o.DefaultForbidScheme = GoogleOpenIdConnectDefaults.AuthenticationScheme;
                // Default scheme that will handle everything else.
                // Once a user is authenticated, the OAuth2 token info is stored in cookies.
                o.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
            })
            .AddCookie()
            .AddGoogleOpenIdConnect(options =>
            {
                options.ClientId = {YOUR_CLIENT_ID};
                options.ClientSecret = {YOUR_CLIENT_SECRET};
            });
    }
          
  • Dans la méthode Startup.Configure, veillez à ajouter les composants de middleware d'authentification et d'autorisation ASP.NET Core 3 au pipeline, ainsi que les redirections HTTPS:
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        ...
        app.UseHttpsRedirection();
        ...
    
        app.UseAuthentication();
        app.UseAuthorization();
    
        ...
    }
          

Utiliser les identifiants de l'utilisateur pour accéder aux API Google en son nom

Vous êtes maintenant prêt à ajouter des méthodes d'action à vos contrôleurs qui nécessitent les identifiants utilisateur pour accéder aux API Google en leur nom. L'extrait de code suivant montre comment lister les fichiers du compte Google Drive de l'utilisateur authentifié. Remarquez surtout deux choses:

  • L'utilisateur doit non seulement être authentifié, mais aussi avoir accordé le champ d'application https://www.googleapis.com/auth/drive.readonly à votre application, que vous spécifiez à l'aide de l'attribut GoogleScopedAuthorize.
  • Nous utilisons le mécanisme d'injection de dépendances standard d'ASP.NET Core 3 pour recevoir un IGoogleAuthProvider que nous utilisons pour obtenir les identifiants de l'utilisateur.

Le code:

  • Commencez par ajouter les directives d'utilisation suivantes à votre contrôleur.
    using Google.Apis.Auth.AspNetCore3;
    using Google.Apis.Auth.OAuth2;
    using Google.Apis.Drive.v3;
    using Google.Apis.Services;
          
  • Ajoutez l'action du contrôleur comme suit (et accompagnez-la d'une vue qui reçoit un modèle IList<string>):
    /// <summary>
    /// Lists the authenticated user's Google Drive files.
    /// Specifying the <see cref="GoogleScopedAuthorizeAttribute"> will guarantee that the code
    /// executes only if the user is authenticated and has granted the scope specified in the attribute
    /// to this application.
    /// </summary>
    /// <param name="auth">The Google authorization provider.
    /// This can also be injected on the controller constructor.</param>
    [GoogleScopedAuthorize(DriveService.ScopeConstants.DriveReadonly)]
    public async Task<IActionResult> DriveFileList([FromServices] IGoogleAuthProvider auth)
    {
        GoogleCredential cred = await auth.GetCredentialAsync();
        var service = new DriveService(new BaseClientService.Initializer
        {
            HttpClientInitializer = cred
        });
        var files = await service.Files.List().ExecuteAsync();
        var fileNames = files.Files.Select(x => x.Name).ToList();
        return View(fileNames);
    }
          

Et voilà les bases. Vous pouvez consulter HomeController.cs dans le projet Google.Apis.Auth.AspNetCore3.IntegrationTests pour découvrir comment:

  • Authentification des utilisateurs uniquement, sans champ d'application spécifique
  • Déconnexion des utilisateurs
  • Autorisation incrémentielle avec code Notez que l'exemple montre une autorisation incrémentielle avec des attributs.
  • Examiner les champs d'application accordés
  • Examiner les jetons d'accès et d'actualisation
  • Forcez l'actualisation du jeton d'accès. Notez que vous n'avez pas besoin de le faire vous-même, car Google.Apis.Auth.AspNetCore3 détecte si le jeton d'accès a expiré ou est sur le point de l'être, et l'actualise automatiquement.

Compte de service

Les API Google sont également compatibles avec les comptes de service. Contrairement au scénario dans lequel une application cliente demande l'accès aux données d'un utilisateur final, les comptes de service permettent d'accéder aux données de l'application cliente.

Votre application cliente signe la demande d'un jeton d'accès à l'aide d'une clé privée téléchargée depuis la console Google APIs. Après avoir créé un ID client, vous devez choisir un type d'application de compte de service, puis vous pouvez télécharger la clé privée. Consultez notre exemple de compte de service utilisant l'API Google Plus.

using System;
using System.Security.Cryptography.X509Certificates;

using Google.Apis.Auth.OAuth2;
using Google.Apis.Plus.v1;
using Google.Apis.Plus.v1.Data;
using Google.Apis.Services;

namespace Google.Apis.Samples.PlusServiceAccount
{
    /// <summary>
    /// This sample demonstrates the simplest use case for a Service Account service.
    /// The certificate needs to be downloaded from the Google API Console
    /// <see cref="https://console.cloud.google.com/">
    ///   "Create another client ID..." -> "Service Account" -> Download the certificate,
    ///   rename it as "key.p12" and add it to the project. Don't forget to change the Build action
    ///   to "Content" and the Copy to Output Directory to "Copy if newer".
    /// </summary>
    public class Program
    {
        // A known public activity.
        private static String ACTIVITY_ID = "z12gtjhq3qn2xxl2o224exwiqruvtda0i";

        public static void Main(string[] args)
        {
            Console.WriteLine("Plus API - Service Account");
            Console.WriteLine("==========================");

            String serviceAccountEmail = "SERVICE_ACCOUNT_EMAIL_HERE";

            var certificate = new X509Certificate2(@"key.p12", "notasecret", X509KeyStorageFlags.Exportable);

            ServiceAccountCredential credential = new ServiceAccountCredential(
               new ServiceAccountCredential.Initializer(serviceAccountEmail)
               {
                   Scopes = new[] { PlusService.Scope.PlusMe }
               }.FromCertificate(certificate));

            // Create the service.
            var service = new PlusService(new BaseClientService.Initializer()
            {
                HttpClientInitializer = credential,
                ApplicationName = "Plus API Sample",
            });

            Activity activity = service.Activities.Get(ACTIVITY_ID).Execute();
            Console.WriteLine("  Activity: " + activity.Object.Content);
            Console.WriteLine("  Video: " + activity.Object.Attachments[0].Url);

            Console.WriteLine("Press any key to continue...");
            Console.ReadKey();
        }
    }
}

Cet exemple crée un ServiceAccountCredential. Les portées requises sont définies et un appel à FromCertificate est effectué, ce qui charge la clé privée à partir de X509Certificate2 donné. Comme dans tous les autres exemples de code, les identifiants sont définis sur HttpClientInitializer.