MSAL.NET を Microsoft.Identity.Web と統合する .NET Framework

このガイドでは、.NET Framework、.NET Standard 2.0、およびクラシック .NET アプリケーション (.NET 4.7.2 以降) で、MSAL.NET と共に Microsoft.Identity.Web のトークン キャッシュおよび証明書パッケージを使用する方法を説明します。

概要を理解する

Microsoft.Identity.Web 1.17+ 以降では、非 ASP.NET Core 環境で MSAL.NET と共に Microsoft.Identity.Web ユーティリティ パッケージを使用できます。

パッケージの利点を特定する

特徴 給付金
トークン キャッシュのシリアル化 メモリ内、SQL Server、Redis、Cosmos DB、PostgreSQL 用の再利用可能なキャッシュ アダプター
証明書ヘルパー KeyVault、ファイル システム、または証明書ストアからの証明書の読み込みの簡略化
クレーム拡張 ClaimsPrincipal操作のためのユーティリティ メソッド
.NET Standard 2.0 .NET Framework 4.7.2 以降、.NET Core、.NET 5 以降と互換性があります
最小依存関係 ASP.NET Core依存関係のない対象パッケージ

サポートされているシナリオを確認する

次のシナリオは、対象となるユーティリティ パッケージでサポートされています。

  • .NET Framework コンソール アプリケーション (デーモン シナリオ)
  • Desktop Applications (.NET Framework)
  • Worker Services (.NET Framework)
  • .NET Standard 2.0 ライブラリ (クロスプラットフォーム互換性)
  • Non-web MSAL.NET アプリケーション

ASP.NET MVC/Web API アプリケーションについては、代わりに OWIN 統合を参照してください。


パッケージの選択

シナリオに一致するパッケージを選択します。

MSAL.NET のコア パッケージを特定する

パッケージ Purpose 依存関係 .NET ターゲット
Microsoft。Identity.Web.TokenCache トークン キャッシュ シリアライザー、 ClaimsPrincipal 拡張機能 最小限 .NET Standard 2.0
Microsoft.Identity.Web.Certificate 証明書の読み込みユーティリティ 最小限 .NET Standard 2.0

パッケージをインストールする

プロジェクトにパッケージを追加するには、次のいずれかの方法を使用します。

パッケージ マネージャー Console:

# Token cache serialization
Install-Package Microsoft.Identity.Web.TokenCache

# Certificate management
Install-Package Microsoft.Identity.Web.Certificate

.NET CLI:

dotnet add package Microsoft.Identity.Web.TokenCache
dotnet add package Microsoft.Identity.Web.Certificate

コア パッケージの制限事項について

コア Microsoft.Identity.Web パッケージには、次のような依存関係 ASP.NET Core (Microsoft.AspNetCore.*) が含まれています。

  • ASP.NET Framework と互換性がありません
  • パッケージ サイズを不必要に増やす
  • 依存関係の競合を引き起こす

.NET Framework および .NET Standard のシナリオではターゲット パッケージを使用してください。


トークン キャッシュのシリアル化を構成する

トークン キャッシュ アダプターについて

Microsoft。Identity.Web は、MSAL.NET の IConfidentialClientApplication とシームレスに連携するトークン キャッシュ アダプターを提供します。

トークン キャッシュを使用して機密クライアントを構築する

次の例では、機密クライアント アプリケーションを作成し、メモリ内トークン キャッシュをアタッチします。

using Microsoft.Identity.Client;
using Microsoft.Identity.Web;
using Microsoft.Identity.Web.TokenCacheProviders;

public class MsalAppBuilder
{
    private static IConfidentialClientApplication _app;

    public static IConfidentialClientApplication BuildConfidentialClientApplication()
    {
        if (_app == null)
        {
            string clientId = ConfigurationManager.AppSettings["AzureAd:ClientId"];
            string clientSecret = ConfigurationManager.AppSettings["AzureAd:ClientSecret"];
            string tenantId = ConfigurationManager.AppSettings["AzureAd:TenantId"];

            // Create the confidential client application
            _app = ConfidentialClientApplicationBuilder.Create(clientId)
                .WithClientSecret(clientSecret)
                .WithTenantId(tenantId)
                .WithAuthority(AzureCloudInstance.AzurePublic, tenantId)
                .Build();

            // Add token cache serialization (choose one option below)
            _app.AddInMemoryTokenCache();
        }

        return _app;
    }
}

