OAuth 2.0

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

Giao thức OAuth 2.0

OAuth 2.0 là giao thức uỷ quyền đượ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à bí mật

Bạn có thể lấy 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 đảm bảo lấy đúng loại cho ứng dụng của bạn:

Trong mỗi đoạn mã dưới đây (ngoại trừ mã tài khoản dịch vụ), bạn phải tải mã ứ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ình 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 sẽ hết hạn sau 1 giờ. Sau đó, bạn sẽ gặp lỗi nếu cố sử dụng mã đó.

UserCredentialAuthorizationCodeFlow sẽ tự động "làm mới" mã thông báo, đơn giản là nhận 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ã truy cập 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 xác thực và mã làm mới trong bộ nhớ liên tục. Nếu không, bạn sẽ cần hiển thị cho người dùng cuối 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 sử dụng một trong các phương thức triển khai sau do thư viện cung cấp:

  • FileDataStore đối với .NET đảm bảo rằng thông tin xác thực sẽ có hiệu lực vĩnh viễn trong một tệp.

Thông tin xác thực tài khoản dịch vụ

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

Cả UserCredentialServiceAccountCredential đều triển khai IConfigurableHttpClientInitializer để bạn có thể đăng ký từng tài khoản trong số 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.
  • Một trình chặn, để chặn tiêu đề Authorization trong 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 nhận được những mục sau:

    • Mật khẩu ứng dụng khách (hoặc một luồng đến 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ã thông báo huỷ để huỷ một hoạt động.
    • Kho dữ liệu không bắt buộc. Nếu kho dữ liệu không được chỉ định, 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 (sử dụng trình khởi tạo). Như đã giải thích ở trên, UserCredential triển khai 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 của ứ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 các thao tác 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)

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

Google.Apis.Auth.AspNetCore3 là thư viện mà bạn nên 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. SDK này triển khai một trình xử lý xác thực OpenIdConnect dành riêng cho Google. Lớp này hỗ trợ tính năng xác thực từng phần và xác định một IGoogleAuthProvider có thể chèn để cung cấp thông tin xác thực của Google có thể dùng 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 chỉnh.

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

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

  • Cài đặt gói Google.Apis.Auth.AspNetCore3.
  • Chúng tôi đang dùng API Google Drive nên bạn cũng cần cài đặt gói Google.Apis.Drive.v3.
  • Tạo một dự á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 bằng ứ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 xác định ứng dụng của bạn cho Google. Hãy làm theo hướng dẫn này để tạo thông tin xác thực uỷ quyền và tải tệp client_secrets.json xuống. Hai điểm nổi bật:
    • Xin 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 phương án 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 đây vào tệp Startup.cs.
    using Google.Apis.Auth.AspNetCore3;
  • Trong phương thức Startup.ConfigureServices, hãy thêm mã sau đây, thay đổi phần giữ chỗ Mã ứng dụng khách và Mật khẩu ứ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 an toàn nào khác. Hãy xem phương thức ClientInfo.Load trong dự án Google.Apis.Auth.AspNetCore3.IntegrationsTests để biết ví dụ về cách trực tiếp tải các giá trị này qua 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 uỷ quyền và xác thực 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, yêu cầu thông tin xác thực của người dùng để thay mặt tay điều khiển 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ú ý chủ yếu đến hai điều:

  • Người dùng không chỉ cần được xác thực, mà họ còn cần cấp phạm vi https://www.googleapis.com/auth/drive.readonly cho ứng dụng của bạn 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 được IGoogleAuthProvider mà chúng tôi sử dụng để lấy thông tin xác thực của người dùng.

Mã:

  • Trước tiên, hãy thêm nội dung sau bằng cách sử dụng lệnh vào bộ đ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 bộ điều khiển như sau (kèm theo 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 điều cơ bản. Bạn có thể xem HomeController.cs trong dự án Google.Apis.Auth.AspNetCore3.IntegrationsTests để tìm hiểu cách bạn có thể đạ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 sự 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 quyền truy cập và làm mới mã thông báo
  • Buộc làm mới mã truy cập. Lưu ý rằng bạn không phải tự làm việc này vì Google.Apis.Auth.AspNetCore3 sẽ phát hiện xem mã truy cập đã hết hạn hoặc sắp hết hạn hay chưa và sẽ tự động làm mới.

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 ứng dụng khách 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 ứng dụng khách.

Ứng dụng khách của bạn ký yêu cầu cấp mã truy cập bằng cách sử dụng một 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ụ" rồi có thể tải khoá riêng tư xuống. Hãy xem mẫu tài khoản dịch vụ 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à có một lệnh gọi đến FromCertificate để tải khoá riêng tư từ X509Certificate2 đã cho. Giống như trong tất cả mã mẫu khác, thông tin xác thực được đặt là HttpClientInitializer.