OAuth 2.0

このドキュメントでは、OAuth 2.0 について、その用途、クライアント ID の取得方法、.NET 用 Google API クライアント ライブラリとの使用方法について説明します。

OAuth 2.0 プロトコル

OAuth 2.0 は、Google API で使用される認証プロトコルです。プロトコルについては、次のリンクをご覧ください。

クライアント ID とシークレットの取得

クライアント ID とシークレットは Google API Console で取得できます。クライアント ID にはさまざまなタイプがあるので、ご使用のアプリケーションに適したタイプを取得してください。

以下の各コード スニペット(サービス アカウントを除く)で、クライアント シークレットをダウンロードして、client_secrets.json としてプロジェクトに保存する必要があります。

認証情報

ユーザー認証情報

UserCredential は、アクセス トークンを使用して保護されたリソースにアクセスするための、スレッドセーフなヘルパークラスです。アクセス トークンは通常 1 時間で期限切れとなります。10 日を過ぎるとアクセス トークンはエラーになります。

UserCredentialAuthorizationCodeFlow は自動的にトークンを更新します。トークンは、新しいアクセス トークンを取得するだけです。有効期限の長い更新トークンを使用して認証を行います。このトークンは、認証コードフローで access_type=offline パラメータを使用すると、アクセス トークンとともに受信します。

ほとんどのアプリケーションでは、認証情報のアクセス トークンと更新トークンを永続ストレージに保存することをおすすめします。 それ以外の場合は、アクセス トークンを 1 時間後に有効期限が切れるため、エンドユーザーに 1 時間ごとにブラウザに承認ページを提示する必要があります。

アクセス トークンと更新トークンが確実に保持されるように、IDataStore の独自の実装を指定するか、ライブラリが提供する次のいずれかの実装を使用できます。

  • .NET の FileDataStore を使用すると、認証情報がファイルに永続的に保存されます。

ServiceAccountCredential

ServiceAccountCredentialUserCredential に似ていますが、異なる目的を果たします。Google OAuth 2.0 は、ウェブ アプリケーションと Google Cloud Storage の間など、サーバー間のインタラクションをサポートしています。リクエスト元のアプリケーションが API にアクセスするには、自身の ID を証明する必要があります。エンドユーザーの関与は必要ありません。 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 インスタンスを作成しています。この静的メソッドは次のように取得されます。

    • クライアント シークレット(またはクライアント シークレットへのストリーム)。
    • 必要なスコープ。
    • ユーザー識別子。
    • オペレーションをキャンセルするためのキャンセル トークン。
    • オプションのデータストア。データストアが指定されていない場合、デフォルトは FileDataStore であり、デフォルトの Google.Apis.Auth フォルダがあります。 フォルダは Environment.SpecialFolder.ApplicationData に作成されます。
  • このメソッドで返される UserCredential は、BooksServiceHttpClientInitializer として設定されます(イニシャライザを使用)。前述のように、UserCredentialHTTP クライアント イニシャライザを実装します。

  • 上記のサンプルコードでは、クライアント シークレット情報がファイルから読み込まれますが、次のようにすることもできます。

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

書籍のサンプルをご覧ください。

ウェブ アプリケーション(ASP.NET Core 3)

Google API は、ウェブサーバー アプリケーション用 OAuth 2.0 をサポートしています。

Google.Apis.Auth.AspNetCore3 は、ASP.NET Core 3 アプリケーションのほとんどの Google ベースの OAuth 2.0 シナリオでの使用が推奨されるライブラリです。Google 固有の OpenIdConnect auth ハンドラが実装されます。段階的認証をサポートし、Google API で使用できる Google 認証情報を提供するための挿入可能な IGoogleAuthProvider を定義します。

このセクションでは、Google.Apis.Auth.AspNetCore3 の構成方法と使用方法について説明します。ここに示すコードは、Google.Apis.Auth.AspNetCore3.IntegrationTests に基づいています。これは完全に機能する標準の ASP.NET Core 3 アプリケーションです。

このチュートリアルをチュートリアルとして使用する場合は、独自の ASP.NET Core 3 アプリケーションが必要です。また、この手順を完了するための手順を行う必要があります。

