OAuth 2.0

Tài liệu này mô tả về OAuth 2.0, trường hợp sử dụng, cách lấy mã ứng dụng khách và cách sử dụng OAuth 2.0 với Thư viện ứng dụng API của Google dành cho .NET.

Giao thức OAuth 2.0

OAuth 2.0 là giao thức uỷ quyền mà các API của Google sử dụng. Bạn nên làm quen với giao thức bằng cách đọc các đường liên kết sau:

Nhận mã ứng dụng khách và khoá bí mật

Bạn có thể nhận mã ứng dụng khách và mã thông báo bí mật trên Google API Console. Có nhiều loại mã ứng dụng khách, vì vậy, hãy nhớ chọn đúng loại cho ứng dụng của bạn:

Trong mỗi đoạn mã bên dưới (ngoại trừ mã tài khoản dịch vụ), bạn phải tải mật khẩu ứng dụng khách xuống và lưu trữ mật khẩu đó dưới dạng client_secrets.json trong dự án.

Thông tin xác thực

Thông tin xác thực người dùng

UserCredential là một lớp trợ giúp an toàn cho luồng, sử dụng mã truy cập để truy cập vào các tài nguyên được bảo vệ. Mã truy cập thường hết hạn sau 1 giờ. Sau đó, bạn sẽ gặp lỗi nếu cố sử dụng mã này.

UserCredentialAuthorizationCodeFlow sẽ tự động "làm mới" mã thông báo, tức là chỉ lấy mã truy cập mới. Bạn có thể thực hiện việc này bằng cách sử dụng mã làm mới dài hạn. Bạn sẽ nhận được mã này cùng với mã truy cập nếu sử dụng thông số access_type=offline trong quy trình mã uỷ quyền.

Trong hầu hết ứng dụng, bạn nên lưu trữ mã truy cập của thông tin đăng nhập và mã làm mới trong bộ nhớ liên tục. Nếu không, bạn sẽ phải cung cấp cho người dùng cuối một trang uỷ quyền trong trình duyệt mỗi giờ, vì mã truy cập sẽ hết hạn sau một giờ kể từ khi bạn nhận được.

Để đảm bảo mã truy cập và làm mới vẫn tồn tại, bạn có thể cung cấp cách triển khai IDataStore của riêng mình hoặc bạn có thể sử dụng một trong các phương thức triển khai sau do thư viện cung cấp:

  • FileDataStore cho .NET đảm bảo rằng thông tin xác thực sẽ được duy trì trong một tệp.

ServiceAccountCredential

ServiceAccountCredential tương tự như UserCredential nhưng phục vụ một mục đích khác. Google OAuth 2.0 hỗ trợ hoạt động tương tác từ máy chủ đến máy chủ, chẳng hạn như hoạt động tương tác giữa ứng dụng web và Google Cloud Storage. Ứng dụng yêu cầu phải chứng minh danh tính của chính nó để có quyền truy cập vào API và người dùng cuối không cần tham gia. ServiceAccountCredential lưu trữ một khoá riêng tư để ký yêu cầu nhận mã truy cập mới.

Cả UserCredentialServiceAccountCredential đều triển khai IConfigurableHttpClientInitializer nên bạn có thể đăng ký từng phương thức này là:

  • Trình xử lý phản hồi không thành công, vì vậy, trình xử lý này sẽ làm mới mã thông báo nếu nhận được mã trạng thái HTTP 401.
  • Trình chặn, để chặn tiêu đề Authorization của mọi yêu cầu.

Các ứng dụng đã cài đặt

