OAuth 2.0

本文档介绍了 OAuth 2.0、何时使用 OAuth 2.0、如何获取客户端 ID,以及如何将 OAuth 2.0 与 .NET 版 Google API 客户端库搭配使用。

OAuth 2.0 协议

OAuth 2.0 是 Google API 使用的授权协议。 您应阅读以下链接,熟悉该协议:

获取客户端 ID 和密钥

您可以在 Google API 控制台中获取客户端 ID 和密钥。 客户端 ID 有不同的类型,因此请务必为您的应用获取正确的类型:

在显示的每个代码段(服务账号代码段除外)中,您都必须下载客户端密钥,并将其作为 client_secrets.json 存储在项目中。

凭据

用户凭据

UserCredential 是一个线程安全的辅助类,用于使用访问令牌访问受保护的资源。 访问令牌通常会在 1 小时后过期;如果您在超出该时间后尝试使用它,系统会报错。

UserCredentialAuthorizationCodeFlow 负责自动“刷新”令牌,即获取新的访问令牌。 这项操作是使用长期有效的刷新令牌完成的,如果您在授权代码流程中使用 access_type=offline 参数,则会随访问令牌一起收到该令牌。

在大多数应用中,建议将凭据的访问令牌和刷新令牌存储在永久存储空间中。 否则,您需要每小时在浏览器中向最终用户显示一次授权页面,因为访问令牌会在您收到后 1 小时过期。

为确保访问令牌和刷新令牌保持不变,您可以提供自己的 IDataStore 实现,也可以使用该库提供的以下某个实现:

  • 适用于 .NET 的 FileDataStore 可确保凭据会持久保存在文件中。

ServiceAccountCredential

ServiceAccountCredentialUserCredential 类似,但用途不同。 Google OAuth 2.0 支持服务器到服务器的互动,例如网络应用和 Google Cloud Storage 之间的服务器到服务器互动。 发出请求的应用需证明自己的身份,才能获得对 API 的访问权限,而无需最终用户参与。 ServiceAccountCredential 用于存储私钥,该私钥用于对请求进行签名以获取新的访问令牌。

UserCredentialServiceAccountCredential 都实现了 IConfigurableHttpClientInitializer,因此您可以将它们分别注册为:

  • 失败的响应处理程序,因此如果收到 HTTP 401 状态代码,则会刷新令牌。
  • 拦截器,用于拦截每个请求中的 Authorization 标头。

已安装的应用

使用 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();             ...         }     } }   
  • 在此示例代码中,通过调用 GoogleWebAuthorizationBroker.AuthorizeAsync 方法创建了新的 UserCredential 实例。 此静态方法会获取以下内容:

    • 客户端密钥(或指向客户端密钥的流)。
    • 必需的镜重。
    • 用户标识符。
    • 用于取消操作的取消令牌。
    • 可选的数据存储区。如果未指定数据存储区,则默认使用包含默认 Google.Apis.Auth 文件夹的 FileDataStore。 该文件夹是在 Environment.SpecialFolder.ApplicationData 中创建的。
  • 此方法返回的 UserCredential 会在 BooksService 上设置为 HttpClientInitializer(使用初始化程序)。 如前所述,UserCredential 会实现 HTTP 客户端初始化程序

  • 请注意,在示例代码中,客户端密钥信息是从文件加载的,但您也可以执行以下操作:

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

请查看我们的图书示例

Web 应用 (ASP.NET Core 3)

Google API 支持 适用于 Web 服务器应用的 OAuth 2.0

在 ASP.NET Core 3 应用中,建议针对大多数基于 Google 的 OAuth 2.0 场景使用 Google.Apis.Auth.AspNetCore3 库。它实现了 Google 专用的 OpenIdConnect 身份验证处理程序。它支持增量身份验证,并定义了一个可注入的 IGoogleAuthProvider 来提供可与 Google API 搭配使用的 Google 凭据。

本部分介绍了如何配置和使用 Google.Apis.Auth.AspNetCore3。此处显示的代码基于 Google.Apis.Auth.AspNetCore3.IntegrationTests,这是一个完全正常的标准 ASP.NET Core 3 应用。