トークン キャッシュ オプションの選択

デプロイ シナリオに最適なキャッシュ プロバイダーを選択します。

インメモリ トークン キャッシュを構成する

次の例では、単純なメモリ内キャッシュを追加します。

using Microsoft.Identity.Web.TokenCacheProviders;

_app.AddInMemoryTokenCache();

サイズ制限付きメモリ キャッシュ (Microsoft。Identity.Web 1.20 以降):

using Microsoft.Extensions.Caching.Memory;

_app.AddInMemoryTokenCache(services =>
{
    // Configure memory cache options
    services.Configure<MemoryCacheOptions>(options =>
    {
        options.SizeLimit = 5000000;  // 5 MB limit
    });
});

特性:

  • 高速アクセス
  • 外部依存関係なし
  • プロセス間で共有されない
  • アプリの再起動時に失われた

ユース ケース: 単一インスタンスコンソール アプリ、デスクトップ アプリケーション


分散インメモリ トークン キャッシュを構成する

マルチインスタンス環境用の分散メモリ内キャッシュを追加するには、次のコードを使用します。

_app.AddDistributedTokenCaches(services =>
{
    // Requires: Microsoft.Extensions.Caching.Memory (NuGet)
    services.AddDistributedMemoryCache();
});

特性:

  • アプリ インスタンス間で共有
  • 負荷分散シナリオに適しています
  • 追加の NuGet パッケージが必要
  • アプリの再起動時に引き続き失われる

ユース ケース: 受け入れ可能なトークンの再取得を使用するマルチインスタンス サービス


SQL Server のトークン キャッシュを構成する

永続的な分散SQL Server キャッシュを追加するには、次のコードを使用します。

using Microsoft.Extensions.Caching.SqlServer;

_app.AddDistributedTokenCaches(services =>
{
    // Requires: Microsoft.Extensions.Caching.SqlServer (NuGet)
    services.AddDistributedSqlServerCache(options =>
    {
        options.ConnectionString = ConfigurationManager.ConnectionStrings["TokenCache"].ConnectionString;
        options.SchemaName = "dbo";
        options.TableName = "TokenCache";

        // IMPORTANT: Set expiration above token lifetime
        // Access tokens typically expire after 1 hour
        options.DefaultSlidingExpiration = TimeSpan.FromMinutes(90);
    });
});

次の SQL を実行して、必要なキャッシュ テーブルを作成します。

-- Create the cache table
CREATE TABLE [dbo].[TokenCache] (
    [Id] NVARCHAR(449) NOT NULL,
    [Value] VARBINARY(MAX) NOT NULL,
    [ExpiresAtTime] DATETIMEOFFSET NOT NULL,
    [SlidingExpirationInSeconds] BIGINT NULL,
    [AbsoluteExpiration] DATETIMEOFFSET NULL,
    PRIMARY KEY ([Id])
);

-- Create index for performance
CREATE INDEX [Index_ExpiresAtTime] ON [dbo].[TokenCache] ([ExpiresAtTime]);

特性:

  • 再起動後も永続的
  • 複数のインスタンス間で共有
  • 信頼性と拡張性
  • SQL Serverセットアップが必要

ユース ケース: 運用デーモン サービス、スケジュールされたタスク、マルチインスタンス ワーカー


Redis トークン キャッシュを構成する

次のコードを使用して、高パフォーマンスの Redis 分散キャッシュを追加します。

using StackExchange.Redis;
using Microsoft.Extensions.Caching.StackExchangeRedis;

_app.AddDistributedTokenCaches(services =>
{
    // Requires: Microsoft.Extensions.Caching.StackExchangeRedis (NuGet)
    services.AddStackExchangeRedisCache(options =>
    {
        options.Configuration = ConfigurationManager.AppSettings["Redis:ConnectionString"];
        options.InstanceName = "TokenCache_";
    });
});

次の例は、運用対応の Redis 構成を示しています。

