OAuth 2.0

W tym dokumencie opisano protokół OAuth 2.0 oraz informacje o tym, kiedy go używać, jak pobierać identyfikatory klientów i jak używać go z biblioteką interfejsu API Google dla domeny .NET.

Protokół OAuth 2.0

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

Pozyskiwanie identyfikatorów i obiektów tajnych klienta

Identyfikatory i tajne kody klienta możesz pobrać z konsoli Google API. Istnieją różne typy identyfikatorów klientów, dlatego dopilnuj, aby otrzymać odpowiedni typ aplikacji:

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

Dane uwierzytelniające

Dane logowania użytkownika

UserCredential to klasa pomocna w wątek, która służy do korzystania z tokena dostępu do chronionych zasobów. Token dostępu zazwyczaj wygasa po godzinie i po tym czasie pojawi się błąd, jeśli spróbujesz go użyć.

UserCredential i AuthorizationCodeFlow zajmują się automatycznym „odświeżaniem”, co oznacza po prostu uzyskanie nowego tokena dostępu. W tym celu używany jest długotrwały token odświeżania, który otrzymujesz wraz z tokenem dostępu, jeśli używasz parametru access_type=offline podczas procesu kodu autoryzacji.

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

Aby mieć pewność, że tokeny dostępu i odświeżania się nie zmienią, możesz wprowadzić własną implementację IDataStore lub użyć jednej z tych implementacji dostępnych w bibliotece:

  • FileDataStore w przypadku pliku .NET zapewnia, że dane logowania pozostaną w pliku.

Dane logowania konta usługi

ServiceAccountCredential jest podobny do UserCredential, ale służy do innego celu. Google OAuth 2.0 obsługuje interakcje między serwerami, np. między aplikacją internetową a Google Cloud Storage. Aby uzyskać dostęp do interfejsu API, aplikacja żądająca musi udowodnić swoją tożsamość, a użytkownik nie musi w tym uczestniczyć. ServiceAccountCredential przechowuje klucz prywatny, który służy do podpisywania prośby o nowy token dostępu.

Zarówno UserCredential, jak i ServiceAccountCredential implementują IConfigurableHttpClientInitializer, aby zarejestrować każdą z nich jako:

  • Niepowodzenie modułu obsługi odpowiedzi, więc odświeży token, jeśli otrzyma kod stanu HTTP 401.
  • Element przechwytywania, który przechwytuje nagłówek Authorization przy każdym żądaniu.

Zainstalowane aplikacje

Przykładowy kod za pomocą 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 zostanie utworzona nowa instancja UserCredential przez wywołanie metody GoogleWebAuthorizationBroker.AuthorizeAsync. Ta metoda statyczna uzyskuje:

    • Tajny klucz klienta (lub strumień do tajnego klucza klienta).
    • Wymagane zakresy.
    • Identyfikator użytkownika.
    • Token anulowania anulowania operacji.
    • Opcjonalny magazyn danych. Jeśli magazyn danych nie został określony, domyślną wartością jest FileDataStore z domyślnym folderem Google.Apis.Auth. Folder zostanie utworzony w aplikacji Environment.SpecialFolder.ApplicationData.
  • Metoda UserCredential, która jest zwracana przez tę metodę, jest ustawiana jako HttpClientInitializer w BooksService (za pomocą inicjatora). Jak wyjaśniliśmy powyżej, UserCredential implementuje inicjator klienta HTTP.

  • W powyższym przykładowym kodzie informacje o kliencie klienta są ładowane 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"));
          

Przejrzyj próbkę książek.

Aplikacje internetowe (ASP.NET Core 3)

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

Google.Apis.Auth.AspNetCore3 to zalecana biblioteka, która jest używana w większości scenariuszy dotyczących protokołu OAuth 2.0 opartych na usługach Google w aplikacjach ASP.NET Core 3. Implementuje moduł obsługi uwierzytelniania OpenIdConnect w Google. Obsługuje uwierzytelnianie przyrostowe i definiuje wstrzyknięty kod IGoogleAuthProvider do przesyłania danych logowania Google, których można używać z interfejsami API Google.

W tej sekcji opisano, jak skonfigurować Google.Apis.Auth.AspNetCore3 i jak z niej korzystać. Podany tu kod jest oparty na: Google.Apis.Auth.AspNetCore3.IntegrationTests, która jest w pełni sprawną, standardową aplikacją ASP.NET Core 3.

Jeśli chcesz wykonać instrukcje zgodnie z tą dokumentacją, musisz mieć własną aplikację ASP.NET Core 3 i wykonać te czynności wstępne.

