OAuth 2.0

W tym dokumencie opisujemy protokół OAuth 2.0 oraz opisujemy, kiedy i jak uzyskiwać identyfikatory klientów z użyciem protokołu OAuth 2.0, oraz jak go używać z biblioteką klienta interfejsów API Google dla języka .NET.

Protokół OAuth 2.0

OAuth 2.0 to protokół autoryzacji używany przez interfejsy API Google. Aby zapoznać się z protokołem, przeczytaj te linki:

Uzyskiwanie identyfikatorów klientów i obiektów tajnych

Identyfikatory klientów i obiekty tajne możesz uzyskać w Konsoli interfejsów API Google. Istnieją różne typy identyfikatorów klienta, więc sprawdź, czy pobierasz odpowiedni typ dla swojej aplikacji:

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

Dane uwierzytelniające

Dane logowania użytkownika

UserCredential to klasa pomocnicza zapewniająca bezpieczeństwo wątku służąca do uzyskiwania dostępu do zabezpieczonych zasobów przy użyciu tokena dostępu. Token dostępu zwykle wygasa po 1 godzinie, a po jego użyciu pojawia się komunikat o błędzie, jeśli spróbujesz go użyć.

UserCredential i AuthorizationCodeFlow dbają o automatyczne „odświeżanie” tokena, co oznacza samo uzyskanie nowego tokena dostępu. W tym celu wykorzystuje się token odświeżania o długotrwałym czasie, który otrzymujesz razem z tokenem dostępu, jeśli podczas przepływu kodu autoryzacji używasz parametru access_type=offline.

W większości aplikacji zalecane jest przechowywanie tokena dostępu i tokena odświeżania w pamięci trwałej. W przeciwnym razie trzeba co godzinę wyświetlać użytkownikowi stronę autoryzacji w przeglądarce, ponieważ token dostępu wygasa po upływie godziny od jego otrzymania.

Aby mieć pewność, że tokeny dostępu i odświeżania będą działać, możesz udostępnić własną implementację IDataStore lub użyć jednej z tych implementacji zapewnianych przez bibliotekę:

  • FileDataStore dla .NET zapewnia, że dane logowania są trwałe w pliku.

ServiceAccountCredential

ServiceAccountCredential jest podobny do właściwości UserCredential, ale ma inny cel. Google OAuth 2.0 obsługuje interakcje między serwerami, takie jak między aplikacją internetową a Google Cloud Storage. Aplikacja żądająca dostępu musi potwierdzić swoją tożsamość, aby uzyskać dostęp do interfejsu API. Użytkownik końcowy nie musi w tym uczestniczyć. ServiceAccountCredential przechowuje klucz prywatny, który służy do podpisywania żądania uzyskania nowego tokena dostępu.

Zarówno UserCredential, jak i ServiceAccountCredential implementują instancję IConfigurableHttpClientInitializer, więc możesz zarejestrować każdą z nich jako:

  • Moduł obsługi odpowiedzi zakończony niepowodzeniem, więc odświeży się token, jeśli otrzyma kod stanu HTTP 401.
  • Przechwytywanie, aby przechwycić nagłówek Authorization przy każdym żądaniu.

Zainstalowane aplikacje

Przykładowy kod wygenerowany przy użyciu 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 uzyskuje te korzyści:

    • Tajny klucz klienta (lub strumień do tajnego klucza klienta).
    • Wymagane zakresy.
    • Identyfikator użytkownika.
    • Token anulowania do anulowania operacji.
    • Opcjonalny magazyn danych. Jeśli nie podasz magazynu danych, domyślną wartością będzie FileDataStore z domyślnym folderem Google.Apis.Auth. Folder zostanie utworzony w aplikacji Environment.SpecialFolder.ApplicationData.
  • Wartość UserCredential zwracana przez tę metodę jest ustawiona jako HttpClientInitializer w BooksService (przy użyciu inicjatora). Jak wyjaśniliśmy powyżej, UserCredential implementuje inicjator klienta HTTP.

  • Zwróć uwagę, że w powyższym przykładowym kodzie informacje o kluczu klienta są wczytywane z pliku, ale można też wykonywać 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 naszą próbką Książek.

Aplikacje internetowe (ASP.NET Core 3)

Interfejsy API Google obsługują protokół OAuth 2.0 dla aplikacji serwerowych.

Google.Apis.Auth.AspNetCore3 to zalecana biblioteka do użycia w większości przypadków opartych na protokole Google OAuth 2.0 w aplikacjach ASP.NET Core 3. Ma ona wbudowany moduł uwierzytelniania OpenIdConnect specyficzny dla Google. Obsługuje uwierzytelnianie przyrostowe i określa wstrzykiwany IGoogleAuthProvider do dostarczania danych logowania Google, których można używać z interfejsami API Google.

Ta sekcja zawiera informacje na temat konfigurowania i używania interfejsu Google.Apis.Auth.AspNetCore3. Pokazany tutaj kod jest oparty na Google.Apis.Auth.AspNetCore3.IntegrationTests – w pełni działającej, standardowej aplikacji ASP.NET Core 3.