services.AddStackExchangeRedisCache(options =>
{
    options.Configuration = ConfigurationManager.AppSettings["Redis:ConnectionString"];
    options.InstanceName = "MyDaemonApp_";

    // Optional: Configure Redis options
    options.ConfigurationOptions = new ConfigurationOptions
    {
        AbortOnConnectFail = false,
        ConnectTimeout = 5000,
        SyncTimeout = 5000
    };
});

特性:

  • 非常に高速
  • インスタンス間で共有
  • 永続的 (Redis 永続化が有効になっている場合)
  • Redis サーバーが必要

ユース ケース: 大量のデーモン アプリ、分散システム、マイクロサービス


Cosmos DB トークン キャッシュを構成する

グローバル分散 Cosmos DB キャッシュを追加するには、次のコードを使用します。

using Microsoft.Extensions.Caching.Cosmos;

_app.AddDistributedTokenCaches(services =>
{
    // Requires: Microsoft.Extensions.Caching.Cosmos (preview)
    services.AddCosmosCache(options =>
    {
        options.ContainerName = "TokenCache";
        options.DatabaseName = "IdentityCache";
        options.ClientBuilder = new CosmosClientBuilder(
            ConfigurationManager.AppSettings["CosmosConnectionString"]);
        options.CreateIfNotExists = true;
    });
});

特性:

  • グローバル分散
  • 高可用性
  • 自動スケーリング
  • Redis よりも待機時間が長い
  • コストの増加

ユース ケース: グローバル デーモン サービス、geo 分散アプリケーション


PostgreSQL トークン キャッシュを構成する

分散 PostgreSQL キャッシュを追加するには、次のコードを使用します。

_app.AddDistributedTokenCaches(services =>
{
    // Requires: Microsoft.Extensions.Caching.Postgres (NuGet)
    services.AddDistributedPostgresCache(options =>
    {
        options.ConnectionString = ConfigurationManager.ConnectionStrings["PostgresCache"].ConnectionString;
        options.SchemaName = ConfigurationManager.AppSettings["PostgresCache:SchemaName"];
        options.TableName = ConfigurationManager.AppSettings["PostgresCache:TableName"];
        options.CreateIfNotExists = bool.Parse(
            ConfigurationManager.AppSettings["PostgresCache:CreateIfNotExists"] ?? "true");

        // Set expiration above token lifetime.
        // Access tokens typically expire after 1 hour.
        options.DefaultSlidingExpiration = TimeSpan.FromMinutes(90);
    });
});

特性:

  • 再起動後も永続的
  • 複数のインスタンス間で共有
  • 使い慣れた SQL セマンティクス
  • Azure Database for PostgreSQLで動作します
  • PostgreSQL サーバーが必要です

ケース:アプリケーションはプライマリ データベースとして PostgreSQL を既に使用しているか、Azure Database for PostgreSQLを使用してAzureホストされているサービスを使用します


完全なデーモン アプリケーションを構築する

次の例は、クライアント資格情報とSQL Server トークン キャッシュを使用してトークンを取得する完全なデーモン アプリケーションを示しています。

using Microsoft.Identity.Client;
using Microsoft.Identity.Web;
using Microsoft.Identity.Web.TokenCacheProviders;
using System;
using System.Threading.Tasks;

namespace DaemonApp
{
    class Program
    {
        private static IConfidentialClientApplication _app;

        static async Task Main(string[] args)
        {
            // Build confidential client with token cache
            _app = BuildConfidentialClient();

            // Acquire token for app-only access
            string[] scopes = new[] { "https://graph.microsoft.com/.default" };

            try
            {
                var result = await _app.AcquireTokenForClient(scopes)
                    .ExecuteAsync();

                Console.WriteLine($"Token acquired successfully!");
                Console.WriteLine($"Token source: {result.AuthenticationResultMetadata.TokenSource}");
                Console.WriteLine($"Expires on: {result.ExpiresOn}");

                // Use token to call API
                await CallProtectedApi(result.AccessToken);
            }
            catch (MsalServiceException ex)
            {
                Console.WriteLine($"Error acquiring token: {ex.ErrorCode}");
                Console.WriteLine($"CorrelationId: {ex.CorrelationId}");
            }
        }

