OAuth 2.0

W tym dokumencie opisujemy OAuth 2.0, podajemy, kiedy należy go używać, jak pozyskiwać identyfikatory klienta oraz jak korzystać z niego w bibliotece klienta interfejsu API Google dla .NET.

Protokół OAuth 2.0

OAuth 2.0 to protokół autoryzacji używany przez interfejsy API Google. Zapoznaj się z protokołem, czytając te artykuły:

Pobieranie identyfikatorów i tajnych kluczy klientów

Identyfikatory klienta i klucze tajne możesz pobrać w Konsoli interfejsów API Google. Istnieją różne typy identyfikatorów klienta, dlatego pamiętaj, aby wybrać odpowiedni typ dla swojej aplikacji:

W każdym z wyświetlanych fragmentów kodu (z wyjątkiem fragmentu kodu dotyczącego konta usługi) musisz pobrać klucz tajny klienta i zapisać go jako client_secrets.json w projekcie.

Dane logowania

Dane logowania użytkownika

UserCredential to klasa pomocnicza chroniąca wątki, która umożliwia korzystanie z tokena dostępu do zasobów chronionych. Token dostępu wygasa zwykle po 1 godzinie. Po tym czasie, jeśli spróbujesz go użyć, pojawi się błąd.

UserCredential AuthorizationCodeFlow automatycznie „odświeża” token, co oznacza uzyskanie nowego tokena dostępu. Aby to zrobić, użyj długoterminowego tokena odświeżania, który otrzymasz wraz z tokenem dostępu, jeśli podczas przepływu kodu autoryzacji użyjesz parametru access_type=offline.

W większości aplikacji zaleca się przechowywanie tokena dostępu i tokena odświeżania w pamięci trwałej. W przeciwnym razie musisz co godzinę wyświetlać użytkownikowi stronę autoryzacji w przeglądarce, ponieważ token dostępu wygasa godzinę po jego otrzymaniu.

Aby zapewnić trwałość tokenów dostępu i odświeżania, możesz podać własną implementacjęIDataStore lub użyć jednej z tych implementacji udostępnionych przez bibliotekę:

  • FileDataStore w języku C# zapewnia, że dane uwierzytelniające będą trwałe w pliku.

ServiceAccountCredential

ServiceAccountCredentialjest podobna do UserCredential, ale służy do innego celu. Google OAuth 2.0 obsługuje interakcję między serwerami, na przykład między aplikacją internetową a Google Cloud Storage. Aplikacja wykonująca żądanie musi potwierdzić swoją tożsamość, aby uzyskać dostęp do interfejsu API. Nie wymaga to udziału użytkownika końcowego. ServiceAccountCredential przechowuje klucz prywatny, który służy do podpisywania żądań w celu uzyskania nowego tokena dostępu.

Zarówno UserCredential, jak i ServiceAccountCredential implementują IConfigurableHttpClientInitializer, więc możesz zarejestrować je jako:

  • Obsługa odpowiedzi z błędem, która odświeża token, jeśli otrzyma kod stanu HTTP 401.
  • Przechwytywanie, aby przechwytywać nagłówek Authorizationw przypadku każdego żądania.

Zainstalowane aplikacje

Przykładowy kod korzystający z interfejsu Books API:

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();
            ...
        }
    }
}
  
  • W tym przykładowym kodzie nowa instancja UserCredential jest tworzona przez wywołanie metody GoogleWebAuthorizationBroker.AuthorizeAsync. Ta metoda statyczna zwraca:

    • tajny klucz klienta (lub strumień do tajnego klucza klienta);
    • Wymagane zakresy.
    • Identyfikator użytkownika.
    • Token anulowania operacji.
    • opcjonalny obiekt pamięci danych. Jeśli nie określisz bazy danych, domyślnie zostanie użyta baza danych FileDataStore z domyślnym folderem Google.Apis.Auth. Folder zostanie utworzony w Environment.SpecialFolder.ApplicationData.
  • UserCredential zwracany przez tę metodę jest ustawiany jako HttpClientInitializer w obiekcie BooksService (za pomocą funkcji inicjującej). Jak już wspomnieliśmy, UserCredential implementuje inicjalizator klienta HTTP.

  • Zwróć uwagę, że w przykładowym kodzie informacje tajne klienta są wczytywane z pliku, ale możesz też wykonać te czynności:

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

Zapoznaj się z przykładem książki.

Aplikacje internetowe (ASP.NET Core 3)

Interfejsy API Google obsługują OAuth 2.0 w internetowych aplikacjach serwerowych.

Biblioteka Google.Apis.Auth.AspNetCore3 jest zalecana do większości scenariuszy OAuth 2.0 Google w aplikacjach ASP.NET Core 3. Wdraża interfejs obsługi autoryzacji OpenIdConnectspecyficzny dla Google. Obsługuje stopniowe uwierzytelnianie i określa funkcję do wstrzykiwania IGoogleAuthProvider, która służy do podawania danych logowania Google, których można używać z interfejsami API Google.

W tej sekcji opisaliśmy, jak skonfigurować i używać pakietu Google.Apis.Auth.AspNetCore3. Pokazujemy tu kod oparty na komponencie Google.Apis.Auth.AspNetCore3.IntegrationTests, który jest w pełni działającą standardową aplikacją ASP.NET Core 3.

Jeśli chcesz postępować zgodnie z tą dokumentacją jako samouczkiem, musisz mieć własną aplikację ASP.NET Core 3 i wykonać te czynności w ramach wstępnej konfiguracji.