Mã mẫu sử dụng API Sách:

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();
            ...
        }
    }
}
  
  • Trong mã mẫu này, một thực thể UserCredential mới sẽ được tạo bằng cách gọi phương thức GoogleWebAuthorizationBroker.AuthorizeAsync. Phương thức tĩnh này có những tính năng sau:

    • Mật khẩu ứng dụng khách (hoặc một luồng tới mật khẩu ứng dụng khách).
    • Các phạm vi bắt buộc.
    • Giá trị nhận dạng người dùng.
    • Mã huỷ để huỷ một thao tác.
    • Kho dữ liệu không bắt buộc. Nếu bạn không chỉ định kho dữ liệu, giá trị mặc định sẽ là FileDataStore với thư mục Google.Apis.Auth mặc định. Thư mục này được tạo trong Environment.SpecialFolder.ApplicationData.
  • UserCredential mà phương thức này trả về được đặt thành HttpClientInitializer trên BooksService (bằng cách sử dụng trình khởi chạy). Như đã giải thích ở trên, UserCredential triển khai một trình khởi chạy ứng dụng HTTP.

  • Lưu ý rằng trong mã mẫu ở trên, thông tin mật khẩu ứng dụng khách được tải từ một tệp, nhưng bạn cũng có thể thực hiện như sau:

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

Hãy xem Mẫu Sách của chúng tôi.

Ứng dụng web (ASP.NET Core 3)

API Google hỗ trợ OAuth 2.0 cho ứng dụng máy chủ web.

Google.Apis.Auth.AspNetCore3 là thư viện được đề xuất sử dụng cho hầu hết các trường hợp OAuth 2.0 dựa trên Google trong các ứng dụng ASP.NET Core 3. Thư viện này sẽ triển khai một trình xử lý xác thực OpenIdConnect dành riêng cho Google. Tính năng này hỗ trợ xác thực dần dần và xác định một IGoogleAuthProvider có thể chèn để cung cấp thông tin đăng nhập Google có thể sử dụng được với các API của Google.

Phần này mô tả cách định cấu hình và sử dụng Google.Apis.Auth.AspNetCore3. Mã hiển thị ở đây dựa trên Google.Apis.Auth.AspNetCore3.IntegrationTests, một ứng dụng ASP.NET Core 3 tiêu chuẩn và hoạt động hoàn toàn.

Nếu muốn làm theo hướng dẫn trong tài liệu này, bạn cần có ứng dụng ASP.NET Core 3 của riêng mình và hoàn thành các bước này như một điều kiện tiên quyết.

Điều kiện tiên quyết

  • Cài đặt gói Google.Apis.Auth.AspNetCore3.
  • Chúng tôi đang sử dụng API Google Drive, vì vậy, bạn cũng sẽ cần cài đặt gói Google.Apis.Drive.v3.
  • Tạo một dự án trên Google Cloud nếu bạn chưa có. Hãy làm theo các hướng dẫn này để thực hiện việc này. Đây sẽ là dự án được xác định cho ứng dụng của bạn.
  • Nhớ bật API Google Drive. Để bật API, hãy làm theo các hướng dẫn này.
  • Tạo thông tin uỷ quyền giúp Google nhận dạng ứng dụng của bạn. Hãy làm theo các hướng dẫn này để tạo thông tin xác thực và tải tệp client_secrets.json xuống. Hai điểm nổi bật:
    • Lưu ý rằng loại thông tin đăng nhập phải là Ứng dụng web.
    • Để chạy ứng dụng này, URI chuyển hướng duy nhất mà bạn cần thêm là https://localhost:5001/signin-oidc.

Định cấu hình ứng dụng để sử dụng Google.Apis.Auth.AspNetCore3

Google.Apis.Auth.AspNetCore3 được định cấu hình trong lớp Startup hoặc giải pháp thay thế tương tự mà bạn có thể đang sử dụng. Các đoạn mã sau được trích xuất từ Startup.cs trong dự án Google.Apis.Auth.AspNetCore3.IntegrationTests.

  • Thêm lệnh sử dụng sau vào tệp Startup.cs.
    using Google.Apis.Auth.AspNetCore3;
  • Trong phương thức Startup.ConfigureServices, hãy thêm mã sau, thay đổi mã ứng dụng khách và phần giữ chỗ bí mật ứng dụng khách bằng các giá trị có trong tệp client_secrets.json. Bạn có thể tải các giá trị này trực tiếp từ tệp JSON hoặc có thể lưu trữ chúng theo bất kỳ cách nào an toàn khác. Hãy xem phương thức ClientInfo.Load trong dự án Google.Apis.Auth.AspNetCore3.IntegrationTests để biết ví dụ về cách tải các giá trị này trực tiếp từ tệp 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};
            });
    }
          
  • Trong phương thức Startup.Configure, hãy nhớ thêm các thành phần phần mềm trung gian xác thực và uỷ quyền ASP.NET Core 3 vào quy trình, cũng như các lệnh chuyển hướng HTTPS:
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        ...
        app.UseHttpsRedirection();
        ...
    
        app.UseAuthentication();
        app.UseAuthorization();
    
        ...
    }
          