        private static IConfidentialClientApplication BuildConfidentialClient()
        {
            var app = ConfidentialClientApplicationBuilder
                .Create(ConfigurationManager.AppSettings["ClientId"])
                .WithClientSecret(ConfigurationManager.AppSettings["ClientSecret"])
                .WithTenantId(ConfigurationManager.AppSettings["TenantId"])
                .Build();

            // Add SQL Server token cache for persistence
            app.AddDistributedTokenCaches(services =>
            {
                services.AddDistributedSqlServerCache(options =>
                {
                    options.ConnectionString = ConfigurationManager
                        .ConnectionStrings["TokenCache"].ConnectionString;
                    options.SchemaName = "dbo";
                    options.TableName = "TokenCache";
                    options.DefaultSlidingExpiration = TimeSpan.FromMinutes(90);
                });
            });

            return app;
        }

        private static async Task CallProtectedApi(string accessToken)
        {
            // Your API call logic
        }
    }
}

証明書の管理

証明書の読み込みについて

Microsoft。Identity.Web は、クライアント資格情報フローのさまざまなソースからの証明書の読み込みを簡略化します。

DefaultCertificateLoader を使用して証明書を読み込む

次の例では、Azure Key Vaultから証明書を読み込み、機密クライアント アプリケーションを作成する方法を示します。

using Microsoft.Identity.Web;
using Microsoft.Identity.Client;

public class CertificateHelper
{
    public static IConfidentialClientApplication CreateAppWithCertificate()
    {
        string clientId = ConfigurationManager.AppSettings["AzureAd:ClientId"];
        string tenantId = ConfigurationManager.AppSettings["AzureAd:TenantId"];

        // Define certificate source
        var certDescription = CertificateDescription.FromKeyVault(
            keyVaultUrl: "https://my-keyvault.vault.azure.net",
            keyVaultCertificateName: "MyCertificate"
        );

        // Load certificate
        ICertificateLoader certificateLoader = new DefaultCertificateLoader();
        certificateLoader.LoadIfNeeded(certDescription);

        // Create confidential client with certificate
        var app = ConfidentialClientApplicationBuilder.Create(clientId)
            .WithCertificate(certDescription.Certificate)
            .WithTenantId(tenantId)
            .Build();

        // Add token cache
        app.AddInMemoryTokenCache();

        return app;
    }
}

証明書ソースの選択

Azure Key Vaultからの読み込み

コンテナーの URL と証明書名を指定して、Azure Key Vaultに格納されている証明書を読み込みます。

var certDescription = CertificateDescription.FromKeyVault(
    keyVaultUrl: "https://my-keyvault.vault.azure.net",
    keyVaultCertificateName: "MyApplicationCert"
);

ICertificateLoader loader = new DefaultCertificateLoader();
loader.LoadIfNeeded(certDescription);

var app = ConfidentialClientApplicationBuilder.Create(clientId)
    .WithCertificate(certDescription.Certificate)
    .WithTenantId(tenantId)
    .Build();

前提条件:

  • Key Vault アクセス権を持つマネージド ID またはサービス プリンシパル
  • Azure.Identity NuGet パッケージ
  • Key Vaultのアクセス許可: 証明書に対するGet

証明書ストアからの読み込み

識別名を使用して、Windows証明書ストアから証明書を読み込みます。

var certDescription = CertificateDescription.FromStoreWithDistinguishedName(
    distinguishedName: "CN=MyApp.contoso.com",
    storeName: StoreName.My,
    storeLocation: StoreLocation.CurrentUser
);

ICertificateLoader loader = new DefaultCertificateLoader();
loader.LoadIfNeeded(certDescription);

var app = ConfidentialClientApplicationBuilder.Create(clientId)
    .WithCertificate(certDescription.Certificate)
    .WithTenantId(tenantId)
    .Build();

拇印で証明書を見つけることもできます。

var certDescription = CertificateDescription.FromStoreWithThumbprint(
    thumbprint: "ABCDEF1234567890ABCDEF1234567890ABCDEF12",
    storeName: StoreName.My,
    storeLocation: StoreLocation.LocalMachine
);