Jeśli chcesz postępować zgodnie z tą dokumentacją jako samouczek, będziesz potrzebować własnej aplikacji ASP.NET Core 3. Konieczne jest też wykonanie tych czynności.

Wymagania wstępne

  • Zainstaluj pakiet Google.Apis.Auth.AspNetCore3.
  • Używamy interfejsu Google Drive API, więc musisz też zainstalować pakiet Google.Apis.Drive.v3.
  • Utwórz projekt Google Cloud, jeśli jeszcze go nie masz. Aby to zrobić, wykonaj te instrukcje. To będzie projekt, z którym identyfikowana będzie Twoja aplikacja.
  • Włącz interfejs Google Drive API. Aby włączyć interfejsy API, wykonaj te instrukcje.
  • Utwórz dane logowania, które pozwolą Google zidentyfikować Twoją aplikację. Wykonaj te instrukcje, aby utworzyć dane uwierzytelniające i pobrać plik client_secrets.json. 2 najważniejsze informacje:
    • Zwróć uwagę, że dane logowania muszą być typu Aplikacja internetowa.
    • Aby można było uruchomić tę aplikację, jedyny identyfikator URI przekierowania, który należy dodać, to https://localhost:5001/signin-oidc.

Skonfiguruj aplikację do korzystania z Google.Apis.Auth.AspNetCore3

Usługa Google.Apis.Auth.AspNetCore3 jest skonfigurowana w klasie Startup lub podobnej opcji, której możesz używać. Poniższe fragmenty kodu zostały wyodrębnione z pliku Startup.cs w projekcie Google.Apis.Auth.AspNetCore3.IntegrationTests.

  • Dodaj do pliku Startup.cs tę dyrektywę using.
    using Google.Apis.Auth.AspNetCore3;
  • W metodzie Startup.ConfigureServices dodaj poniższy kod i zmień obiekty zastępcze identyfikatora klienta i tajnego klucza klienta wartościami, które znajdują się w pliku client_secrets.json. Możesz wczytać te wartości bezpośrednio z pliku JSON lub przechowywać je w dowolny inny bezpieczny sposób. Zapoznaj się z metodą ClientInfo.Load w projekcie Google.Apis.Auth.AspNetCore3.IntegrationTests, aby zobaczyć, jak wczytać te wartości bezpośrednio z pliku 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};
            });
    }
          
  • W metodzie Startup.Configure dodaj do potoku komponenty uwierzytelniania i autoryzacji ASP.NET Core 3, a także przekierowania HTTPS:
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        ...
        app.UseHttpsRedirection();
        ...
    
        app.UseAuthentication();
        app.UseAuthorization();
    
        ...
    }
          

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

Teraz możesz dodać do kontrolerów metody działań, które wymagają danych logowania użytkownika w celu uzyskania dostępu do interfejsów API Google w jego imieniu. Poniższy fragment pokazuje, jak wyświetlić listę plików na koncie Dysku Google uwierzytelnionego użytkownika. Zwróć uwagę głównie na 2 rzeczy:

  • Użytkownik musi nie tylko być uwierzytelniony, ale też przyznać aplikacji zakres https://www.googleapis.com/auth/drive.readonly, który określasz w atrybucie GoogleScopedAuthorize.
  • Używamy standardowego mechanizmu wstrzykiwania zależności ASP.NET Core 3, aby otrzymać IGoogleAuthProvider, którego używamy do uzyskania danych logowania użytkownika.

Kod:

  • Najpierw do kontrolera dodaj 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 prosty widok, który odbiera 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. Zerknij na HomeController.cs w projekcie Google.Apis.Auth.AspNetCore3.IntegrationTests, aby dowiedzieć się, jak:

  • Tylko uwierzytelnianie użytkowników, bez konkretnych zakresów
  • Funkcja wylogowywania
  • Autoryzacja przyrostowa przy użyciu kodu. Powyższy fragment kodu pokazuje przyrostową autoryzację za pomocą atrybutów.
  • Sprawdź aktualnie przyznane zakresy
  • Sprawdź tokeny dostępu i odśwież
  • Wymuś odświeżenie tokena dostępu. Nie musisz robić tego samodzielnie, ponieważ Google.Apis.Auth.AspNetCore3 wykryje, czy token dostępu wygasł czy jest bliski wygaśnięcia, 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 własnej aplikacji klienckiej.

Aplikacja kliencka 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”. Następnie możesz pobrać klucz prywatny. Zapoznaj się z naszym przykładowym kontem usługi za pomocą 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();
        }
    }
}

Powyższy przykładowy kod tworzy ServiceAccountCredential. Wymagane zakresy zostały ustawione i występuje wywołanie FromCertificate, które wczytuje klucz prywatny z podanego X509Certificate2. Podobnie jak we wszystkich innych przykładach kodu dane logowania są ustawione na HttpClientInitializer.