Prerequisites

  • Google.Apis.Auth.AspNetCore3 パッケージをインストールします。
  • Google Drive API を使用しているため、Google.Apis.Drive.v3 パッケージもインストールする必要があります。
  • Google Cloud プロジェクトをまだ作成していない場合は作成します。こちらの手順に沿って削除してください。アプリが識別されるプロジェクトになります。
  • 必ず Google Drive API を有効にしてください。API を有効にするには、こちらの手順を行ってください。
  • Google に対してアプリを識別するための認証情報を作成します。こちらの手順に沿って認証情報を作成し、client_secrets.json ファイルをダウンロードします。2 つのハイライトは次のとおりです。
    • 認証情報は [Web application] である必要があります。
    • このアプリを実行する場合、追加する必要があるリダイレクト URI は https://localhost:5001/signin-oidc だけです。

Google.Apis.Auth.AspNetCore3 を使用するようにアプリケーションを構成する

Google.Apis.Auth.AspNetCore3 は Startup クラス、または使用している同様の代替手段で構成されます。次のスニペットは、Google.Apis.Auth.AspNetCore3.IntegrationTests プロジェクトの Startup.cs から抽出されます。

  • Startup.cs ファイルに、次のディレクティブを使用して追加します。
    using Google.Apis.Auth.AspNetCore3;
  • Startup.ConfigureServices メソッドに次のコードを追加します。client_secrets.json ファイルに含まれる値を使用して、クライアント ID とクライアント シークレットのプレースホルダを変更します。これらの値は、JSON ファイルから直接読み込むことも、他の安全な方法で保存することもできます。JSON ファイルからこれらの値を直接読み込む例については、Google.Apis.Auth.AspNetCore3.IntegrationTests プロジェクトの ClientInfo.Load メソッドをご覧ください。
    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 ドライブ アカウントのファイルをリストする方法を示しています。主に次の 2 つの点に注意してください。

  • ユーザーは認証されるだけでなく、https://www.googleapis.com/auth/drive.readonly スコープをアプリに付与する必要があります。これは、GoogleScopedAuthorize 属性で指定されます。
  • ASP.NET Core 3 の標準依存関係挿入メカニズムを使用して、ユーザーの認証情報を取得する IGoogleAuthProvider を受信します。

コード:

  • まず、コントローラを使用して、次のディレクティブを使用して追加します。
    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 はアクセス トークンが期限切れか、期限切れ間近であることを検出し、自動的に更新するため、ご自身で行う必要はありません。

ウェブ アプリケーション(ASP.NET MVC)

Google API は、ウェブサーバー アプリケーション用 OAuth 2.0 をサポートしています。次のコードを正常に実行するには、まず Google API Console でプロジェクトにリダイレクト URI を追加する必要があります。FlowMetadata とそのデフォルト設定を使用するため、リダイレクト URI を your_site/AuthCallback/IndexAsync に設定します。

OAuth 2.0 認証情報のリダイレクト URI を確認する方法は次のとおりです。

  1. API コンソールで [認証情報] ページを開きます。
  2. OAuth 2.0 認証情報をまだ作成していない場合は、[認証情報を作成 & OAuth クライアント ID] をクリックして作成します。
  3. 認証情報を作成したら、[OAuth 2.0 クライアント ID] セクションのクライアント ID(ウェブ アプリケーション用)をクリックして、リダイレクト URL を表示または編集します。

IDE で新しいウェブ アプリケーション プロジェクトを作成したら、ドライブYouTube など、使用するサービスに適した Google.Apis NuGet パッケージを追加します。次に、Google.Apis.Auth.MVC パッケージを追加します。次のコードは、Google API サービスにクエリを実行する ASP.NET MVC アプリケーションを示しています。

  1. 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 は、ユーザー ID と使用している IAuthorizationCodeFlow を取得するための独自のロジックを含む抽象クラスです。

    上記のサンプルコードでは、適切なスコープ、クライアント シークレット、データストアを使用して、新しい GoogleAuthorizationCodeFlow を作成しています。IDataStore の独自の実装を追加することを検討してください。たとえば、EntityFramework を使用する実装を記述できます。

  2. Google API サービスを使用する独自のコントローラを実装します。 次のサンプルでは、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. 独自のコールバック コントローラを実装します。実装は次のようになります。
    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(); }
            }
        }
    }
          

サービス アカウント

Google API はサービス アカウントもサポートしています。クライアント アプリケーションがエンドユーザーのデータへのアクセスをリクエストするシナリオとは異なり、サービス アカウントはクライアント アプリケーション独自のデータへのアクセスを提供します。

クライアント アプリケーションが、Google API Console からダウンロードした秘密鍵を使用して、アクセス トークンのリクエストに署名します。新しいクライアント 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 として設定されます。