ファイル システムからの読み込み

ローカル ファイル システム上の PFX ファイルから証明書を読み込みます。

var certDescription = CertificateDescription.FromPath(
    path: @"C:\Certificates\MyAppCert.pfx",
    password: ConfigurationManager.AppSettings["Certificate:Password"]
);

ICertificateLoader loader = new DefaultCertificateLoader();
loader.LoadIfNeeded(certDescription);

var app = ConfidentialClientApplicationBuilder.Create(clientId)
    .WithCertificate(certDescription.Certificate)
    .WithTenantId(tenantId)
    .Build();

セキュリティに関する注意: パスワードをハードコーディングしないでください。 セキュリティで保護された構成を使用します。


Base64 でエンコードされた文字列から読み込む

構成に格納されている Base64 でエンコードされた文字列から証明書を読み込みます。

string base64Cert = ConfigurationManager.AppSettings["Certificate:Base64"];

var certDescription = CertificateDescription.FromBase64Encoded(
    base64EncodedValue: base64Cert,
    password: ConfigurationManager.AppSettings["Certificate:Password"]  // Optional
);

ICertificateLoader loader = new DefaultCertificateLoader();
loader.LoadIfNeeded(certDescription);

App.config からの証明書の読み込みを構成する

App.config ファイルで証明書の設定を定義し、実行時に読み込みます。

App.config:

<appSettings>
  <add key="AzureAd:ClientId" value="your-client-id" />
  <add key="AzureAd:TenantId" value="your-tenant-id" />

  <!-- Option 1: KeyVault -->
  <add key="Certificate:SourceType" value="KeyVault" />
  <add key="Certificate:KeyVaultUrl" value="https://my-vault.vault.azure.net" />
  <add key="Certificate:KeyVaultCertificateName" value="MyCert" />

  <!-- Option 2: Store -->
  <!--
  <add key="Certificate:SourceType" value="StoreWithThumbprint" />
  <add key="Certificate:CertificateThumbprint" value="ABCD..." />
  <add key="Certificate:CertificateStorePath" value="CurrentUser/My" />
  -->
</appSettings>

<connectionStrings>
  <add name="TokenCache"
       connectionString="Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=TokenCache;Integrated Security=True;" />
</connectionStrings>

構成に基づいて証明書を読み込むには、次のヘルパー メソッドを使用します。

public static CertificateDescription GetCertificateFromConfig()
{
    string sourceType = ConfigurationManager.AppSettings["Certificate:SourceType"];

    return sourceType switch
    {
        "KeyVault" => CertificateDescription.FromKeyVault(
            ConfigurationManager.AppSettings["Certificate:KeyVaultUrl"],
            ConfigurationManager.AppSettings["Certificate:KeyVaultCertificateName"]
        ),

        "StoreWithThumbprint" => CertificateDescription.FromStoreWithThumbprint(
            ConfigurationManager.AppSettings["Certificate:CertificateThumbprint"],
            StoreName.My,
            StoreLocation.CurrentUser
        ),

        _ => throw new ConfigurationErrorsException("Invalid certificate source type")
    };
}

サンプル アプリケーションを調べる

これらのサンプルを確認して、動作する実装を確認します。

公式のMicrosoftサンプルを確認する

次の表に、トークン キャッシュと証明書の読み込みを示す公式サンプルを示します。

サンプル プラットフォーム 説明
ConfidentialClientTokenCache コンソール (.NET Framework) トークン キャッシュのシリアル化パターン
active-directory-dotnetcore-daemon-v2 コンソール (.NET Core) Key Vaultからの証明書の読み込み

ベスト プラクティスに従う

これらのパターンを適用して、信頼性の高いセキュリティで保護されたアプリケーションを構築します。

1. IConfidentialClientApplication にシングルトン パターンを使用します。

1 つのインスタンスを作成し、アプリケーション全体で再利用します。

private static IConfidentialClientApplication _app;

public static IConfidentialClientApplication GetApp()
{
    if (_app == null)
    {
        _app = ConfidentialClientApplicationBuilder.Create(clientId)
            .WithClientSecret(clientSecret)
            .WithTenantId(tenantId)
            .Build();

        _app.AddDistributedTokenCaches(/* ... */);
    }

    return _app;
}

