MSAL.NET でのトークン キャッシュのシリアル化

Microsoft Authentication Library (MSAL) では、トークンの取得後に、トークンがキャッシュされます。 パブリック クライアント アプリケーション (デスクトップおよびモバイル アプリ) では、別の方法でトークンを取得する前に、キャッシュからトークンを取得する必要があります。 機密クライアント アプリケーションの取得方法によって、キャッシュ自体が管理されます。 この記事では、MSAL.NET でのトークン キャッシュの既定のシリアル化とカスタムのシリアル化について説明します。

簡単な概要

次のようにすることが推奨されます。

  • デスクトップ アプリケーションを作成する場合は、「デスクトップ アプリ」で説明されているように、クロスプラットフォーム トークン キャッシュを使用します。
  • モバイル アプリと UWP アプリに対しては何もしません。 MSAL.NET では、キャッシュ用にセキュリティで保護されたストレージが提供されます。
  • ASP.NET Core の Web アプリWeb API で、上位レベル API として Microsoft.Identity.Web を使用します。 トークン キャッシュなどを取得できます。 ASP.NET Core Web アプリと Web API に関する記事をご覧ください。
  • Web アプリWeb API のその他のケースでは、次のようにします。
    • 実稼働アプリケーションでユーザー向けのトークンを要求する場合は、分散トークン キャッシュ (Redis、SQL Server、Azure Cosmos DB、分散メモリ) を使用します。 Microsoft.Identity.Web.TokenCache で使用できるトークン キャッシュ シリアライザーを使用します。
    • それ以外の場合、メモリ内キャッシュを使用するには、次のようにします。
      • AcquireTokenForClient のみを使用している場合は、機密クライアント アプリケーション インスタンスを再利用してシリアライザーを追加しないか、または新しい機密クライアント アプリケーションを作成してAcquireTokenForClientを有効にします。

        共有キャッシュはシリアル化されないため、より高速です。 ただし、トークンがキャッシュされるとメモリが増加します。 トークンの数は、テナントの数にダウンストリーム API の数を掛けたものと同じです。 アプリ トークンのサイズは約 2 KB であるのに対し、ユーザーのトークンのサイズは約 7 KB です。 開発に適しています。また、ユーザー数が少ない場合にも適しています。

      • メモリ内のトークン キャッシュを使用し、かつそのサイズと削除のポリシーを制御する場合は、Microsoft.Identity.Web メモリ内キャッシュ オプションを使用します。

  • SDK をビルドし、機密クライアント アプリケーション用に独自のトークン キャッシュ シリアライザーを記述する場合は、Microsoft.Identity.Web.MsalAsbtractTokenCacheProvider から継承し、ReadCacheBytesAsync メソッドをオーバーライドします。

Microsoft.Identity.Web ライブラリ内の Microsoft.Identity.Web.TokenCache NuGet パッケージでは、トークン キャッシュのシリアル化が提供されます。

拡張メソッド Description
AddInMemoryTokenCaches トークンの格納と取得のために、メモリ内に一時キャッシュを作成します。 メモリ内トークン キャッシュは他の種類のキャッシュよりも高速ですが、それらのトークンはアプリケーションの再起動時に保持されないため、キャッシュ サイズを制御することはできません。 メモリ内キャッシュは、アプリの再起動間でトークンを保持する必要がないアプリケーションに適しています。 AcquireTokenForClient (クライアント資格情報の付与) を使用するサービスやデーモンなど、マシン間の認証シナリオに参加するアプリでは、メモリ内トークン キャッシュを使用します。 メモリ内トークン キャッシュは、サンプル アプリケーションや、ローカルアプリの開発にも適しています。 Microsoft.Identity.Web バージョン 1.19.0 以降では、すべてのアプリケーション インスタンスでメモリ内トークン キャッシュを共有します。
AddSessionTokenCaches トークン キャッシュは、ユーザー セッションにバインドされています。 このオプションは、ID トークンに多くの要求が含まれている場合は、Cookie が大きくなりすぎるため適していません。
AddDistributedTokenCaches トークンキャッシュは、ASP.NET Core IDistributedCache の実装に対するアダプターです。 このため、分散メモリ キャッシュ、Redis キャッシュ、分散 NCache、または SQL Server キャッシュから選択できます。 IDistributedCache の実装の詳細については、「IDistributedCache」を参照してください。

メモリ内トークン キャッシュ

ASP.NET Core アプリケーション内の Startup クラスの ConfigureServices メソッドでメモリ内キャッシュを使用するコードの例を次に示します。

#using Microsoft.Identity.Web
using Microsoft.Identity.Web;

public class Startup
{
 const string scopesToRequest = "user.read";
  
  public void ConfigureServices(IServiceCollection services)
  {
   // code before
   services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
           .AddMicrosoftIdentityWebApp(Configuration)
             .EnableTokenAcquisitionToCallDownstreamApi(new string[] { scopesToRequest })
                .AddInMemoryTokenCaches();
   // code after
  }
  // code after
}

アプリ専用トークンを要求する場合、運用環境では AddInMemoryTokenCaches が適しています。 ユーザー トークンを使用する場合は、分散トークン キャッシュの使用を検討してください。

ASP.NET Core Web アプリと Web API では、トークン キャッシュ構成コードは類似しています。

分散トークン キャッシュ

可能な分散キャッシュの例を次に示します。

