このハウツー ガイドでは、機密クライアント アプリケーションを Azure Active Directory Authentication Library for .NET (ADAL.NET) から .NET のMicrosoft Authentication Library (MSAL.NET) に移行します。 機密クライアント アプリケーションには、独自のサービスを呼び出す Web アプリ、Web API、デーモン アプリケーションが含まれます。 機密アプリの詳細については、「 認証フローとアプリケーションシナリオ」を参照してください。 アプリが ASP.NET Core に基づいている場合は、Microsoft.Identity.Web を参照してください。
アプリの登録の場合:
- 新しいアプリ登録を作成する必要はありません。 (同じクライアント ID を保持します)。
- 事前認証 (管理者が同意した API アクセス許可) を変更する必要はありません。
移行の手順
アプリで ADAL.NET を使用するコードを見つけます。
機密クライアント アプリで ADAL を使用するコードは、 AuthenticationContext をインスタンス化し、次のパラメーターを使用して AcquireTokenByAuthorizationCode または AcquireTokenAsync のオーバーライドのいずれかを呼び出します。
-
resourceId文字列。 この変数は、呼び出す Web API のアプリ ID URI です。
-
IClientAssertionCertificateまたはClientAssertionのインスタンス。 このインスタンスは、アプリの ID を証明するためのクライアント資格情報をアプリに提供します。
ADAL.NET を使用しているアプリがあることを特定したら、MSAL.NET NuGet パッケージ Microsoft.Identity.Client をインストールし、プロジェクトのライブラリ参照を更新します。 詳細については、「 NuGet パッケージのインストール」を参照してください。 トークン キャッシュ シリアライザーを使用するには、Microsoftをインストールします。Identity.Web.TokenCache。
機密クライアントのシナリオに従ってコードを更新します。 一部の手順は一般的であり、すべての機密クライアント シナリオに適用されます。 その他の手順は、各シナリオに固有です。
機密顧客シナリオ:
証明書とキャッシュを処理するために、ADAL.NET のラッパーを提供している可能性があります。 このガイドでは、同じアプローチを使用して、ADAL.NET から MSAL.NET に移行するプロセスを示します。 ただし、このコードはデモンストレーションのみを目的としています。 これらのラッパーをコピー/貼り付けしたり、そのままコードに統合したりしないでください。
デーモン アプリを移行する
デーモン シナリオでは、OAuth2.0 クライアント資格情報フローが使用されます。 サービス間呼び出しとも呼ばれます。 アプリは、ユーザーの代わりにではなく、それ自体に代わってトークンを取得します。
コードでデーモン シナリオが使用されているかどうかを確認する
アプリの ADAL コードには、次のパラメーターを指定した AuthenticationContext.AcquireTokenAsync の呼び出しが含まれている場合、デーモン シナリオが使用されます。
- 最初のパラメーターとしてのリソース (アプリ ID URI)
-
IClientAssertionCertificateまたは 2 番目のパラメーターとしてClientAssertion
AuthenticationContext.AcquireTokenAsync には、 UserAssertion型のパラメーターがありません。 その場合、アプリは Web API であり、 ダウンストリーム Web API を呼び出す Web API シナリオを 使用します。
デーモン シナリオのコードを更新する
コードを更新するための次の手順は、すべての機密クライアント シナリオに適用されます。
- ソース コードに MSAL.NET 名前空間 (
using Microsoft.Identity.Client;) を追加します。
-
AuthenticationContextをインスタンス化する代わりに、ConfidentialClientApplicationBuilder.Createを使用してIConfidentialClientApplicationをインスタンス化します。
-
resourceId文字列の代わりに、MSAL.NET はスコープを使用します。 ADAL.NET を使用するアプリケーションは事前認証されているため、常に次のスコープを使用できます: new string[] { $"{resourceId}/.default" }。
-
AuthenticationContext.AcquireTokenAsyncの呼び出しをIConfidentialClientApplication.AcquireTokenXXXの呼び出しに置き換えます。XXX はシナリオによって異なります。
この場合は、 AuthenticationContext.AcquireTokenAsync の呼び出しを IConfidentialClientApplication.AcquireTokenClientの呼び出しに置き換えます。
デーモン シナリオの ADAL.NET と MSAL.NET コードの比較を次に示します。
using Microsoft.IdentityModel.Clients.ActiveDirectory;
using System.Security.Cryptography.X509Certificates;
using System.Threading.Tasks;
public partial class AuthWrapper
{
const string ClientId = "Guid (AppID)";
const string authority
= "https://login.microsoftonline.com/{tenant}";
// App ID URI of web API to call
const string resourceId = "https://target-api.domain.com";
X509Certificate2 certificate = LoadCertificate();
public async Task<AuthenticationResult> GetAuthenticationResult()
{
var authContext = new AuthenticationContext(authority);
var clientAssertionCert = new ClientAssertionCertificate(
ClientId,
certificate);
var authResult = await authContext.AcquireTokenAsync(
resourceId,
clientAssertionCert,
);
return authResult;
}
}
using Microsoft.Identity.Client;
using System.Security.Cryptography.X509Certificates;
using System.Threading.Tasks;
public partial class AuthWrapper
{
const string ClientId = "Guid (Application ID)";
const string authority
= "https://login.microsoftonline.com/{tenant}";
// App ID URI of web API to call
const string resourceId = "https://target-api.domain.com";
X509Certificate2 certificate = LoadCertificate();
IConfidentialClientApplication app;
public async Task<AuthenticationResult> GetAuthenticationResult()
{
var app = ConfidentialClientApplicationBuilder.Create(ClientId)
.WithCertificate(certificate)
.WithAuthority(authority)
.Build();
// Setup token caching https://learn.microsoft.com/azure/active-directory/develop/msal-net-token-cache-serialization?tabs=aspnet
// For example, for an in-memory cache with 1GB limit, use
app.AddInMemoryTokenCache(services =>
{
// Configure the memory cache options
services.Configure<MemoryCacheOptions>(options =>
{
options.SizeLimit = 1024 * 1024 * 1024; // in bytes (1 GB of memory)
});
}
var authResult = await app.AcquireTokenForClient(
new [] { $"{resourceId}/.default" })
// .WithTenantId(specificTenant)
// See https://aka.ms/msal.net/withTenantId
.ExecuteAsync()
.ConfigureAwait(false);
return authResult;
}
}
トークン キャッシュの利点
トークン キャッシュを設定しない場合、トークン発行者によって調整が行われるので、エラーが発生します。 また、キャッシュからトークンを取得する時間 (10 ~ 20 ミリ秒) は、ESTS (500 ~ 30000 ミリ秒) よりもかなり少なくなります。
分散トークン キャッシュを実装する場合は、 Web アプリまたは Web API (機密クライアント アプリケーション) のトークン キャッシュに関するページを参照してください。
デーモン シナリオの詳細と、新しいアプリケーションでそれを MSAL.NET または Microsoft.Identity.Web を使用して実装する方法について説明します。
ダウンストリーム Web API を呼び出す Web API を移行する
ダウンストリーム Web API を呼び出す Web API は、OAuth2.0 On-Behalf-of (OBO) フローを使用します。 Web API は、HTTP Authorize ヘッダーから取得したアクセス トークンを使用し、このトークンを検証します。 その後、このトークンは、ダウンストリーム Web API を呼び出すためにトークンに対して交換されます。 このトークンは、ADAL.NET と MSAL.NET の両方でUserAssertion インスタンスとして使用されます。
コードで OBO が使用されているかどうかを確認する
アプリの ADAL コードには、次のパラメーターを持つ AuthenticationContext.AcquireTokenAsync の呼び出しが含まれている場合、OBO が使用されます。
- 最初のパラメーターとしてのリソース (アプリ ID URI)
-
IClientAssertionCertificateまたは 2 番目のパラメーターとしてClientAssertion
-
UserAssertion 型のパラメーター
OBO を使用してコードを更新する
コードを更新するための次の手順は、すべての機密クライアント シナリオに適用されます。
- ソース コードに MSAL.NET 名前空間 (
using Microsoft.Identity.Client;) を追加します。
-
AuthenticationContextをインスタンス化する代わりに、ConfidentialClientApplicationBuilder.Createを使用してIConfidentialClientApplicationをインスタンス化します。
-
resourceId文字列の代わりに、MSAL.NET はスコープを使用します。 ADAL.NET を使用するアプリケーションは事前認証されているため、常に次のスコープを使用できます: new string[] { $"{resourceId}/.default" }。
-
AuthenticationContext.AcquireTokenAsyncの呼び出しをIConfidentialClientApplication.AcquireTokenXXXの呼び出しに置き換えます。XXX はシナリオによって異なります。
この場合、 AuthenticationContext.AcquireTokenAsync の呼び出しを IConfidentialClientApplication.AcquireTokenOnBehalfOfの呼び出しに置き換えます。
ADAL.NET と MSAL.NET のサンプル OBO コードの比較を次に示します。
using Microsoft.IdentityModel.Clients.ActiveDirectory;
using System.Security.Cryptography.X509Certificates;
using System.Threading.Tasks;
public partial class AuthWrapper
{
const string ClientId = "Guid (AppID)";
const string authority
= "https://login.microsoftonline.com/common";
X509Certificate2 certificate = LoadCertificate();
public async Task<AuthenticationResult> GetAuthenticationResult(
string resourceId,
string tokenUsedToCallTheWebApi)
{
var authContext = new AuthenticationContext(authority);
var clientAssertionCert = new ClientAssertionCertificate(
ClientId,
certificate);
var userAssertion = new UserAssertion(tokenUsedToCallTheWebApi);
var authResult = await authContext.AcquireTokenAsync(
resourceId,
clientAssertionCert,
userAssertion,
);
return authResult;
}
}
using Microsoft.Identity.Client;
using System.Security.Cryptography.X509Certificates;
using System.Threading.Tasks;
public partial class AuthWrapper
{
const string ClientId = "Guid (Application ID)";
const string authority
= "https://login.microsoftonline.com/common";
X509Certificate2 certificate = LoadCertificate();
IConfidentialClientApplication app;
public async Task<AuthenticationResult> GetAuthenticationResult(
string resourceId,
string tokenUsedToCallTheWebApi)
{
var app = ConfidentialClientApplicationBuilder.Create(ClientId)
.WithCertificate(certificate)
.WithAuthority(authority)
.Build();
// Setup token caching https://learn.microsoft.com/azure/active-directory/develop/msal-net-token-cache-serialization?tabs=aspnet
// For example, for an in-memory cache with 1GB limit. For OBO, it is recommended to use a distributed cache like Redis.
app.AddInMemoryTokenCache(services =>
{
// Configure the memory cache options
services.Configure<MemoryCacheOptions>(options =>
{
options.SizeLimit = 1024 * 1024 * 1024; // in bytes (1 GB of memory)
});
}
var userAssertion = new UserAssertion(tokenUsedToCallTheWebApi);
var authResult = await app.AcquireTokenOnBehalfOf(
new string[] { $"{resourceId}/.default" },
userAssertion)
// .WithTenantId(specificTenant)
// See https://aka.ms/msal.net/withTenantId
.ExecuteAsync()
.ConfigureAwait(false);
return authResult;
}
}
トークン キャッシュの利点
OBO でのトークン キャッシュの場合は、分散トークン キャッシュを使用します。 詳細については、 Web アプリまたは Web API (機密クライアント アプリ) のトークン キャッシュに関するページを参照してください。
app.UseInMemoryTokenCaches(); // or a distributed token cache.
ダウンストリーム Web API を呼び出す Web API について詳しくは、こちらをご覧ください。また、新しいアプリで MSAL.NET または Microsoft.Identity.Web を使用してそれらを実装する方法についても説明します。
Web API を呼び出す Web アプリを移行する
アプリで ASP.NET Coreを使用している場合は、Microsoftに更新することを強くお勧めします。Identity.Web は、すべてを自動的に処理するためです。 手短な紹介については、Microsoft.Identity.Web の一般提供開始のお知らせをご覧ください。 Web アプリでの使用方法の詳細については、Web アプリで Microsoft.Identity.Web を使用する理由を参照してください。
ユーザーをサインインさせ、ユーザーに代わって Web API を呼び出す Web アプリは、OAuth2.0 承認コード フローを使用します。 通常:
- アプリは、Microsoft ID プラットフォーム承認エンドポイントに移動して、承認コード フローの第 1 段階を実行してユーザーをサインインさせます。 ユーザーはサインインし、必要に応じて多要素認証を実行します。 この操作の結果として、アプリは承認コードを受け取ります。 この段階では、認証ライブラリは使用されません。
- アプリは、承認コード フローの第 2 区間を実行します。 認証コードを使用して、アクセス トークン、ID トークン、および更新トークンを取得します。 アプリケーションでは、
redirectUri値 (Microsoft ID プラットフォーム エンドポイントがセキュリティ トークンを提供する URI) を指定する必要があります。 アプリはその URI を受け取った後、通常、ADAL または MSAL の AcquireTokenByAuthorizationCode を呼び出してコードを引き換え、トークン キャッシュに格納されるトークンを取得します。
- アプリは ADAL または MSAL を使用して
AcquireTokenSilent を呼び出し、Web アプリ コントローラーから必要な Web API を呼び出すためのトークンを取得します。
コードで認証コード フローが使用されているかどうかを確認する
アプリの ADAL コードには、 AuthenticationContext.AcquireTokenByAuthorizationCodeAsyncの呼び出しが含まれている場合、認証コード フローが使用されます。
承認コード フローを使用してコードを更新する
コードを更新するための次の手順は、すべての機密クライアント シナリオに適用されます。
- ソース コードに MSAL.NET 名前空間 (
using Microsoft.Identity.Client;) を追加します。
-
AuthenticationContextをインスタンス化する代わりに、ConfidentialClientApplicationBuilder.Createを使用してIConfidentialClientApplicationをインスタンス化します。
-
resourceId文字列の代わりに、MSAL.NET はスコープを使用します。 ADAL.NET を使用するアプリケーションは事前認証されているため、常に次のスコープを使用できます: new string[] { $"{resourceId}/.default" }。
-
AuthenticationContext.AcquireTokenAsyncの呼び出しをIConfidentialClientApplication.AcquireTokenXXXの呼び出しに置き換えます。XXX はシナリオによって異なります。
この場合は、 AuthenticationContext.AcquireTokenAsync の呼び出しを IConfidentialClientApplication.AcquireTokenByAuthorizationCodeの呼び出しに置き換えます。
ADAL.NET と MSAL.NET のサンプル承認コード フローの比較を次に示します。
using Microsoft.IdentityModel.Clients.ActiveDirectory;
using System.Security.Cryptography.X509Certificates;
using System.Threading.Tasks;
public partial class AuthWrapper
{
const string ClientId = "Guid (AppID)";
const string authority
= "https://login.microsoftonline.com/common";
private Uri redirectUri = new Uri("host/login_oidc");
X509Certificate2 certificate = LoadCertificate();
public async Task<AuthenticationResult> GetAuthenticationResult(
string resourceId,
string authorizationCode)
{
var ac = new AuthenticationContext(authority);
var clientAssertionCert = new ClientAssertionCertificate(
ClientId,
certificate);
var authResult = await ac.AcquireTokenByAuthorizationCodeAsync(
authorizationCode,
redirectUri,
clientAssertionCert,
resourceId,
);
return authResult;
}
}
using Microsoft.Identity.Client;
using Microsoft.Identity.Web;
using System;
using System.Security.Claims;
using System.Security.Cryptography.X509Certificates;
using System.Threading.Tasks;
public partial class AuthWrapper
{
const string ClientId = "Guid (Application ID)";
const string authority
= "https://login.microsoftonline.com/{tenant}";
private Uri redirectUri = new Uri("host/login_oidc");
X509Certificate2 certificate = LoadCertificate();
public IConfidentialClientApplication CreateApplication()
{
IConfidentialClientApplication app;
app = ConfidentialClientApplicationBuilder.Create(ClientId)
.WithCertificate(certificate)
.WithAuthority(authority)
.WithRedirectUri(redirectUri.ToString())
.WithLegacyCacheCompatibility(false)
.Build();
// Add a token cache. For details about other serialization
// see https://aka.ms/msal-net-cca-token-cache-serialization
app.AddInMemoryTokenCache();
return app;
}
// Called from 'code received event'.
public async Task<AuthenticationResult> GetAuthenticationResult(
string resourceId,
string authorizationCode)
{
IConfidentialClientApplication app = CreateApplication();
var authResult = await app.AcquireTokenByAuthorizationCode(
new[] { $"{resourceId}/.default" },
authorizationCode)
.ExecuteAsync()
.ConfigureAwait(false);
return authResult;
}
}
AcquireTokenByAuthorizationCodeを呼び出すと、承認コードの受信時にトークンがトークン キャッシュに追加されます。 他のリソースまたはテナントの追加トークンを取得するには、コントローラーで AcquireTokenSilent を使用します。
public partial class AuthWrapper
{
// Called from controllers
public async Task<AuthenticationResult> GetAuthenticationResult(
string resourceId2,
string authority)
{
IConfidentialClientApplication app = CreateApplication();
AuthenticationResult authResult;
var scopes = new[] { $"{resourceId2}/.default" };
var account = await app.GetAccountAsync(ClaimsPrincipal.Current.GetMsalAccountId());
try
{
// try to get an already cached token
authResult = await app.AcquireTokenSilent(
scopes,
account)
// .WithTenantId(specificTenantId)
// See https://aka.ms/msal.net/withTenantId
.ExecuteAsync().ConfigureAwait(false);
}
catch (MsalUiRequiredException)
{
// The controller will need to challenge the user
// including asking for claims={ex.Claims}
throw;
}
return authResult;
}
}
トークン キャッシュの利点
Web アプリでは AcquireTokenByAuthorizationCodeを使用するため、トークン キャッシュには分散トークン キャッシュを使用する必要があります。 詳細については、 Web アプリまたは Web API のトークン キャッシュに関するページを参照してください。
app.UseInMemoryTokenCaches(); // or a distributed token cache.
MsalUiRequiredException の処理
コントローラーがさまざまなスコープ/リソースに対してサイレント モードでトークンを取得しようとすると、ユーザーが再サインインする必要がある場合、またはリソースへのアクセスに (条件付きアクセス ポリシーが原因で) より多くの要求が必要な場合、MSAL.NET は期待どおりにMsalUiRequiredExceptionをスローする可能性があります。 軽減策の詳細については、MSAL.NET でエラーと例外を処理する方法を参照してください。
Web API を呼び出す Web アプリについてさらに詳しく学び、新しいアプリケーションでそれらが MSAL.NET または Microsoft.Identity.Web を使用してどのように実装されるかを確認してください。
MSAL の利点
アプリの MSAL.NET の主な利点は次のとおりです。
回復力。 MSAL.NET は、次の方法でアプリの回復性を高めます。
- Microsoft Entra ID キャッシュ資格情報サービス (CCS) のメリット CCS は、Microsoft Entra バックアップとして動作します。
- 呼び出した API が 継続的なアクセス評価を通じて有効期間の長いトークンを有効にする場合、トークンのプロアクティブな更新。
セキュリティ。 呼び出す Web API で必要な場合は、所有証明 (PoP) トークンを取得できます。 詳細については、MSAL.NET の所有証明トークンを参照してください。
パフォーマンスとスケーラビリティ。 キャッシュを ADAL.NET と共有する必要がない場合は、機密クライアント アプリケーション (.WithLegacyCacheCompatibility(false)) を作成するときにレガシ キャッシュの互換性を無効にして、パフォーマンスを大幅に向上させます。
app = ConfidentialClientApplicationBuilder.Create(ClientId)
.WithCertificate(certificate)
.WithAuthority(authority)
.WithLegacyCacheCompatibility(false)
.Build();
Troubleshooting
MsalServiceException
次のトラブルシューティング情報では、2 つの前提条件があります。
- ADAL.NET コードが動作していました。
- 同じクライアント ID を保持して MSAL に移行しました。
次のいずれかのメッセージで例外が発生した場合:
AADSTS700027: Client assertion contains an invalid signature. [Reason - The key was not found.]
AADSTS90002: Tenant 'aaaabbbb-0000-cccc-1111-dddd2222eeee' not found. This may happen if there are no active
subscriptions for the tenant. Check to make sure you have the correct tenant ID. Check with your subscription
administrator.
次の手順を使用して例外のトラブルシューティングを行います。
- 最新バージョンの MSAL.NET を使用していることを確認します。
- 機密クライアント アプリを構築するときに設定した機関ホストと、ADAL で使用した機関ホストが類似していることを確認します。 特に、それは同じクラウド(Azure Government、21Vianet が運営する Microsoft Azure、または Azure Germany)ですか。
MsalClientException
マルチテナント アプリでは、Web API を呼び出すときのユーザーのテナントなど、特定のテナントを対象とするアプリを構築する際に共通の権限を指定します。 MSAL.NET 4.37.0 以降、アプリの作成時に.WithAzureRegionを指定すると、トークン要求中に.WithAuthorityを使用して機関を指定できなくなります。 その場合、以前のバージョンの MSAL.NET から更新すると、次のエラーが発生します。
MsalClientException - "You configured WithAuthority at the request level, and also WithAzureRegion. This is not supported when the environment changes from application to request. Use WithTenantId at the request level instead."
この問題を修復するには、AcquireTokenXXX 式の .WithAuthority を .WithTenantIdに置き換えます。 GUID またはドメイン名を使用してテナントを指定します。
次のステップ
詳細については、以下をご覧ください。