Wymagania wstępne

  • Zainstaluj pakiet Google.Apis.Auth.AspNetCore3.
  • Używamy interfejsu API Dysku Google, więc musisz też zainstalować pakiet Google.Apis.Drive.v3.
  • Utwórz projekt Google Cloud (jeśli jeszcze go nie masz). Aby to zrobić, postępuj zgodnie z  tymi instrukcjami. Będzie to projekt, z którym będzie powiązana Twoja aplikacja.
  • Pamiętaj, aby włączyć interfejs Drive API. Aby włączyć interfejsy API, wykonaj te instrukcje.
  • Utwórz dane uwierzytelniające, które będą identyfikować Twoją aplikację w Google. Aby utworzyć dane uwierzytelniające i pobrać plik client_secrets.json, wykonaj te instrukcje. 2 najważniejsze informacje:
    • Pamiętaj, że typ danych logowania musi być ustawiony na Aplikacja internetowa.
    • Aby uruchomić tę aplikację, musisz dodać tylko identyfikator URI przekierowania: https://localhost:5001/signin-oidc.

Skonfiguruj aplikację tak, aby używała Google.Apis.Auth.AspNetCore3

Google.Apis.Auth.AspNetCore3 jest skonfigurowana w klasie Startup lub podobnej alternatywie, z której możesz korzystać. Poniższe fragmenty kodu pochodzą z projektu Startup.cs w projekcie Google.Apis.Auth.AspNetCore3.IntegrationTests.

  • Dodaj do pliku Startup.cs tę dyrektywę.
    using Google.Apis.Auth.AspNetCore3;
  • W metodzie Startup.ConfigureServices dodaj ten kod, zastępując wskaźniki zastępcze identyfikatora klienta i tajnego klucza klienta wartościami zawartymi w pliku client_secrets.json. Możesz załadować te wartości bezpośrednio z pliku JSON lub zapisać je w dowolny inny bezpieczny sposób. Aby dowiedzieć się, jak wczytywać te wartości bezpośrednio z pliku JSON, zapoznaj się z metodą ClientInfo.Load w projekcie Google.Apis.Auth.AspNetCore3.IntegrationTests
    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};
            });
    }
          
  • W metodzie Startup.Configure pamiętaj, aby dodać do potoku komponenty pośredniczące ASP.NET Core 3 do uwierzytelniania i autoryzacji, a także przekierowania HTTPS:
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        ...
        app.UseHttpsRedirection();
        ...
    
        app.UseAuthentication();
        app.UseAuthorization();
    
        ...
    }
          

Używanie danych logowania użytkownika do uzyskiwania dostępu do interfejsów Google API w jego imieniu

Możesz teraz dodać do kontrolerów metody działania, które wymagają danych logowania użytkownika, aby uzyskać dostęp do interfejsów API Google w jego imieniu. Ten fragment kodu pokazuje, jak wyświetlić listę plików na koncie Dysku Google uwierzytelnionego użytkownika. Zwróć uwagę przede wszystkim na 2 rzeczy:

  • Użytkownik musi nie tylko przejść uwierzytelnianie, ale też przyznać Twojej aplikacji zakres https://www.googleapis.com/auth/drive.readonly, który określasz za pomocą atrybutu GoogleScopedAuthorize.
  • Korzystamy ze standardowego mechanizmu wstrzykiwania zależności w ASP.NET Core 3, aby otrzymywać obiekt IGoogleAuthProvider, którego używamy do uzyskiwania danych uwierzytelniających użytkownika.

Kod:

  • Najpierw dodaj do kontrolera te dyrektywy.
    using Google.Apis.Auth.AspNetCore3;
    using Google.Apis.Auth.OAuth2;
    using Google.Apis.Drive.v3;
    using Google.Apis.Services;
          
  • Dodaj działanie kontrolera w ten sposób (i dołącz do niego widok, który przyjmuje model 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);
    }
          

To są podstawy. Aby dowiedzieć się, jak to zrobić, możesz zapoznać się z projektem HomeController.cs z pakietu Google.Apis.Auth.AspNetCore3.IntegrationTests, w którym znajdziesz:

  • tylko uwierzytelnianie użytkownika bez określonych zakresów uprawnień;
  • Wylogowanie użytkownika
  • Autoryzacja przyrostowa z kodem. Zwróć uwagę, że przykład pokazuje stopniowe autoryzowanie z atrybutami.
  • Sprawdzanie przyznanych zakresów
  • Sprawdzanie tokenów dostępu i odświeżania
  • Wymuś odświeżenie tokena dostępu. Nie musisz tego robić samodzielnie, ponieważ Google.Apis.Auth.AspNetCore3 wykryje, czy token dostępu wygasł lub wygaśnie wkrótce i automatycznie go odświeży.

Konto usługi

Interfejsy API Google obsługują też konta usługi. W przeciwieństwie do sytuacji, w której aplikacja kliencka prosi o dostęp do danych użytkownika, konta usługi zapewniają dostęp do danych aplikacji klienckiej.

Aplikacja klienta podpisuje żądanie tokena dostępu za pomocą klucza prywatnego pobranego z konsoli interfejsów API Google. Po utworzeniu nowego identyfikatora klienta wybierz typ aplikacji Konto usługi, a potem pobierz klucz prywatny. Zapoznaj się z naszym przykładem konta usługi korzystającego z interfejsu Google Plus API.

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();
        }
    }
}

Ten przykład tworzy ServiceAccountCredential. Wymagane zakresy są ustawione, a wywołanie funkcji FromCertificate wczytuje klucz prywatny z danego FromCertificate.X509Certificate2 Podobnie jak we wszystkich innych przykładowych fragmentach kodu, dane logowania są ustawione jako HttpClientInitializer.