// or use a distributed Token Cache by adding
   services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
           .AddMicrosoftIdentityWebApp(Configuration)
             .EnableTokenAcquisitionToCallDownstreamApi(new string[] { scopesToRequest }
               .AddDistributedTokenCaches();

// Distributed token caches have a L1/L2 mechanism.
// L1 is in memory, and L2 is the distributed cache
// implementation that you will choose below.
// You can configure them to limit the memory of the 
// L1 cache, encrypt, and set eviction policies.
services.Configure<MsalDistributedTokenCacheAdapterOptions>(options => 
  {
    // Optional: Disable the L1 cache in apps that don't use session affinity
    //                 by setting DisableL1Cache to 'false'.
    options.DisableL1Cache = false;
    
    // Or limit the memory (by default, this is 500 MB)
    options.sizeLimit = 1024 * 1024 * 1024, // 1 GB

    // You can choose if you encrypt or not encrypt the cache
    options.Encrypt = false;

    // And you can set eviction policies for the distributed
    // cache.
    options.SlidingExpiration = TimeSpan.FromHours(1);
  }

// Then, choose your implementation of distributed cache
// -----------------------------------------------------

// For instance, the distributed in-memory cache (not cleared when you stop the app)
services.AddDistributedMemoryCache();

// Or a Redis cache
// Requires the Microsoft.Extensions.Caching.StackExchangeRedis NuGet package
services.AddStackExchangeRedisCache(options =>
{
 options.Configuration = "localhost";
 options.InstanceName = "SampleInstance";
});

// You can even decide if you want to repair the connection
// with Redis and retry on Redis failures. 
services.Configure<MsalDistributedTokenCacheAdapterOptions>(options => 
{
  options.OnL2CacheFailure = (ex) =>
  {
    if (ex is StackExchange.Redis.RedisConnectionException)
    {
      // action: try to reconnect or something
      return true; //try to do the cache operation again
    }
    return false;
  };
});

// Or even a SQL Server token cache
// Requires the Microsoft.Extensions.Caching.SqlServer NuGet package
services.AddDistributedSqlServerCache(options =>
{
 options.ConnectionString = _config["DistCache_ConnectionString"];
 options.SchemaName = "dbo";
 options.TableName = "TestCache";
});

// Or an Azure Cosmos DB cache
// Requires the Microsoft.Extensions.Caching.Cosmos NuGet package
services.AddCosmosCache((CosmosCacheOptions cacheOptions) =>
{
    cacheOptions.ContainerName = Configuration["CosmosCacheContainer"];
    cacheOptions.DatabaseName = Configuration["CosmosCacheDatabase"];
    cacheOptions.ClientBuilder = new CosmosClientBuilder(Configuration["CosmosConnectionString"]);
    cacheOptions.CreateIfNotExists = true;
});

詳細については、次を参照してください。

分散キャッシュの使用法は、フェーズ 2-2 トークン キャッシュASP.NET Core Web アプリのチュートリアルで説明しています。

キャッシュ ヒット率とキャッシュ パフォーマンスの監視

MSAL は、AuthenticationResult.AuthenticationResultMetadata オブジェクトの一部として、重要なメトリックを公開しています。 これらのメトリックをログに記録して、ご自分のアプリケーションの正常性を評価できます。

メトリック 意味 アラームをトリガーするタイミング
DurationTotalInMs MSAL で費やした合計時間 (ネットワーク呼び出しとキャッシュを含む)。 全体の待機時間が長い場合のアラーム (> 1 秒)。 値はトークン ソースによって異なります。 キャッシュから: 1 回のキャッシュ アクセス。 Azure Active Directory (Azure AD) から: 2 回のキャッシュ アクセス + 1 回の HTTP 呼び出し。 1 回目 (プロセスごと) の呼び出しでは、1 回分の HTTP 呼び出しが追加で発生するため、時間がかかります。
DurationInCacheInMs アプリ開発者によってカスタマイズされた、トークン キャッシュの読み込みまたは保存に要した時間 (Redis に保存するなど)。 スパイク時のアラーム。
DurationInHttpInMs Azure AD への HTTP 呼び出しに要した時間。 スパイク時のアラーム。
TokenSource トークンのソース。 キャッシュからのトークンの取得が格段に速くなります (例: ~ 700 ms に対して ~ 100 ms)。 キャッシュ ヒット率の監視とアラーム生成に使用できます。 DurationTotalInMs で使用します。
CacheRefreshReason ID プロバイダーからアクセス トークンをフェッチする理由。 TokenSource で使用します。

次のステップ

次のサンプルでは、トークン キャッシュのシリアル化を示します。

サンプル プラットフォーム 説明
[active-directory-dotnet-desktop-msgraph-v2](https://github.com/azure-samples/active-directory-dotnet-desktop-msgraph-v2) デスクトップ (WPF) Microsoft Graph API を呼び出す Windows デスクトップ .NET (WPF) アプリケーション。 Diagram that shows a topology with a desktop app client flowing to Azure Active Directory by acquiring a token interactively and to Microsoft Graph.
[active-directory-dotnet-v1-to-v2](https://github.com/Azure-Samples/active-directory-dotnet-v1-to-v2) デスクトップ (コンソール) Azure AD v1.0 アプリケーション (ADAL.NET を使用) から Microsoft ID プラットフォーム アプリケーション (MSAL.NET を使用) への移行を示す一連の Visual Studio ソリューション。 特に、トークン キャッシュの移行に関するページと、機密クライアントのトークン キャッシュに関するページを参照してください。
[ms-identity-aspnet-webapp-openidconnect](https://github.com/Azure-Samples/ms-identity-aspnet-webapp-openidconnect) ASP.NET (net472) ASP.NET MVC アプリケーションでのトークン キャッシュのシリアル化の例 (MSAL.NET を使用)。 特に、MsalAppBuilder に関するページを参照してください。