Sử dụng thông tin đăng nhập của người dùng để thay mặt họ truy cập vào các API của Google

Giờ đây, bạn đã sẵn sàng thêm các phương thức hành động vào tay điều khiển mà yêu cầu thông tin xác thực của người dùng để thay mặt họ truy cập vào các API của Google. Đoạn mã sau đây cho biết cách liệt kê các tệp trên tài khoản Google Drive của người dùng đã xác thực. Hãy chú ý hai điều chủ yếu:

  • Người dùng không chỉ cần được xác thực, mà còn cần phải cấp phạm vi https://www.googleapis.com/auth/drive.readonly cho ứng dụng mà bạn chỉ định thông qua thuộc tính GoogleScopedAuthorize.
  • Chúng tôi đang sử dụng cơ chế chèn phần phụ thuộc tiêu chuẩn của ASP.NET Core 3 để nhận IGoogleAuthProvider mà chúng tôi dùng để lấy thông tin đăng nhập của người dùng.

Mã:

  • Trước tiên, hãy thêm nội dung sau bằng lệnh vào tay điều khiển.
    using Google.Apis.Auth.AspNetCore3;
    using Google.Apis.Auth.OAuth2;
    using Google.Apis.Drive.v3;
    using Google.Apis.Services;
          
  • Thêm thao tác của tay điều khiển như sau (và đi kèm với thao tác này là một khung hiển thị đơn giản nhận được mô hình 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);
    }
          

Và đây là những thông tin cơ bản. Bạn có thể xem HomeController.cs trong dự án Google.Apis.Auth.AspNetCore3.IntegrationTests để tìm hiểu cách đạt được:

  • Chỉ xác thực người dùng, không có phạm vi cụ thể
  • Chức năng đăng xuất
  • Uỷ quyền gia tăng qua mã. Xin lưu ý rằng đoạn mã ở trên cho thấy việc uỷ quyền gia tăng thông qua các thuộc tính.
  • Kiểm tra các phạm vi hiện được cấp
  • Kiểm tra mã truy cập và làm mới
  • Buộc làm mới mã truy cập. Xin lưu ý rằng bạn không phải tự làm điều này vì Google.Apis.Auth.AspNetCore3 sẽ phát hiện xem mã truy cập đã hết hạn hoặc gần hết hạn và sẽ tự động làm mới mã.

Tài khoản dịch vụ

Các API của Google cũng hỗ trợ Tài khoản dịch vụ. Không giống như trường hợp trong đó ứng dụng yêu cầu quyền truy cập vào dữ liệu của người dùng cuối, tài khoản dịch vụ cung cấp quyền truy cập vào dữ liệu của chính ứng dụng đó.

Ứng dụng khách của bạn ký yêu cầu mã truy cập bằng cách sử dụng khoá riêng tư được tải xuống từ Google API Console. Sau khi tạo mã ứng dụng khách mới, bạn nên chọn loại ứng dụng "Tài khoản dịch vụ" và sau đó có thể tải khoá riêng tư xuống. Hãy xem mẫu tài khoản dịch vụ sử dụng API Google Plus của chúng tôi.

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

Mã mẫu ở trên sẽ tạo một ServiceAccountCredential. Các phạm vi bắt buộc đã được đặt và sẽ có một lệnh gọi đến FromCertificate để tải khoá riêng tư từ X509Certificate2 đã cho. Như trong tất cả các mã mẫu khác, thông tin xác thực được đặt là HttpClientInitializer.