如果您想按照本文档中的说明操作,请先创建自己的 ASP.NET Core 3 应用,并完成以下步骤。

前提条件

  • 安装 Google.Apis.Auth.AspNetCore3 软件包。
  • 我们使用的是 Google 云端硬盘 API,因此您还需要安装 Google.Apis.Drive.v3 软件包。
  • 如果您还没有 Google Cloud 项目,请创建一个。请按照 这些说明操作。该项目将用于标识您的应用。
  • 请务必启用 Google 云端硬盘 API。如需启用 API,请按照 这些说明操作
  • 创建授权凭据,以向 Google 标识您的应用。按照 这些说明创建授权凭据并下载 client_secrets.json 文件。两大亮点:
    • 请注意,凭据的类型必须为 Web 应用
    • 如需运行此应用,您只需添加 https://localhost:5001/signin-oidc 重定向 URI。

将您的应用配置为使用 Google.Apis.Auth.AspNetCore3

Google.Apis.Auth.AspNetCore3 在 Startup 类或您可能使用的类似替代方案中进行配置。以下代码段摘自 Google.Apis.Auth.AspNetCore3.IntegrationTests 项目中的 Startup.cs

  • 将以下 using 指令添加到 Startup.cs 文件中。
    using Google.Apis.Auth.AspNetCore3;
  • Startup.ConfigureServices 方法中,添加以下代码,将“客户端 ID”和“客户端密钥”占位符替换为 client_secrets.json 文件中包含的值。您可以直接从 JSON 文件加载这些值,也可以通过任何其他安全的方式存储这些值。请参阅 Google.Apis.Auth.AspNetCore3.IntegrationTests 项目中的 ClientInfo.Load 方法,查看如何直接从 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};         }); }       
  • Startup.Configure 方法中,请务必将 ASP.NET Core 3 身份验证和授权中间件组件以及 HTTPS 重定向添加到流水线中:
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env) {     ...     app.UseHttpsRedirection();     ...      app.UseAuthentication();     app.UseAuthorization();      ... }       

使用用户凭据代表用户访问 Google API

现在,您可以向控制器添加操作方法,这些方法需要用户凭据才能代表用户访问 Google API。以下代码段展示了如何列出已验证用户的 Google 云端硬盘账号中的文件。请主要注意以下两点:

  • 用户不仅需要通过身份验证,还需要向您的应用授予 https://www.googleapis.com/auth/drive.readonly 范围(您使用 GoogleScopedAuthorize 属性指定)。
  • 我们使用 ASP.NET Core 3 的标准依赖项注入机制来接收 IGoogleAuthProvider,以便获取用户的凭据。

代码:

  • 首先,将以下 using 指令添加到控制器。
    using Google.Apis.Auth.AspNetCore3; using Google.Apis.Auth.OAuth2; using Google.Apis.Drive.v3; using Google.Apis.Services;       
  • 添加控制器操作,如下所示(并附带接收 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); }       

以上就是基本知识。您可以查看 Google.Apis.Auth.AspNetCore3.IntegrationTests 项目中的 HomeController.cs,了解如何实现以下目标:

  • 仅限用户身份验证,没有特定镜重
  • 用户退出
  • 使用代码进行增量授权。请注意,该示例展示了使用属性进行增量授权。
  • 检查已授予的范围
  • 检查访问令牌和刷新令牌
  • 强制刷新访问令牌。请注意,您无需自行执行此操作,因为 Google.Apis.Auth.AspNetCore3 会检测访问令牌是否已过期或即将过期,并会自动刷新该令牌。

服务账号

Google API 还支持服务账号。 与客户端应用请求访问最终用户数据的情况不同,服务账号提供对客户端应用自身数据的访问权限。

您的客户端应用使用从 Google API 控制台下载的私钥对访问令牌请求进行签名。 创建新的客户端 ID 后,您应选择“服务账号”应用类型,然后才能下载私钥。 请查看我们的 使用 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();         }     } }

此示例会创建一个 ServiceAccountCredential。设置了所需的镜重范围,并调用了 FromCertificate,该调用会从给定的 X509Certificate2 加载私钥。 与所有其他示例代码一样,凭据设置为 HttpClientInitializer