OAuth 2.0

Questo documento descrive OAuth 2.0, quando utilizzarlo, come acquisire gli ID client e come utilizzarlo con la libreria client dell'API di Google per .NET.

Protocollo OAuth 2.0

OAuth 2.0 è il protocollo di autorizzazione utilizzato dalle API di Google. Ti consigliamo di acquisire familiarità con il protocollo leggendo i seguenti link:

Acquisisci ID client e secret

Puoi ottenere ID client e secret nella console API di Google. Esistono diversi tipi di ID client, quindi assicurati di ottenere il tipo corretto per la tua applicazione:

In ogni snippet di codice mostrato (tranne quello per l'account di servizio), devi scaricare il segreto cliente e memorizzarlo come client_secrets.json nel progetto.

Credenziali

Credenziali utente

UserCredential è una classe di assistenza sicura per l'utilizzo di un token di accesso per accedere alle risorse protette. In genere, un token di accesso scade dopo 1 ora, dopodiché verrà visualizzato un errore se provi a utilizzarlo.

UserCredential e AuthorizationCodeFlow si occupano di "aggiornare" automaticamente il token, ovvero di ottenere un nuovo token di accesso. Questo viene fatto utilizzando un token di aggiornamento di lunga durata, che ricevi insieme al token di accesso se utilizzi il parammetro access_type=offline durante il flusso del codice di autorizzazione.

Nella maggior parte delle applicazioni, è consigliabile memorizzare il token di accesso e il token di aggiornamento della credenziale nella memoria permanente. In caso contrario, dovrai presentare all'utente finale una pagina di autorizzazione nel browser ogni ora, perché il token di accesso scade un'ora dopo averlo ricevuto.

Per assicurarti che i token di accesso e aggiornamento rimangano invariati, puoi fornire la tua implementazione di IDataStore, oppure puoi utilizzare una delle seguenti implementazioni fornite dalla libreria:

  • FileDataStore per .NET garantisce che la credenziale sia persistente in un file.

ServiceAccountCredential

ServiceAccountCredential è simile a UserCredential, ma ha uno scopo diverso. OAuth 2.0 di Google supporta le interazioni server-to-server, ad esempio quelle tra un'applicazione web e Google Cloud Storage. L'applicazione richiedente deve dimostrare la propria identità per ottenere l'accesso a un'API e non è necessario coinvolgere un utente finale. ServiceAccountCredential memorizza una chiave privata, che viene utilizzata per firmare una richiesta per ottenere un nuovo token di accesso.

Sia UserCredential che ServiceAccountCredential implementano IConfigurableHttpClientInitializer quindi puoi registrarli come:

  • Un gestore delle risposte non andate a buon fine, quindi aggiornerà il token se riceve un codice di stato HTTP 401.
  • Un intercettatore per intercettare l'intestazione Authorization in ogni richiesta.

Applicazioni installate

Codice di esempio che utilizza l'API Libri:

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();
            ...
        }
    }
}
  
  • In questo codice di esempio viene creata una nuova istanza UserCredential chiamando il metodo GoogleWebAuthorizationBroker.AuthorizeAsync. Questo metodo statico riceve quanto segue:

    • Il client secret (o uno stream al client secret).
    • Gli ambiti richiesti.
    • L'identificatore utente.
    • Il token di annullamento per annullare un'operazione.
    • Un archivio dati facoltativo. Se il datastore non è specificato, il valore predefinito è un FileDataStore con una cartella Google.Apis.Auth predefinita. La cartella viene creata in Environment.SpecialFolder.ApplicationData.
  • Il UserCredential restituito da questo metodo viene impostato come HttpClientInitializer su BooksService (utilizzando l'inizializzatore). Come spiegato in precedenza, UserCredential implementa un inizializzatore del client HTTP.

  • Tieni presente che nel codice di esempio le informazioni del segreto client vengono caricate da un file, ma puoi anche eseguire le seguenti operazioni:

    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"));
          

Dai un'occhiata al nostro esempio di libri.

Applicazioni web (ASP.NET Core 3)

Le API di Google supportano OAuth 2.0 per applicazioni server web.

Google.Apis.Auth.AspNetCore3 è la libreria consigliata per la maggior parte degli scenari OAuth 2.0 basati su Google nelle applicazioni ASP.NET Core 3. Implementa un gestore di autenticazione OpenIdConnect specifico per Google. Supporta l'autenticazione incrementale e definisce un IGoogleAuthProvider iniettabile per fornire le credenziali di Google che possono essere utilizzate con le API di Google.

Questa sezione descrive come configurare e utilizzare Google.Apis.Auth.AspNetCore3. Il codice mostrato qui si basa su Google.Apis.Auth.AspNetCore3.IntegrationTests, un'applicazione ASP.NET Core 3 standard completamente funzionante.

Se vuoi seguire questa documentazione come tutorial, devi avere una tua applicazione ASP.NET Core 3 e completare questi passaggi come prerequisito.

Prerequisiti

  • Installa il pacchetto Google.Apis.Auth.AspNetCore3.
  • Utilizziamo l'API Google Drive, quindi dovrai anche installare il pacchetto Google.Apis.Drive.v3.
  • Se non ne hai già uno, crea un progetto Google Cloud. Segui queste istruzioni per farlo. Questo sarà il progetto con cui viene identificata la tua app.
  • Assicurati di attivare l'API Google Drive. Per abilitare le API, segui queste istruzioni.
  • Crea le credenziali di autorizzazione che identificheranno la tua app per Google. Segui queste istruzioni per creare le credenziali di autorizzazione e scaricare il client_secrets.json file. Due punti salienti:
    • Tieni presente che il tipo di credenziali deve essere Applicazione web.
    • Per eseguire questa app, l'unico URI di reindirizzamento da aggiungere è https://localhost:5001/signin-oidc.

Configura l'applicazione in modo che utilizzi Google.Apis.Auth.AspNetCore3

Google.Apis.Auth.AspNetCore3 è configurato nella classe Startup o in un'alternativa simile che potresti utilizzare. I seguenti snippet sono estratti da Startup.cs nel progetto Google.Apis.Auth.AspNetCore3.IntegrationTests.

  • Aggiungi la seguente istruzione using al file Startup.cs.
    using Google.Apis.Auth.AspNetCore3;
  • Nel metodo Startup.ConfigureServices, aggiungi il seguente codice, sostituendo i segnaposto ID cliente e Segreto cliente con i valori contenuti nel file client_secrets.json. Puoi caricare questi valori direttamente dal file JSON o archiviarli in qualsiasi altro modo sicuro. Dai un'occhiata al metodo ClientInfo.Load nel progetto Google.Apis.Auth.AspNetCore3.IntegrationTests per un esempio su come caricare questi valori direttamente dal file 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};
            });
    }
          
  • Nel metodo Startup.Configure, assicurati di aggiungere alla pipeline i componenti middleware di autenticazione e autorizzazione di ASP.NET Core 3, nonché i reindirizzamenti HTTPS:
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        ...
        app.UseHttpsRedirection();
        ...
    
        app.UseAuthentication();
        app.UseAuthorization();
    
        ...
    }
          