Wymagania wstępne

  • Zainstaluj pakiet Google.Apis.Auth.AspNetCore3.
  • Używamy interfejsu API Dysku Google, dlatego 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. Będzie to projekt identyfikujący Twoją aplikację.
  • Włącz interfejs Google Drive API. Aby włączyć interfejsy API, postępuj zgodnie z tymi instrukcjami.
  • Utwórz dane logowania, które będą identyfikować Twoją aplikację w Google. Wykonaj te instrukcje, aby utworzyć dane logowania i pobrać plik client_secrets.json. Dwie ważne informacje:
    • Zwróć uwagę, że dane logowania muszą mieć typ Aplikacja internetowa.
    • Aby uruchomić tę aplikację, wystarczy podać identyfikator URI przekierowania: https://localhost:5001/signin-oidc.

Konfigurowanie aplikacji pod kątem korzystania z Google.Apis.Auth.AspNetCore3

Plik Google.Apis.Auth.AspNetCore3 jest skonfigurowany w klasie Startup lub podobnej metod, której możesz używać. Poniższe fragmenty są wyodrębnione z Startup.cs w projekcie Google.Apis.Auth.AspNetCore3.IntegrationTests.

  • Dodaj do pliku Startup.cs poniższą dyrektywę.
    using Google.Apis.Auth.AspNetCore3;
  • W metodzie Startup.ConfigureServices dodaj następujący kod zastępczy Client-ID i obiekty tajne klucza klienta w wartościach z pliku client_secrets.json. Wartości możesz wczytywać bezpośrednio z pliku JSON lub w inny sposób zabezpieczony. Przykład metody wczytywania tych wartości bezpośrednio z pliku JSON znajdziesz w projekcie 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 dodaj do potoku komponenty uwierzytelniania i autoryzacji autoryzacji ASP.NET Core 3 oraz przekierowania HTTPS:
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        ...
        app.UseHttpsRedirection();
        ...
    
        app.UseAuthentication();
        app.UseAuthorization();
    
        ...
    }
          

Używaj danych logowania użytkownika, aby uzyskiwać dostęp do interfejsów API Google w ich imieniu

Możesz teraz dodawać metody działania do kontrolerów, które wymagają danych logowania użytkownika do korzystania z interfejsów API Google w ich imieniu. Poniższy fragment przedstawia, jak wyświetlić listę plików na uwierzytelnionym koncie Dysku Google. Zwróć uwagę przede wszystkim na te kwestie:

  • Użytkownik nie tylko musi zostać uwierzytelniony, ale musi też przyznać aplikacji zakres https://www.googleapis.com/auth/drive.readonly określony w atrybucie GoogleScopedAuthorize.
  • Do uzyskania danych logowania użytkownika używamy IGoogleAuthProvider standardowego mechanizmu wstrzykiwania zależności ASP.NET Core 3&#39.

Kod:

  • Najpierw dodaj w kontrolerze te polecenia:
    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 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);
    }
          

A to podstawy. Aby dowiedzieć się, co możesz osiągnąć, zapoznaj się z narzędziem HomeController.cs w projekcie Google.Apis.Auth.AspNetCore3.IntegrationTests:

  • Tylko uwierzytelnianie użytkownika, bez określonych zakresów
  • Funkcja wylogowywania
  • Autoryzacja przyrostowa za pomocą kodu. Zwróć uwagę, że fragment kodu powyżej zawiera przyrostową autoryzację według atrybutów.
  • Badanie obecnie przyznanych zakresów
  • Badanie dostępu i tokenów odświeżania
  • Wymuś odświeżenie tokena dostępu. Nie musisz robić tego samodzielnie, bo Google.Apis.Auth.AspNetCore3 wykryje, czy token dostępu wygasł lub niedługo wygaśnie – i automatycznie się odświeży.

Aplikacje internetowe (ASP.NET MVC)

Interfejsy API Google obsługują protokół OAuth 2.0 dla aplikacji internetowych. Aby uruchomić poniższy kod, musisz najpierw dodać do projektu identyfikator URI przekierowania w konsoli Google API. Ponieważ będziesz używać atrybutu FlowMetadata i jego ustawień domyślnych, ustaw identyfikator URI przekierowania na wartość your_site/AuthCallback/IndexAsync.

Aby znaleźć identyfikatory URI przekierowania dla danych logowania OAuth 2.0, wykonaj te czynności:

  1. Otwórz stronę Dane logowania w konsoli interfejsu API.
  2. Jeśli nie utworzono jeszcze danych logowania OAuth 2.0, kliknij Utwórz dane logowania OAuth 2.0.
  3. Po utworzeniu danych logowania możesz wyświetlić lub edytować przekierowania, klikając identyfikator klienta (w przypadku aplikacji internetowej) w sekcji Identyfikatory klienta OAuth 2.0.