2. 適切なトークン キャッシュの有効期限を設定します。

不要な再取得を防ぐために、トークンの有効期間を超えるスライディング有効期限を構成します。

// Access tokens typically expire after 1 hour
// Set cache expiration ABOVE token lifetime
options.DefaultSlidingExpiration = TimeSpan.FromMinutes(90);

3. セキュリティで保護された証明書ストレージを使用します。

証明書をAzure Key Vaultまたは適切にセキュリティで保護された証明書ストアに格納します。

// Azure Key Vault (production)
var cert = CertificateDescription.FromKeyVault(keyVaultUrl, certName);

// Certificate store with proper permissions
var cert = CertificateDescription.FromStoreWithThumbprint(
    thumbprint, StoreName.My, StoreLocation.LocalMachine);

4. 適切なエラー処理を実装します。

MSAL 例外をキャッチし、トラブルシューティングのために関連付け ID をログに記録します。

try
{
    var result = await app.AcquireTokenForClient(scopes).ExecuteAsync();
}
catch (MsalServiceException ex)
{
    logger.Error($"Token acquisition failed. CorrelationId: {ex.CorrelationId}, ErrorCode: {ex.ErrorCode}");
    throw;
}

5. 運用には分散キャッシュを使用します。

分散キャッシュは、インスタンス間でトークンを共有し、再起動後も保持されます。

// Correct for daemon services
app.AddDistributedTokenCaches(services =>
{
    services.AddDistributedSqlServerCache(/* ... */);
});

一般的な間違いを避ける

1. 新しい IConfidentialClientApplication インスタンスを繰り返し作成しないでください。

// Wrong - creates new instance every time
public void AcquireToken()
{
    var app = ConfidentialClientApplicationBuilder.Create(clientId).Build();
    // ...
}

// Correct - use singleton
private static readonly IConfidentialClientApplication _app = BuildApp();

2. シークレットをハードコーディングしないでください。

// Wrong
.WithClientSecret("supersecretvalue123")

// Correct
.WithClientSecret(ConfigurationManager.AppSettings["AzureAd:ClientSecret"])

3. マルチインスタンス サービスにはメモリ内キャッシュを使用しないでください。

// Wrong for services with multiple instances
app.AddInMemoryTokenCache();

// Correct - use distributed cache
app.AddDistributedTokenCaches(services =>
{
    services.AddDistributedSqlServerCache(/* ... */);
});

4. 証明書の検証を無視しないでください。

// Wrong - skips validation
ServicePointManager.ServerCertificateValidationCallback = (sender, cert, chain, errors) => true;

// Correct - validate certificates properly

ADAL.NET からの移行

主な違いを確認し、MSAL.NET を Microsoft.Identity.Web とともに使用するようコードを更新してください。

主な違いを理解する

特徴 ADAL.NET (非推奨) MSAL.NET + Microsoft。Identity.Web
スコープ リソースベース (https://graph.microsoft.com) スコープベース (https://graph.microsoft.com/.default)
トークン キャッシュ 手動によるシリアル化が必要 拡張メソッドを使用した組み込みアダプター
証明書 X509Certificate2 の手動読み込み DefaultCertificateLoader 複数のソースがある場合
機関 建設時に固定 要求ごとにオーバーライドできます

移行の例を比較する

ADAL.NET (Old):

AuthenticationContext authContext = new AuthenticationContext(authority);
ClientCredential credential = new ClientCredential(clientId, clientSecret);
AuthenticationResult result = await authContext.AcquireTokenAsync(resource, credential);

MSAL.NET とMicrosoft。Identity.Web (新規):

var app = ConfidentialClientApplicationBuilder.Create(clientId)
    .WithClientSecret(clientSecret)
    .WithTenantId(tenantId)
    .Build();

app.AddInMemoryTokenCache();  // Add token cache

string[] scopes = new[] { "https://graph.microsoft.com/.default" };
AuthenticationResult result = await app.AcquireTokenForClient(scopes).ExecuteAsync();

これらのリソースを使用して、関連するシナリオの詳細を確認してください。