Utilizza le credenziali dell'utente per accedere alle API di Google per suo conto

Ora puoi aggiungere ai controller metodi di azione che richiedono le credenziali dell'utente per accedere alle API di Google per suo conto. Lo snippet seguente mostra come elencare i file nell'account Google Drive dell'utente autenticato. Tieni presente soprattutto due cose:

  • L'utente non deve solo essere autenticato, ma deve anche aver concesso all'applicazione l'ambito https://www.googleapis.com/auth/drive.readonly, che specifichi utilizzando l'attributo GoogleScopedAuthorize.
  • Utilizziamo il meccanismo di dipendenza standard di ASP.NET Core 3 per ricevere un IGoogleAuthProvider che utilizziamo per ottenere le credenziali dell'utente.

Il codice:

  • Per prima cosa, aggiungi le seguenti direttive using al controller.
    using Google.Apis.Auth.AspNetCore3;
    using Google.Apis.Auth.OAuth2;
    using Google.Apis.Drive.v3;
    using Google.Apis.Services;
          
  • Aggiungi l'azione del controller come segue (e accompagnala con una vista che riceve un modello 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);
    }
          

E queste sono le nozioni di base. Puoi dare un'occhiata a HomeController.cs del progetto Google.Apis.Auth.AspNetCore3.IntegrationTests per scoprire come ottenere:

  • Solo autenticazione utente, senza ambiti specifici
  • Disconnessione utente
  • Autorizzazione incrementale con codice. Tieni presente che il sample mostra l'autorizzazione incrementale con attributi.
  • Esamina gli ambiti concessi
  • Esaminare i token di accesso e di aggiornamento
  • Forzare l'aggiornamento del token di accesso. Tieni presente che non devi farlo manualmente perché Google.Apis.Auth.AspNetCore3 rileverà se il token di accesso è scaduto o sta per scadere e lo aggiornerà automaticamente.

Service account

Le API di Google supportano anche service account. A differenza dello scenario in cui un'applicazione client richiede l'accesso ai dati di un utente finale, gli account di servizio forniscono l'accesso ai dati dell'applicazione client.

L'applicazione client firma la richiesta di un token di accesso utilizzando una chiave privata scaricata dalla console API di Google. Dopo aver creato un nuovo ID client, devi scegliere un tipo di applicazione Account di servizio e poi puoi scaricare la chiave privata. Dai un'occhiata al nostro esempio di account di servizio che utilizza 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();
        }
    }
}

Questo esempio crea un ServiceAccountCredential. Gli ambiti richiesti sono impostati ed è presente una chiamata a FromCertificate, che carica la chiave privata dal X509Certificate2 specificato. Come in tutti gli altri esempi di codice, la credenziale è impostata su HttpClientInitializer.