Po utworzeniu w projekcie IDE nowego projektu aplikacji internetowej dodaj odpowiedni pakiet Google.Apis NuGet dla Dysku, YouTube lub innej usługi, z której chcesz korzystać. Następnie dodaj pakiet Google.Apis.Auth.MVC. Poniższy kod pokazuje aplikację ASP.NET MVC, która wysyła zapytanie do usługi interfejsu API Google.

  1. Dodaj własną implementację tagu FlowMetadata.
    using System;
    using System.Web.Mvc;
    
    using Google.Apis.Auth.OAuth2;
    using Google.Apis.Auth.OAuth2.Flows;
    using Google.Apis.Auth.OAuth2.Mvc;
    using Google.Apis.Drive.v2;
    using Google.Apis.Util.Store;
    
    namespace Google.Apis.Sample.MVC4
    {
        public class AppFlowMetadata : FlowMetadata
        {
            private static readonly IAuthorizationCodeFlow flow =
                new GoogleAuthorizationCodeFlow(new GoogleAuthorizationCodeFlow.Initializer
                    {
                        ClientSecrets = new ClientSecrets
                        {
                            ClientId = "PUT_CLIENT_ID_HERE",
                            ClientSecret = "PUT_CLIENT_SECRET_HERE"
                        },
                        Scopes = new[] { DriveService.Scope.Drive },
                        DataStore = new FileDataStore("Drive.Api.Auth.Store")
                    });
    
            public override string GetUserId(Controller controller)
            {
                // In this sample we use the session to store the user identifiers.
                // That's not the best practice, because you should have a logic to identify
                // a user. You might want to use "OpenID Connect".
                // You can read more about the protocol in the following link:
                // https://developers.google.com/accounts/docs/OAuth2Login.
                var user = controller.Session["user"];
                if (user == null)
                {
                    user = Guid.NewGuid();
                    controller.Session["user"] = user;
                }
                return user.ToString();
    
            }
    
            public override IAuthorizationCodeFlow Flow
            {
                get { return flow; }
            }
        }
    }
          

    FlowMetadata to abstrakcyjna klasa zawierająca Twoją logikę pobierania identyfikatora użytkownika i używanego IAuthorizationCodeFlow.

    W powyższym przykładowym kodzie zostanie utworzony nowy element GoogleAuthorizationCodeFlow z odpowiednimi zakresami, obiektami tajnymi klienta i magazynem danych. Możesz dodać własną implementację tagu IDataStore, na przykład możesz użyć właściwości EntityFramework.

  2. Zaimplementuj własny kontroler, który korzysta z usługi interfejsu API Google. Ta przykład używa DriveService:
    using System.Linq;
    using System.Threading;
    using System.Threading.Tasks;
    using System.Web.Mvc;
    
    using Google.Apis.Auth.OAuth2.Mvc;
    using Google.Apis.Drive.v2;
    using Google.Apis.Services;
    
    using Google.Apis.Sample.MVC4;
    
    namespace Google.Apis.Sample.MVC4.Controllers
    {
        public class HomeController : Controller
        {
            public async Task<ActionResult> IndexAsync(CancellationToken cancellationToken)
            {
                var result = await new AuthorizationCodeMvcApp(this, new AppFlowMetadata()).
                    AuthorizeAsync(cancellationToken);
    
                if (result.Credential != null)
                {
                    var service = new DriveService(new BaseClientService.Initializer
                        {
                            HttpClientInitializer = result.Credential,
                            ApplicationName = "ASP.NET MVC Sample"
                        });
    
                    // YOUR CODE SHOULD BE HERE..
                    // SAMPLE CODE:
                    var list = await service.Files.List().ExecuteAsync();
                    ViewBag.Message = "FILE COUNT IS: " + list.Items.Count();
                    return View();
                }
                else
                {
                    return new RedirectResult(result.RedirectUri);
                }
            }
        }
    }
          
  3. Zaimplementuj własny kontroler wywołania zwrotnego. Implementacja powinna wyglądać mniej więcej tak:
    using Google.Apis.Sample.MVC4;
    
    namespace Google.Apis.Sample.MVC4.Controllers
    {
        public class AuthCallbackController : Google.Apis.Auth.OAuth2.Mvc.Controllers.AuthCallbackController
        {
            protected override Google.Apis.Auth.OAuth2.Mvc.FlowMetadata FlowData
            {
                get { return new AppFlowMetadata(); }
            }
        }
    }
          

Konto usługi

Interfejsy API Google obsługują też konta usługi. Inaczej niż w przypadku scenariusza, w którym aplikacja kliencka prosi o dostęp do danych użytkownika końcowego, konta usługi zapewniają dostęp do takich danych.

Aplikacja kliencka podpisuje żądanie tokena dostępu przy użyciu klucza prywatnego pobranego z Konsoli interfejsów API Google. Po utworzeniu nowego identyfikatora klienta wybierz typ aplikacji „Konto usługi”, a następnie pobierz klucz prywatny. Zapoznaj się z 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 są ustawione zakresy i wywołanie FromCertificate, które wczytuje klucz prywatny z podanego X509Certificate2. Tak jak we wszystkich innych przykładowych kodach, dane logowania są ustawione na HttpClientInitializer.