メモ
これは、この記事の最新バージョンではありません。 現在のリリースについては、 この記事の .NET 9 バージョンを参照してください。
重要
この情報はリリース前の製品に関する事項であり、正式版がリリースされるまでに大幅に変更される可能性があります。 Microsoft はここに示されている情報について、明示か黙示かを問わず、一切保証しません。
現在のリリースについては、 この記事の .NET 9 バージョンを参照してください。
この記事では、Blazor Web AppGitHub リポジトリ (.NET 8 以降) のサンプル アプリを使用してdotnet/blazor-samples
でをセキュリティで保護する方法 (ダウンロード方法) について説明します。
Microsoft Entra ID または Azure AD B2C の場合は、適切な既定値で OIDC とAddMicrosoftIdentityWebApp認証ハンドラーの両方を追加する Microsoft Identity Web (Microsoft.Identity.Web
NuGet パッケージ、API ドキュメント) のCookieを使用できます。 サンプル アプリとこの記事のガイダンスでは、Microsoft Identity Web は使用されません。 このガイダンスでは、任意の OIDC プロバイダー用の OIDC ハンドラーを "手動で" 構成する方法を示します。 Microsoft Identity Web の実装の詳細については、「Microsoft Entra ID を使用して ASP.NET Core Blazor Web App をセキュリティで保護する」を参照してください。
このバージョンの記事では、グローバル インタラクティブ自動レンダリング (サーバーおよび プロジェクト) を採用するアプリで .Client
パターンを採用せずに OIDC を実装する方法について説明します。 BFF パターンは、外部サービスに対して認証された要求を行う場合に役立ちます。 アプリの仕様で BFF パターンの採用が必要な場合は、記事のバージョン セレクターを BFF パターン に変更します。
次の仕様が採用されています。
- Blazor Web Appでは、グローバル対話機能を備えた自動レンダリング モードが使用されます。
- ユーザーの認証状態をキャプチャし、それをサーバーとクライアントの間でフローするために、サーバーおよびクライアント アプリによってカスタム認証状態プロバイダー サービスが使用されます。
- このアプリは、すべての OIDC 認証フローの開始点です。 OIDC はアプリで手動で構成され、 Microsoft Entra ID または Microsoft Identity Web パッケージに依存しません。また、サンプル アプリでは Microsoft Azure ホスティングが必要ありません。 ただし、サンプル アプリは Entra や Microsoft Identity Web で使用でき、Azure でホストできます。
- 非対話形式でのトークンの自動更新。
- 別の Web API プロジェクトでは、気象データのセキュリティで保護された Web API 呼び出しを示します。
サンプル ソリューション
サンプル アプリは、次のプロジェクトで構成されます。
-
BlazorWebAppOidc
: 気象データのBlazor Web App エンドポイントの例を含む、のサーバー側プロジェクト。 -
BlazorWebAppOidc.Client
: Blazor Web App のクライアント側プロジェクト。 -
MinimalApiJwt
: 気象データ用の 最小限の API エンドポイントを含むバックエンド Web API。
次のリンクを使用して、Blazor サンプル リポジトリの最新バージョン フォルダーからサンプルにアクセスします。 サンプルは、.NET 8 以降の BlazorWebAppOidc
フォルダーにあります。
Aspire/Aspire.AppHost
プロジェクトからソリューションを開始します。
サンプル コードを表示またはダウンロードする (ダウンロード方法)
ソリューションの機能の例:
カスタム cookie リフレッシャー (
CookieOidcRefresher.cs
) を使用する非対話形式でのトークンの自動更新。気象データは、
/weather-forecast
プロジェクトのProgram
ファイル (Program.cs
) 内の最小 API エンドポイント (MinimalApiJwt
) によって処理されます。 このエンドポイントは、RequireAuthorization を呼び出すことで認可を要求します。 プロジェクトに追加するすべてのコントローラーに対して、[Authorize]
属性 をコントローラーまたはアクションに追加します。 承認ポリシーを使用してアプリ全体で承認を要求し、パブリック エンドポイントのサブセットで承認をオプトアウトする方法の詳細については、Razor Pages OIDC ガイダンスを参照してください。このアプリは、気象データ用の Web API を安全に呼び出します。
-
Weather
コンポーネントをサーバーでレンダリングする場合、コンポーネントはサーバー上のServerWeatherForecaster
を使用して、MinimalApiJwt
から要求にアクセス トークンをアタッチするDelegatingHandler (TokenHandler
) を使用して、HttpContext プロジェクトの Web API から気象データを取得します。 - コンポーネントがクライアントにレンダリングされると、コンポーネントは
ClientWeatherForecaster
サービス実装を使用します。この実装では、構成済みの HttpClient (クライアント プロジェクトのProgram
ファイル内) を使用して、サーバー プロジェクトのServerWeatherForecaster
から Web API 呼び出しを行います。
-
- サーバー プロジェクトは AddAuthenticationStateSerialization を呼び出して、 PersistentComponentState を使用して認証状態をクライアントにフローするサーバー側認証状態プロバイダーを追加します。 クライアントは AddAuthenticationStateDeserialization を呼び出して、サーバーによって渡された認証状態を逆シリアル化して使用します。 認証状態は、WebAssembly アプリケーションの有効期間中は変わりません。
-
PersistingAuthenticationStateProvider
クラス (PersistingAuthenticationStateProvider.cs
) は、AuthenticationStateProvider を使用してクライアントに認証状態をフローするサーバー側 PersistentComponentState であり、WebAssembly アプリケーションの有効期間中は固定されます。
Blazor Web Appのサービス抽象化を使用した (Web) API 呼び出しの詳細については、「ASP.NET Core Blazor アプリからの Web API の呼び出し」を参照してください。
Microsoft Entra ID アプリの登録
アプリと Web API が同じソリューション内にある場合でも、アプリと Web API には個別の登録を使用することをお勧めします。 次のガイダンスは、サンプル ソリューションの BlazorWebAppOidc
アプリと MinimalApiJwt
Web API を対象としていますが、一般に、アプリと Web API の Entra ベースの登録にも同じガイダンスが適用されます。
最初に Web API (MinimalApiJwt
) を登録して、アプリの登録時に Web API へのアクセスを許可できるようにします。 Web API のテナント ID とクライアント ID は、 Program
ファイルで Web API を構成するために使用されます。 Web API を登録した後、アプリの登録で Web API を公開します>スコープ名がの Weather.Get
します。 アプリの構成で使用するアプリ ID URI を記録します。
次に、BlazorWebAppOidc
プラットフォーム構成と/のBlazorWebApOidc.Client
(ポートは必要ありません) にアプリ (https://localhost/signin-oidc
) を登録します。 アプリのテナント ID とクライアント ID、および Web API のベース アドレス、アプリ ID URI、天気スコープ名は、 Program
ファイルでアプリを構成するために使用されます。
アプリの登録>API のアクセス許可で Web API へのアクセス許可を付与します。 アプリのセキュリティ仕様で呼び出される場合は、組織が Web API にアクセスするための管理者の同意を付与できます。 承認されたユーザーとグループは、アプリ登録および>でアプリの登録に割り当てられます。
Entra または Azure portal の 暗黙的な許可とハイブリッド フロー のアプリ登録構成で、 アクセス トークン または ID トークンを返す承認エンドポイントのチェック ボックスをオンにしないでください。 OpenID Connect ハンドラーは、承認エンドポイントから返されたコードを使用して、適切なトークンを自動的に要求します。
Entra または Azure ポータルのアプリ登録で、管理>証明書とシークレット>新しいクライアント シークレットを作成します。 次のセクションで使用するには、クライアント シークレット の値 を保持します。
特定の設定に関する追加の Entra 構成ガイダンスについては、この記事の後半で説明します。
クライアント シークレットを確立する
このセクションは、 Blazor Web App (BlazorWebAppOidc
プロジェクト) のサーバー プロジェクトにのみ適用されます。
警告
アプリ シークレット、接続文字列、資格情報、パスワード、個人識別番号 (PIN)、プライベート C#/.NET コード、秘密キー/トークンをクライアント側コードに格納しないでください。これは 常に安全ではありません。 テスト/ステージング環境と運用環境では、サーバー側の Blazor コードと Web API は、プロジェクト コードまたは構成ファイル内で資格情報を維持しないように、セキュリティで保護された認証フローを使用する必要があります。 ローカル開発テスト以外では、環境変数が最も安全なアプローチではないため、環境変数を使用して機密データを格納しないようにすることをお勧めします。 ローカル開発テストでは、機密データをセキュリティで保護するために Secret Manager ツール を使用することをお勧めします。 詳細については、「 機密データと資格情報を安全に管理する」を参照してください。
ローカル開発テストでは、 Secret Manager ツール を使用して、 Blazor サーバー プロジェクトのクライアント シークレットを構成キー Authentication:Schemes:MicrosoftOidc:ClientSecret
の下に格納します。
Blazor サーバー プロジェクトは、Secret Manager ツール用に初期化されていません。 Visual Studio の Developer PowerShell コマンド シェルなどのコマンド シェルを使用して、次のコマンドを実行します。 コマンドを実行する前に、 cd
コマンドを使用してサーバー プロジェクトのディレクトリにディレクトリを変更します。 このコマンドは、ユーザー シークレット識別子 (サーバー アプリのプロジェクト ファイル内の<UserSecretsId>
) を確立します。
dotnet user-secrets init
次のコマンドを実行して、クライアント シークレットを設定します。
{SECRET}
プレースホルダーは、アプリの登録から取得したクライアント シークレットです。
dotnet user-secrets set "Authentication:Schemes:MicrosoftOidc:ClientSecret" "{SECRET}"
Visual Studio を使用している場合は、 ソリューション エクスプローラー でサーバー プロジェクトを右クリックし、[ユーザー シークレットの管理] を選択することで 、シークレットが設定されていることを確認できます。
MinimalApiJwt
プロジェクト
MinimalApiJwt
プロジェクトは、複数のフロントエンド プロジェクト用のバックエンド Web API です。 このプロジェクトでは、気象データ用の 最小限の API エンドポイントを構成します。
MinimalApiJwt.http
ファイルは、気象データの要求のテストに使用できます。 エンドポイントをテストするには MinimalApiJwt
プロジェクトが実行されている必要があり、エンドポイントはファイルにハードコーディングされていることに注意してください。 詳細については、「 Visual Studio 2022 での .http ファイルの使用」を参照してください。
プロジェクトには、 開発環境で OpenAPI ドキュメント と Swagger UI を生成するためのパッケージと構成が含まれています。 詳細については、「 生成された OpenAPI ドキュメントを使用する」を参照してください。
このプロジェクトでは、気象データ用の 最小 API エンドポイントが作成されます。
app.MapGet("/weather-forecast", () =>
{
var forecast = Enumerable.Range(1, 5).Select(index =>
new WeatherForecast
(
DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
Random.Shared.Next(-20, 55),
summaries[Random.Shared.Next(summaries.Length)]
))
.ToArray();
return forecast;
}).RequireAuthorization();
プロジェクトの JwtBearerOptions ファイル内の AddJwtBearer 呼び出しの Program
でプロジェクトを構成します。
Authorityは、OIDC 呼び出しを行う権限を設定します。
MinimalApiJwt
プロジェクトには別のアプリ登録を使用することをお勧めします。 この権限は、ID プロバイダーによって返される JWT の issurer (iss
) と一致します。
jwtOptions.Authority = "{AUTHORITY}";
機関の形式は、使用中のテナントの種類によって異なります。 Microsoft Entra ID の次の例では、 aaaabbbb-0000-cccc-1111-dddd2222eeee
のテナント ID を使用します。
ME-ID テナント権限の例:
jwtOptions.Authority = "https://sts.windows.net/aaaabbbb-0000-cccc-1111-dddd2222eeee/";
AAD B2C テナントオーソリティの例:
jwtOptions.Authority = "https://login.microsoftonline.com/aaaabbbb-0000-cccc-1111-dddd2222eeee/v2.0/";
Audienceは、受信した OIDC トークンの対象ユーザーを設定します。
jwtOptions.Audience = "{APP ID URI}";
メモ
Microsoft Entra ID を使用する場合は、[Entra または Azure portal で API を公開する] でWeather.Get
スコープを追加するときに構成されたアプリケーション ID URI のパスのみに値を一致させます。 値にスコープ名 "Weather.Get
" を含めないでください。
対象ユーザーの形式は、使用中のテナントの種類によって異なります。 Microsoft Entra ID の次の例では、 contoso
のテナント ID と 11112222-bbbb-3333-cccc-4444dddd5555
のクライアント ID を使用します。
ME-ID テナントのアプリ ID URI の例:
jwtOptions.Audience = "api://11112222-bbbb-3333-cccc-4444dddd5555";
AAD B2C テナントアプリ ID URI の例:
jwtOptions.Audience = "https://contoso.onmicrosoft.com/11112222-bbbb-3333-cccc-4444dddd5555";
Blazor Web App サーバー プロジェクト (BlazorWebAppOidc
)
BlazorWebAppOidc
プロジェクトは、Blazor Web App のサーバー側プロジェクトです。
DelegatingHandler (TokenHandler
) は、送信要求へのユーザーのアクセス トークンのアタッチを管理します。 トークン ハンドラーは、静的サーバー側レンダリング (静的 SSR) 中にのみ実行されるため、このシナリオでは HttpContext を使用しても安全です。 詳細については、ASP.NET Core Blazor アプリの IHttpContextAccessor/HttpContext、およびコア サーバー側の ASP.NET および追加のセキュリティ シナリオBlazor Web App参照してください。
TokenHandler.cs
:
public class TokenHandler(IHttpContextAccessor httpContextAccessor) :
DelegatingHandler
{
protected override async Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken)
{
if (httpContextAccessor.HttpContext is null)
{
throw new Exception("HttpContext not available");
}
var accessToken = await httpContextAccessor.HttpContext
.GetTokenAsync("access_token");
request.Headers.Authorization =
new AuthenticationHeaderValue("Bearer", accessToken);
return await base.SendAsync(request, cancellationToken);
}
}
プロジェクトのProgram
ファイルでは、トークン ハンドラー (TokenHandler
) がサービスとして登録され、名前AddHttpMessageHandler ("MinimalApiJwt
") を使用してバックエンド Web API に安全な要求を行うExternalApi
を持つメッセージ ハンドラーとして指定されます。
builder.Services.AddScoped<TokenHandler>();
builder.Services.AddHttpClient("ExternalApi",
client => client.BaseAddress = new Uri(builder.Configuration["ExternalApiUri"] ??
throw new Exception("Missing base address!")))
.AddHttpMessageHandler<TokenHandler>();
プロジェクトの appsettings.json
ファイルで、外部 API URI を構成します。
"ExternalApiUri": "{BASE ADDRESS}"
例:
"ExternalApiUri": "https://localhost:7277"
次の OpenIdConnectOptions の構成は、プロジェクトの Program
ファイルに含まれる AddOpenIdConnect の呼び出しにあります。
PushedAuthorizationBehavior: プッシュされた承認要求 (PAR) のサポートを制御します。 既定では、ID プロバイダーの検出ドキュメント (通常は .well-known/openid-configuration
にあります) が PAR のサポートをアドバタイズする場合は、PAR を使用します。 アプリの PAR サポートを必要とする場合は、 PushedAuthorizationBehavior.Require
の値を割り当てることができます。 PAR は Microsoft Entra ではサポートされておらず、Entra が今後サポートする予定はありません。
oidcOptions.PushedAuthorizationBehavior = PushedAuthorizationBehavior.UseIfAvailable;
SignInScheme: 認証が成功した後にユーザーの ID を保持するミドルウェアに対応する認証スキームを設定します。 OIDC ハンドラーでは、複数の要求にわたってユーザー資格情報を保持できるサインイン スキームを使用する必要があります。 次の行は、デモンストレーションの目的のためだけに存在します。 省略すると、DefaultSignInScheme がフォールバック値として使用されます。
oidcOptions.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
openid
と profile
のスコープ (Scope) (省略可能): openid
と profile
スコープも、OIDC ハンドラーが機能するために必要なので既定で構成されますが、スコープが Authentication:Schemes:MicrosoftOidc:Scope
の構成に含まれている場合は、これらを追加し直すことが必要になる場合があります。 一般的な構成ガイダンスについては、ASP.NET Core の構成 と ASP.NET Core Blazor 構成 を参照してください。
oidcOptions.Scope.Add(OpenIdConnectScope.OpenIdProfile);
SaveTokens: 認可が成功した後で、アクセス トークンと更新トークンを AuthenticationProperties 内に保存する必要があるかどうかを定義します。 このプロパティは true
に設定されているため、非インタラクティブ トークン更新用に更新トークンが格納されます。
oidcOptions.SaveTokens = true;
オフライン アクセスのスコープ (Scope): offline_access
スコープが更新トークンのために必要です。
oidcOptions.Scope.Add(OpenIdConnectScope.OfflineAccess);
Authority と ClientId: OIDC 呼び出しのためのオーソリティとクライアント ID を設定します。
oidcOptions.Authority = "{AUTHORITY}";
oidcOptions.ClientId = "{CLIENT ID}";
次の例では、 aaaabbbb-0000-cccc-1111-dddd2222eeee
のテナント ID と 00001111-aaaa-2222-bbbb-3333cccc4444
のクライアント ID を使用します。
oidcOptions.Authority = "https://login.microsoftonline.com/aaaabbbb-0000-cccc-1111-dddd2222eeee/v2.0/";
oidcOptions.ClientId = "00001111-aaaa-2222-bbbb-3333cccc4444";
マルチテナント アプリの場合は、"共通" 機関を使用する必要があります。 "common" オーソリティをシングルテナント アプリに対して使用することもできますが、このセクションで後ほど示すように、カスタムの IssuerValidator が必要になります。
oidcOptions.Authority = "https://login.microsoftonline.com/common/v2.0/";
ResponseType: 認可コード フローのみを実行するように、OIDC ハンドラーを構成します。 このモードでは、暗黙的な許可とハイブリッド フローは必要ありません。 OIDC ハンドラーは、承認エンドポイントから返されたコードを使用して適切なトークンを自動的に要求します。
oidcOptions.ResponseType = OpenIdConnectResponseType.Code;
MapInboundClaims および NameClaimType と RoleClaimType の構成: 多くの OIDC サーバーは、name
の既定値 SOAP/WS-Fed ではなく、"role
" と "ClaimTypes" を使用します。
MapInboundClaims が false
に設定されていると、ハンドラーは要求のマッピングを実行せず、JWT からの要求名がアプリによって直接使用されます。 次の例では、ロール要求の種類を "roles
" に設定します。これは Microsoft Entra ID (ME-ID) に適しています。 詳細情報は、ID プロバイダーのドキュメントを参照してください。
メモ
ほとんどの OIDC プロバイダーでは、MapInboundClaims を false
に設定する必要があります。こうすることで、要求の名前は変更されなくなります。
oidcOptions.MapInboundClaims = false;
oidcOptions.TokenValidationParameters.NameClaimType = "name";
oidcOptions.TokenValidationParameters.RoleClaimType = "roles";
パスの構成: パスは、OIDC プロバイダーにアプリケーションを登録するときに構成された、リダイレクト URI (ログイン コールバック パス) およびログアウト後リダイレクト (サインアウト コールバック パス) パスと、一致している必要があります。 Azure portal では、アプリの登録の [認証 ] ブレードでパスが構成されます。 サインインとサインアウト両方のパスを、リダイレクト URI として登録する必要があります。 既定値は、/signin-oidc
と /signout-callback-oidc
です。
CallbackPath: ユーザーエージェントが返される、アプリのベース パス内の要求パス。
アプリの OIDC プロバイダー登録で、サインアウト時のコールバック パスを構成します。 次の例では、 {PORT}
のプレースホルダーはアプリのポートです。
https://localhost:{PORT}/signin-oidc
メモ
Microsoft Entra ID を使う場合、localhost
アドレスにポートは必要ありません。 他のほとんどの OIDC プロバイダーには、正しいポートが必要です。
SignedOutCallbackPath (構成キー: 「SignedOutCallbackPath
」): ユーザー エージェントが ID プロバイダーからサイン アウトした後に最初に返される OIDC ハンドラーによってインターセプトされたアプリのベース パス内の要求パス。 既定値の「/signout-callback-oidc
」が使用されるため、サンプル アプリではパスの値は設定されません。 要求をインターセプトした後、OIDC ハンドラーは SignedOutRedirectUri または RedirectUri(指定されている場合) にリダイレクトします。
アプリの OIDC プロバイダー登録で、サインアウト時のコールバック パスを構成します。 次の例では、 {PORT}
のプレースホルダーはアプリのポートです。
https://localhost:{PORT}/signout-callback-oidc
メモ
Microsoft Entra ID を使用する場合は、 Entra または Azure portal で Web プラットフォーム構成の リダイレクト URI エントリのパスを設定します。 Entra を使用する場合、localhost
アドレスにポートは必要ありません。 他のほとんどの OIDC プロバイダーには、正しいポートが必要です。 Entra でアプリの登録にサインアウト時のコールバック パス URI を追加しない場合、Entra はユーザーをアプリにリダイレクトすることを拒否し、単にブラウザー ウィンドウを閉じるよう要求します。
RemoteSignOutPath: このパスで要求を受け取ると、ハンドラーはサインアウト スキームを使用してサインアウトを呼び出します。
次の例では、 {PORT}
のプレースホルダーはアプリのポートです。
https://localhost/signout-oidc
メモ
Microsoft Entra ID を使用する場合は、Entra または Azure portal で フロント チャネルログアウト URL を 設定します。 Entra を使用する場合、localhost
アドレスにポートは必要ありません。 他のほとんどの OIDC プロバイダーには、正しいポートが必要です。
oidcOptions.CallbackPath = new PathString("{PATH}");
oidcOptions.SignedOutCallbackPath = new PathString("{PATH}");
oidcOptions.RemoteSignOutPath = new PathString("{PATH}");
例 (既定値):
oidcOptions.CallbackPath = new PathString("/signin-oidc");
oidcOptions.SignedOutCallbackPath = new PathString("/signout-callback-oidc");
oidcOptions.RemoteSignOutPath = new PathString("/signout-oidc");
("common" エンドポイントを使用する Microsoft Azure のみ) : 多くの OIDC プロバイダーは既定の発行者検証コントロールで動作しますが、TokenValidationParameters.IssuerValidator によって返されるテナント ID ({TENANT ID}
) でパラメーター化された発行者を考慮する必要があります。https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration
詳細については、 OpenID Connect を使用した SecurityTokenInvalidIssuerException と Azure AD の "共通" エンドポイント (AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet
#1731) に関するページを参照してください。
Microsoft Entra ID または Azure AD B2C の「共通」エンドポイントを使用するアプリの場合のみ:
var microsoftIssuerValidator = AadIssuerValidator.GetAadIssuerValidator(oidcOptions.Authority);
oidcOptions.TokenValidationParameters.IssuerValidator = microsoftIssuerValidator.Validate;
Blazor Web App クライアント プロジェクト (BlazorWebAppOidc.Client
)
BlazorWebAppOidc.Client
プロジェクトは、Blazor Web App のクライアント側プロジェクトです。
クライアントは AddAuthenticationStateDeserialization を呼び出して、サーバーによって渡された認証状態を逆シリアル化して使用します。 認証状態は、WebAssembly アプリケーションの有効期間中は変わりません。
PersistentAuthenticationStateProvider
クラス (PersistentAuthenticationStateProvider.cs
) は、ページがサーバーでレンダリングされたときにページに保持されていたデータを探すことでユーザーの認証状態を決定するクライアント側 AuthenticationStateProvider です。 認証状態は、WebAssembly アプリケーションの有効期間中は変わりません。
ユーザーがログインまたはログアウトする必要がある場合は、ページ全体の再読み込みが必要です。
サンプル アプリでは、表示のためにユーザー名とメールアドレスだけを提供します。
Microsoft Entra ID または Azure AD B2C の場合は、適切な既定値で OIDC とAddMicrosoftIdentityWebApp認証ハンドラーの両方を追加する Microsoft Identity Web (Microsoft.Identity.Web
NuGet パッケージ、API ドキュメント) のCookieを使用できます。 サンプル アプリとこの記事のガイダンスでは、Microsoft Identity Web は使用されません。 このガイダンスでは、任意の OIDC プロバイダー用の OIDC ハンドラーを "手動で" 構成する方法を示します。 Microsoft Identity Web の実装の詳細については、「Microsoft Entra ID を使用して ASP.NET Core Blazor Web App をセキュリティで保護する」を参照してください。
このバージョンの記事では、グローバル対話型サーバー レンダリング (単一プロジェクト) を採用するアプリで Backend for Frontend (BFF) パターンを採用せずに OIDC を実装する方法について説明します。 BFF パターンは、外部サービスに対して認証された要求を行う場合に役立ちます。 アプリの仕様でグローバルな対話型自動レンダリングを使用して BFF パターンを採用する必要がある場合は、記事のバージョン セレクターを BFF パターン に変更します。
次の仕様が採用されています。
- Blazor Web Appでは、グローバル対話機能を備えたサーバー レンダリング モードが使用されます。
- このアプリは、すべての OIDC 認証フローの開始点です。 OIDC はアプリで手動で構成され、 Microsoft Entra ID または Microsoft Identity Web パッケージに依存しません。また、サンプル アプリでは Microsoft Azure ホスティングが必要ありません。 ただし、サンプル アプリは Entra や Microsoft Identity Web で使用でき、Azure でホストできます。
- 非対話形式でのトークンの自動更新。
- 別の Web API プロジェクトでは、気象データのセキュリティで保護された Web API 呼び出しを示します。
サンプル ソリューション
サンプル アプリは、次のプロジェクトで構成されます。
-
BlazorWebAppOidcServer
: Blazor Web App サーバー側プロジェクト(グローバルなインタラクティブサーバーレンダリング)。 -
MinimalApiJwt
: 気象データ用の 最小限の API エンドポイントを含むバックエンド Web API。
次のリンクを使用して、Blazor サンプル リポジトリの最新バージョン フォルダーからサンプルにアクセスします。 サンプルは、.NET 8 以降の BlazorWebAppOidcServer
フォルダーにあります。
サンプル コードを表示またはダウンロードする (ダウンロード方法)
Microsoft Entra ID アプリの登録
アプリと Web API が同じソリューション内にある場合でも、アプリと Web API には個別の登録を使用することをお勧めします。 次のガイダンスは、サンプル ソリューションの BlazorWebAppOidcServer
アプリと MinimalApiJwt
Web API を対象としていますが、一般に、アプリと Web API の Entra ベースの登録にも同じガイダンスが適用されます。
最初に Web API (MinimalApiJwt
) を登録して、アプリの登録時に Web API へのアクセスを許可できるようにします。 Web API のテナント ID とクライアント ID は、 Program
ファイルで Web API を構成するために使用されます。 Web API を登録した後、アプリの登録で Web API を公開します>スコープ名がの Weather.Get
します。 アプリの構成で使用するアプリ ID URI を記録します。
次に、BlazorWebAppOidcServer
プラットフォーム構成とのリダイレクト URI (ポートは必要ありません) にアプリ (https://localhost/signin-oidc
) を登録します。 アプリのテナント ID とクライアント ID、および Web API のベース アドレス、アプリ ID URI、天気スコープ名は、 Program
ファイルでアプリを構成するために使用されます。
アプリの登録>API のアクセス許可で Web API へのアクセス許可を付与します。 アプリのセキュリティ仕様で呼び出される場合は、組織が Web API にアクセスするための管理者の同意を付与できます。 承認されたユーザーとグループは、アプリ登録および>でアプリの登録に割り当てられます。
Entra または Azure portal の 暗黙的な許可とハイブリッド フロー のアプリ登録構成で、 アクセス トークン または ID トークンを返す承認エンドポイントのチェック ボックスをオンにしないでください。 OpenID Connect ハンドラーは、承認エンドポイントから返されたコードを使用して、適切なトークンを自動的に要求します。
Entra または Azure ポータルのアプリ登録で、管理>証明書とシークレット>新しいクライアント シークレットを作成します。 次のセクションで使用するには、クライアント シークレット の値 を保持します。
特定の設定に関する追加の Entra 構成ガイダンスについては、この記事の後半で説明します。
クライアント シークレットを確立する
このセクションは、 Blazor Web App (BlazorWebAppOidcServer
プロジェクト) のサーバー プロジェクトにのみ適用されます。
警告
アプリ シークレット、接続文字列、資格情報、パスワード、個人識別番号 (PIN)、プライベート C#/.NET コード、秘密キー/トークンをクライアント側コードに格納しないでください。これは 常に安全ではありません。 テスト/ステージング環境と運用環境では、サーバー側の Blazor コードと Web API は、プロジェクト コードまたは構成ファイル内で資格情報を維持しないように、セキュリティで保護された認証フローを使用する必要があります。 ローカル開発テスト以外では、環境変数が最も安全なアプローチではないため、環境変数を使用して機密データを格納しないようにすることをお勧めします。 ローカル開発テストでは、機密データをセキュリティで保護するために Secret Manager ツール を使用することをお勧めします。 詳細については、「 機密データと資格情報を安全に管理する」を参照してください。
ローカル開発テストでは、 Secret Manager ツール を使用して、 Blazor サーバー プロジェクトのクライアント シークレットを構成キー Authentication:Schemes:MicrosoftOidc:ClientSecret
の下に格納します。
Blazor サーバー プロジェクトは、Secret Manager ツール用に初期化されていません。 Visual Studio の Developer PowerShell コマンド シェルなどのコマンド シェルを使用して、次のコマンドを実行します。 コマンドを実行する前に、 cd
コマンドを使用してサーバー プロジェクトのディレクトリにディレクトリを変更します。 このコマンドは、ユーザー シークレット識別子 (アプリのプロジェクト ファイル内の <UserSecretsId>
) を確立します。
dotnet user-secrets init
次のコマンドを実行して、クライアント シークレットを設定します。
{SECRET}
プレースホルダーは、アプリの登録から取得したクライアント シークレットです。
dotnet user-secrets set "Authentication:Schemes:MicrosoftOidc:ClientSecret" "{SECRET}"
Visual Studio を使用している場合は、 ソリューション エクスプローラー でプロジェクトを右クリックし、[ユーザー シークレットの管理] を選択することで 、シークレットが設定されていることを確認できます。
MinimalApiJwt
プロジェクト
MinimalApiJwt
プロジェクトは、複数のフロントエンド プロジェクト用のバックエンド Web API です。 このプロジェクトでは、気象データ用の 最小限の API エンドポイントを構成します。
MinimalApiJwt.http
ファイルは、気象データの要求のテストに使用できます。 エンドポイントをテストするには MinimalApiJwt
プロジェクトが実行されている必要があり、エンドポイントはファイルにハードコーディングされていることに注意してください。 詳細については、「 Visual Studio 2022 での .http ファイルの使用」を参照してください。
プロジェクトには、 開発環境で OpenAPI ドキュメント と Swagger UI を生成するためのパッケージと構成が含まれています。 詳細については、「 生成された OpenAPI ドキュメントを使用する」を参照してください。
このプロジェクトでは、気象データ用の 最小 API エンドポイントが作成されます。
app.MapGet("/weather-forecast", () =>
{
var forecast = Enumerable.Range(1, 5).Select(index =>
new WeatherForecast
(
DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
Random.Shared.Next(-20, 55),
summaries[Random.Shared.Next(summaries.Length)]
))
.ToArray();
return forecast;
}).RequireAuthorization();
プロジェクトの JwtBearerOptions ファイル内の AddJwtBearer 呼び出しの Program
でプロジェクトを構成します。
Authorityは、OIDC 呼び出しを行う権限を設定します。
MinimalApiJwt
プロジェクトには別のアプリ登録を使用することをお勧めします。 この権限は、ID プロバイダーによって返される JWT の issurer (iss
) と一致します。
jwtOptions.Authority = "{AUTHORITY}";
機関の形式は、使用中のテナントの種類によって異なります。 Microsoft Entra ID の次の例では、 aaaabbbb-0000-cccc-1111-dddd2222eeee
のテナント ID を使用します。
ME-ID テナント権限の例:
jwtOptions.Authority = "https://sts.windows.net/aaaabbbb-0000-cccc-1111-dddd2222eeee/";
AAD B2C テナントオーソリティの例:
jwtOptions.Authority = "https://login.microsoftonline.com/aaaabbbb-0000-cccc-1111-dddd2222eeee/v2.0/";
Audienceは、受信した OIDC トークンの対象ユーザーを設定します。
jwtOptions.Audience = "{APP ID URI}";
メモ
Microsoft Entra ID を使用する場合は、[Entra または Azure portal で API を公開する] でWeather.Get
スコープを追加するときに構成されたアプリケーション ID URI のパスのみに値を一致させます。 値にスコープ名 "Weather.Get
" を含めないでください。
対象ユーザーの形式は、使用中のテナントの種類によって異なります。 Microsoft Entra ID の次の例では、 contoso
のテナント ID と 11112222-bbbb-3333-cccc-4444dddd5555
のクライアント ID を使用します。
ME-ID テナントのアプリ ID URI の例:
jwtOptions.Audience = "api://11112222-bbbb-3333-cccc-4444dddd5555";
AAD B2C テナントアプリ ID URI の例:
jwtOptions.Audience = "https://contoso.onmicrosoft.com/11112222-bbbb-3333-cccc-4444dddd5555";
BlazorWebAppOidcServer
プロジェクト
非対話型トークンの自動更新は、カスタム cookie リフレッシャー (CookieOidcRefresher.cs
) によって管理されます。
DelegatingHandler (TokenHandler
) は、送信要求へのユーザーのアクセス トークンのアタッチを管理します。 トークン ハンドラーは、静的サーバー側レンダリング (静的 SSR) 中にのみ実行されるため、このシナリオでは HttpContext を使用しても安全です。 詳細については、ASP.NET Core Blazor アプリの IHttpContextAccessor/HttpContext、およびコア サーバー側の ASP.NET および追加のセキュリティ シナリオBlazor Web App参照してください。
TokenHandler.cs
:
public class TokenHandler(IHttpContextAccessor httpContextAccessor) :
DelegatingHandler
{
protected override async Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken)
{
if (httpContextAccessor.HttpContext is null)
{
throw new Exception("HttpContext not available");
}
var accessToken = await httpContextAccessor.HttpContext
.GetTokenAsync("access_token");
request.Headers.Authorization =
new AuthenticationHeaderValue("Bearer", accessToken);
return await base.SendAsync(request, cancellationToken);
}
}
プロジェクトのProgram
ファイルでは、トークン ハンドラー (TokenHandler
) がサービスとして登録され、名前AddHttpMessageHandler ("MinimalApiJwt
") を使用してバックエンド Web API に安全な要求を行うExternalApi
を持つメッセージ ハンドラーとして指定されます。
builder.Services.AddScoped<TokenHandler>();
builder.Services.AddHttpClient("ExternalApi",
client => client.BaseAddress = new Uri(builder.Configuration["ExternalApiUri"] ??
throw new Exception("Missing base address!")))
.AddHttpMessageHandler<TokenHandler>();
Weather
コンポーネントは、[Authorize]
属性を使用して、未承認のアクセスを防ぎます。
承認ポリシーを使用してアプリ全体で承認を要求し、パブリック エンドポイントのサブセットで承認をオプトアウトする方法の詳細については、Razor Pages OIDC ガイダンスを参照してください。
ExternalApi
HTTP クライアントは、セキュリティで保護された Web API に気象データの要求を行うために使用されます。
OnInitializedAsync
の:
using var request = new HttpRequestMessage(HttpMethod.Get, "/weather-forecast");
var client = ClientFactory.CreateClient("ExternalApi");
using var response = await client.SendAsync(request);
response.EnsureSuccessStatusCode();
forecasts = await response.Content.ReadFromJsonAsync<WeatherForecast[]>() ??
throw new IOException("No weather forecast!");
プロジェクトの appsettings.json
ファイルで、外部 API URI を構成します。
"ExternalApiUri": "{BASE ADDRESS}"
例:
"ExternalApiUri": "https://localhost:7277"
次の OpenIdConnectOptions の構成は、プロジェクトの Program
ファイルに含まれる AddOpenIdConnect の呼び出しにあります。
PushedAuthorizationBehavior: プッシュされた承認要求 (PAR) のサポートを制御します。 既定では、ID プロバイダーの検出ドキュメント (通常は .well-known/openid-configuration
にあります) が PAR のサポートをアドバタイズする場合は、PAR を使用します。 アプリの PAR サポートを必要とする場合は、 PushedAuthorizationBehavior.Require
の値を割り当てることができます。 PAR は Microsoft Entra ではサポートされておらず、Entra が今後サポートする予定はありません。
oidcOptions.PushedAuthorizationBehavior = PushedAuthorizationBehavior.UseIfAvailable;
SignInScheme: 認証が成功した後にユーザーの ID を保持するミドルウェアに対応する認証スキームを設定します。 OIDC ハンドラーでは、複数の要求にわたってユーザー資格情報を保持できるサインイン スキームを使用する必要があります。 次の行は、デモンストレーションの目的のためだけに存在します。 省略すると、DefaultSignInScheme がフォールバック値として使用されます。
oidcOptions.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
openid
と profile
のスコープ (Scope) (省略可能): openid
と profile
スコープも、OIDC ハンドラーが機能するために必要なので既定で構成されますが、スコープが Authentication:Schemes:MicrosoftOidc:Scope
の構成に含まれている場合は、これらを追加し直すことが必要になる場合があります。 一般的な構成ガイダンスについては、ASP.NET Core の構成 と ASP.NET Core Blazor 構成 を参照してください。
oidcOptions.Scope.Add(OpenIdConnectScope.OpenIdProfile);
気象データ用の外部 Web API にアクセスするための Weather.Get
スコープを構成します。 次の例は、ME-ID テナント ドメインでの Entra ID の使用に基づいています。 次の例では、 {APP ID URI}
プレースホルダーは、Web API が公開されている Entra または Azure portal にあります。 その他の ID プロバイダーの場合は、適切なスコープを使用します。
oidcOptions.Scope.Add("{APP ID URI}/Weather.Get");
スコープの形式は、使用中のテナントの種類によって異なります。 次の例では、テナント ドメインが contoso.onmicrosoft.com
され、クライアント ID が 11112222-bbbb-3333-cccc-4444dddd5555
されています。
ME-ID テナントのアプリ ID URI の例:
oidcOptions.Scope.Add("api://11112222-bbbb-3333-cccc-4444dddd5555/Weather.Get");
AAD B2C テナントアプリ ID URI の例:
oidcOptions.Scope.Add("https://contoso.onmicrosoft.com/11112222-bbbb-3333-cccc-4444dddd5555/Weather.Get");
SaveTokens: 認可が成功した後で、アクセス トークンと更新トークンを AuthenticationProperties 内に保存する必要があるかどうかを定義します。 このプロパティは true
に設定されているため、非インタラクティブ トークン更新用に更新トークンが格納されます。
oidcOptions.SaveTokens = true;
オフライン アクセスのスコープ (Scope): offline_access
スコープが更新トークンのために必要です。
oidcOptions.Scope.Add(OpenIdConnectScope.OfflineAccess);
Authority と ClientId: OIDC 呼び出しのためのオーソリティとクライアント ID を設定します。
oidcOptions.Authority = "{AUTHORITY}";
oidcOptions.ClientId = "{CLIENT ID}";
次の例では、 aaaabbbb-0000-cccc-1111-dddd2222eeee
のテナント ID と 00001111-aaaa-2222-bbbb-3333cccc4444
のクライアント ID を使用します。
oidcOptions.Authority = "https://login.microsoftonline.com/aaaabbbb-0000-cccc-1111-dddd2222eeee/v2.0/";
oidcOptions.ClientId = "00001111-aaaa-2222-bbbb-3333cccc4444";
マルチテナント アプリの場合は、"共通" 機関を使用する必要があります。 "common" オーソリティをシングルテナント アプリに対して使用することもできますが、このセクションで後ほど示すように、カスタムの IssuerValidator が必要になります。
oidcOptions.Authority = "https://login.microsoftonline.com/common/v2.0/";
ResponseType: 認可コード フローのみを実行するように、OIDC ハンドラーを構成します。 このモードでは、暗黙的な許可とハイブリッド フローは必要ありません。 OIDC ハンドラーは、承認エンドポイントから返されたコードを使用して適切なトークンを自動的に要求します。
oidcOptions.ResponseType = OpenIdConnectResponseType.Code;
MapInboundClaims および NameClaimType と RoleClaimType の構成: 多くの OIDC サーバーは、name
の既定値 SOAP/WS-Fed ではなく、"role
" と "ClaimTypes" を使用します。
MapInboundClaims が false
に設定されていると、ハンドラーは要求のマッピングを実行せず、JWT からの要求名がアプリによって直接使用されます。 次の例では、ロール要求の種類を "roles
" に設定します。これは Microsoft Entra ID (ME-ID) に適しています。 詳細情報は、ID プロバイダーのドキュメントを参照してください。
メモ
ほとんどの OIDC プロバイダーでは、MapInboundClaims を false
に設定する必要があります。こうすることで、要求の名前は変更されなくなります。
oidcOptions.MapInboundClaims = false;
oidcOptions.TokenValidationParameters.NameClaimType = "name";
oidcOptions.TokenValidationParameters.RoleClaimType = "roles";
パスの構成: パスは、OIDC プロバイダーにアプリケーションを登録するときに構成された、リダイレクト URI (ログイン コールバック パス) およびログアウト後リダイレクト (サインアウト コールバック パス) パスと、一致している必要があります。 Azure portal では、アプリの登録の [認証 ] ブレードでパスが構成されます。 サインインとサインアウト両方のパスを、リダイレクト URI として登録する必要があります。 既定値は、/signin-oidc
と /signout-callback-oidc
です。
CallbackPath: ユーザーエージェントが返される、アプリのベース パス内の要求パス。
アプリの OIDC プロバイダー登録で、サインアウト時のコールバック パスを構成します。 次の例では、 {PORT}
のプレースホルダーはアプリのポートです。
https://localhost:{PORT}/signin-oidc
メモ
Microsoft Entra ID を使う場合、localhost
アドレスにポートは必要ありません。 他のほとんどの OIDC プロバイダーには、正しいポートが必要です。
SignedOutCallbackPath (構成キー: 「SignedOutCallbackPath
」): ユーザー エージェントが ID プロバイダーからサイン アウトした後に最初に返される OIDC ハンドラーによってインターセプトされたアプリのベース パス内の要求パス。 既定値の「/signout-callback-oidc
」が使用されるため、サンプル アプリではパスの値は設定されません。 要求をインターセプトした後、OIDC ハンドラーは SignedOutRedirectUri または RedirectUri(指定されている場合) にリダイレクトします。
アプリの OIDC プロバイダー登録で、サインアウト時のコールバック パスを構成します。 次の例では、 {PORT}
のプレースホルダーはアプリのポートです。
https://localhost:{PORT}/signout-callback-oidc
メモ
Microsoft Entra ID を使用する場合は、 Entra または Azure portal で Web プラットフォーム構成の リダイレクト URI エントリのパスを設定します。 Entra を使用する場合、localhost
アドレスにポートは必要ありません。 他のほとんどの OIDC プロバイダーには、正しいポートが必要です。 Entra でアプリの登録にサインアウト時のコールバック パス URI を追加しない場合、Entra はユーザーをアプリにリダイレクトすることを拒否し、単にブラウザー ウィンドウを閉じるよう要求します。
RemoteSignOutPath: このパスで要求を受け取ると、ハンドラーはサインアウト スキームを使用してサインアウトを呼び出します。
次の例では、 {PORT}
のプレースホルダーはアプリのポートです。
https://localhost/signout-oidc
メモ
Microsoft Entra ID を使用する場合は、Entra または Azure portal で フロント チャネルログアウト URL を 設定します。 Entra を使用する場合、localhost
アドレスにポートは必要ありません。 他のほとんどの OIDC プロバイダーには、正しいポートが必要です。
oidcOptions.CallbackPath = new PathString("{PATH}");
oidcOptions.SignedOutCallbackPath = new PathString("{PATH}");
oidcOptions.RemoteSignOutPath = new PathString("{PATH}");
例 (既定値):
oidcOptions.CallbackPath = new PathString("/signin-oidc");
oidcOptions.SignedOutCallbackPath = new PathString("/signout-callback-oidc");
oidcOptions.RemoteSignOutPath = new PathString("/signout-oidc");
("common" エンドポイントを使用する Microsoft Azure のみ) : 多くの OIDC プロバイダーは既定の発行者検証コントロールで動作しますが、TokenValidationParameters.IssuerValidator によって返されるテナント ID ({TENANT ID}
) でパラメーター化された発行者を考慮する必要があります。https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration
詳細については、 OpenID Connect を使用した SecurityTokenInvalidIssuerException と Azure AD の "共通" エンドポイント (AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet
#1731) に関するページを参照してください。
Microsoft Entra ID または Azure AD B2C の「共通」エンドポイントを使用するアプリの場合のみ:
var microsoftIssuerValidator = AadIssuerValidator.GetAadIssuerValidator(oidcOptions.Authority);
oidcOptions.TokenValidationParameters.IssuerValidator = microsoftIssuerValidator.Validate;
Microsoft Entra ID または Azure AD B2C の場合は、適切な既定値で OIDC とAddMicrosoftIdentityWebApp認証ハンドラーの両方を追加する Microsoft Identity Web (Microsoft.Identity.Web
NuGet パッケージ、API ドキュメント) のCookieを使用できます。 サンプル アプリとこの記事のガイダンスでは、Microsoft Identity Web は使用されません。 このガイダンスでは、任意の OIDC プロバイダー用の OIDC ハンドラーを "手動で" 構成する方法を示します。 Microsoft Identity Web の実装の詳細については、「Microsoft Entra ID を使用して ASP.NET Core Blazor Web App をセキュリティで保護する」を参照してください。
このバージョンの記事では、 フロントエンドバックエンド (BFF) パターンを使用した OIDC の実装について説明します。 アプリの仕様で BFF パターンの採用が必要ない場合は、記事のバージョン セレクターを 非 BFF パターン (対話型自動) (対話型自動レンダリング) または 非 BFF パターン (対話型サーバー) (対話型サーバー レンダリング) に変更します。
前提条件
.NET Aspire には Visual Studio バージョン 17.10 以降が必要です。
また、「をビルドする」の「.NET Aspire」セクションを参照してください。
サンプル ソリューション
サンプル アプリは、次のプロジェクトで構成されます。
-
.NET Aspire:
-
Aspire.AppHost
: アプリの高度なオーケストレーションの問題を管理するために使用されます。 -
Aspire.ServiceDefaults
: 必要に応じて拡張してカスタマイズできる ..NET Aspire の既定のアプリ構成が含まれます。
-
-
MinimalApiJwt
: 気象データの 最小 API エンドポイントの例を含むバックエンド Web API。 -
BlazorWebAppOidc
: Blazor Web App のサーバー側プロジェクト。 バックエンド Web API プロジェクトの天気予報エンドポイントに対する要求をプロキシするために、このプロジェクトではYARP を使用し、認証内に保存されているを利用します。 -
BlazorWebAppOidc.Client
: Blazor Web App のクライアント側プロジェクト。
次のリンクを使用して、Blazor サンプル リポジトリの最新バージョン フォルダーからサンプルにアクセスします。 サンプルは、.NET 8 以降の BlazorWebAppOidcBff
フォルダーにあります。
サンプル コードを表示またはダウンロードする (ダウンロード方法)
Blazor Web Appでは、グローバル対話機能を備えた自動レンダリング モードが使用されます。
サーバー プロジェクトは AddAuthenticationStateSerialization を呼び出して、 PersistentComponentState を使用して認証状態をクライアントにフローするサーバー側認証状態プロバイダーを追加します。 クライアントは AddAuthenticationStateDeserialization を呼び出して、サーバーによって渡された認証状態を逆シリアル化して使用します。 認証状態は、WebAssembly アプリケーションの有効期間中は変わりません。
PersistingAuthenticationStateProvider
クラス (PersistingAuthenticationStateProvider.cs
) は、AuthenticationStateProvider を使用してクライアントに認証状態をフローするサーバー側 PersistentComponentState であり、WebAssembly アプリケーションの有効期間中は固定されます。
このアプリは、すべての OIDC 認証フローの開始点です。 OIDC はアプリで手動で構成され、 Microsoft Entra ID または Microsoft Identity Web パッケージに依存しません。また、サンプル アプリでは Microsoft Azure ホスティングが必要ありません。 ただし、サンプル アプリは Entra や Microsoft Identity Web で使用でき、Azure でホストできます。
カスタム cookie リフレッシャー (CookieOidcRefresher.cs
) を使用する非対話形式でのトークンの自動更新。
バックエンド のフロントエンド (BFF) パターンは、サービス検出に.NET Aspireを使用し、バックエンド アプリの天気予報エンドポイントに要求をプロキシする場合は YARP を使用して採用されます。
バックエンド Web API (MinimalApiJwt
) は、JWT ベアラー認証を使用して、サインイン Blazor Web Appのcookieによって保存された JWT トークンを検証します。
Aspire は、.NET クラウドネイティブ アプリを構築するエクスペリエンスを強化します。 これは、分散型アプリを構築して実行するための、一貫性のある、厳格なツールとパターンのセットを提供します。
YARP (Yet Another Reverse Proxy) は、リバース プロキシ サーバーの作成に使用されるライブラリです。
MapForwarder
サーバー プロジェクトの Program
ファイルでは、送信要求の既定の構成、カスタマイズされた変換、および既定の HTTP クライアントを使用して、指定したパターンに一致する HTTP 要求の直接転送を特定の宛先に追加します。
- コンポーネントを
Weather
サーバーでレンダリングする場合、ServerWeatherForecaster
クラスを使用して、ユーザーのアクセス トークンを使用して気象データの要求をプロキシします。 IHttpContextAccessor.HttpContext は、HttpContext メソッドでGetWeatherForecastAsync
を使用できるかどうかを判断します。 詳細については、「 コア Razor コンポーネント ASP.NET 参照してください。 - コンポーネントをクライアントでレンダリングする場合、コンポーネントは
ClientWeatherForecaster
サービス実装を使用します。この実装では、事前に構成した HttpClient (クライアント プロジェクトのProgram
ファイル内) を使用して、サーバー プロジェクトへの Web API 呼び出しが行われます。 サーバー プロジェクトの/weather-forecast
ファイルで定義されている最小 API エンドポイント (Program
) は、ユーザーのアクセス トークンで要求を変換し、気象データを取得します。
.NET Aspireの詳細については、「.NET Aspireの一般提供: .NET Cloud-Native 開発の簡略化 (2024 年 5 月)」を参照してください。
Blazor Web Appのサービス抽象化を使用した (Web) API 呼び出しの詳細については、「ASP.NET Core Blazor アプリからの Web API の呼び出し」を参照してください。
Microsoft Entra ID アプリの登録
アプリと Web API が同じソリューション内にある場合でも、アプリと Web API には個別の登録を使用することをお勧めします。 次のガイダンスは、サンプル ソリューションの BlazorWebAppOidc
アプリと MinimalApiJwt
Web API を対象としていますが、一般に、アプリと Web API の Entra ベースの登録にも同じガイダンスが適用されます。
最初に Web API (MinimalApiJwt
) を登録して、アプリの登録時に Web API へのアクセスを許可できるようにします。 Web API のテナント ID とクライアント ID は、 Program
ファイルで Web API を構成するために使用されます。 Web API を登録した後、アプリの登録で Web API を公開します>スコープ名がの Weather.Get
します。 アプリの構成で使用するアプリ ID URI を記録します。
次に、BlazorWebAppOidc
プラットフォーム構成と/のBlazorWebApOidc.Client
(ポートは必要ありません) にアプリ (https://localhost/signin-oidc
) を登録します。 アプリのテナント ID とクライアント ID、および Web API のベース アドレス、アプリ ID URI、天気スコープ名は、 Program
ファイルでアプリを構成するために使用されます。
アプリの登録>API のアクセス許可で Web API へのアクセス許可を付与します。 アプリのセキュリティ仕様で呼び出される場合は、組織が Web API にアクセスするための管理者の同意を付与できます。 承認されたユーザーとグループは、アプリ登録および>でアプリの登録に割り当てられます。
Entra または Azure portal の 暗黙的な許可とハイブリッド フロー のアプリ登録構成で、 アクセス トークン または ID トークンを返す承認エンドポイントのチェック ボックスをオンにしないでください。 OpenID Connect ハンドラーは、承認エンドポイントから返されたコードを使用して、適切なトークンを自動的に要求します。
Entra または Azure ポータルのアプリ登録で、管理>証明書とシークレット>新しいクライアント シークレットを作成します。 次のセクションで使用するには、クライアント シークレット の値 を保持します。
特定の設定に関する追加の Entra 構成ガイダンスについては、この記事の後半で説明します。
クライアント シークレットを確立する
このセクションは、 Blazor Web App (BlazorWebAppOidc
プロジェクト) のサーバー プロジェクトにのみ適用されます。
警告
アプリ シークレット、接続文字列、資格情報、パスワード、個人識別番号 (PIN)、プライベート C#/.NET コード、秘密キー/トークンをクライアント側コードに格納しないでください。これは 常に安全ではありません。 テスト/ステージング環境と運用環境では、サーバー側の Blazor コードと Web API は、プロジェクト コードまたは構成ファイル内で資格情報を維持しないように、セキュリティで保護された認証フローを使用する必要があります。 ローカル開発テスト以外では、環境変数が最も安全なアプローチではないため、環境変数を使用して機密データを格納しないようにすることをお勧めします。 ローカル開発テストでは、機密データをセキュリティで保護するために Secret Manager ツール を使用することをお勧めします。 詳細については、「 機密データと資格情報を安全に管理する」を参照してください。
ローカル開発テストでは、 Secret Manager ツール を使用して、 Blazor サーバー プロジェクトのクライアント シークレットを構成キー Authentication:Schemes:MicrosoftOidc:ClientSecret
の下に格納します。
Blazor サーバー プロジェクトは、Secret Manager ツール用に初期化されていません。 Visual Studio の Developer PowerShell コマンド シェルなどのコマンド シェルを使用して、次のコマンドを実行します。 コマンドを実行する前に、 cd
コマンドを使用してサーバー プロジェクトのディレクトリにディレクトリを変更します。 このコマンドは、ユーザー シークレット識別子 (サーバー アプリのプロジェクト ファイル内の<UserSecretsId>
) を確立します。
dotnet user-secrets init
次のコマンドを実行して、クライアント シークレットを設定します。
{SECRET}
プレースホルダーは、アプリの登録から取得したクライアント シークレットです。
dotnet user-secrets set "Authentication:Schemes:MicrosoftOidc:ClientSecret" "{SECRET}"
Visual Studio を使用している場合は、 ソリューション エクスプローラー でサーバー プロジェクトを右クリックし、[ユーザー シークレットの管理] を選択することで 、シークレットが設定されていることを確認できます。
.NET Aspire プロジェクト
.NET Aspireの使用の詳細と、サンプル アプリの.AppHost
および.ServiceDefaults
プロジェクトの詳細については、.NET Aspireドキュメントを参照してください。
.NET Aspire の前提条件を満たしていることを確認します。 詳細については、「する」の.NET Aspireセクションを参照してください。
サンプル アプリでは、開発テスト中に使用する、安全でない HTTP 起動プロファイル (http
) のみが構成されます。 安全でない安全な起動設定プロファイルの例を含む詳細については、「.NET Aspireで安全でないトランスポートを許可する (.NET Aspire ドキュメント)」を参照してください。
MinimalApiJwt
プロジェクト
MinimalApiJwt
プロジェクトは、複数のフロントエンド プロジェクト用のバックエンド Web API です。 このプロジェクトでは、気象データ用の 最小限の API エンドポイントを構成します。
Blazor Web App サーバー側プロジェクト (BlazorWebAppOidc
) からの要求は、MinimalApiJwt
プロジェクトにプロキシされます。
MinimalApiJwt.http
ファイルは、気象データの要求のテストに使用できます。 エンドポイントをテストするには MinimalApiJwt
プロジェクトが実行されている必要があり、エンドポイントはファイルにハードコーディングされていることに注意してください。 詳細については、「 Visual Studio 2022 での .http ファイルの使用」を参照してください。
プロジェクトには、 開発環境で OpenAPI ドキュメント と Swagger UI を生成するためのパッケージと構成が含まれています。 詳細については、「 生成された OpenAPI ドキュメントを使用する」を参照してください。
安全な天気予報データ エンドポイントは、プロジェクトの Program
ファイルにあります。
app.MapGet("/weather-forecast", () =>
{
var forecast = Enumerable.Range(1, 5).Select(index =>
new WeatherForecast
(
DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
Random.Shared.Next(-20, 55),
summaries[Random.Shared.Next(summaries.Length)]
))
.ToArray();
return forecast;
}).RequireAuthorization();
RequireAuthorization 拡張メソッドでは、ルート定義の認可が必要です。 プロジェクトに追加するすべてのコントローラーに対して、 [Authorize]
属性 をコントローラーまたはアクションに追加します。
プロジェクトの JwtBearerOptions ファイル内の AddJwtBearer 呼び出しの Program
でプロジェクトを構成します。
Authorityは、OIDC 呼び出しを行う権限を設定します。
MinimalApiJwt
プロジェクトには別のアプリ登録を使用することをお勧めします。 この権限は、ID プロバイダーによって返される JWT の issurer (iss
) と一致します。
jwtOptions.Authority = "{AUTHORITY}";
機関の形式は、使用中のテナントの種類によって異なります。 Microsoft Entra ID の次の例では、 aaaabbbb-0000-cccc-1111-dddd2222eeee
のテナント ID を使用します。
ME-ID テナント権限の例:
jwtOptions.Authority = "https://sts.windows.net/aaaabbbb-0000-cccc-1111-dddd2222eeee/";
AAD B2C テナントオーソリティの例:
jwtOptions.Authority = "https://login.microsoftonline.com/aaaabbbb-0000-cccc-1111-dddd2222eeee/v2.0/";
Audienceは、受信した OIDC トークンの対象ユーザーを設定します。
jwtOptions.Audience = "{APP ID URI}";
メモ
Microsoft Entra ID を使用する場合は、[Entra または Azure portal で API を公開する] でWeather.Get
スコープを追加するときに構成されたアプリケーション ID URI のパスのみに値を一致させます。 値にスコープ名 "Weather.Get
" を含めないでください。
対象ユーザーの形式は、使用中のテナントの種類によって異なります。 Microsoft Entra ID の次の例では、 contoso
のテナント ID と 11112222-bbbb-3333-cccc-4444dddd5555
のクライアント ID を使用します。
ME-ID テナントのアプリ ID URI の例:
jwtOptions.Audience = "api://11112222-bbbb-3333-cccc-4444dddd5555";
AAD B2C テナントアプリ ID URI の例:
jwtOptions.Audience = "https://contoso.onmicrosoft.com/11112222-bbbb-3333-cccc-4444dddd5555";
サーバー側の Blazor Web App プロジェクト (BlazorWebAppOidc
)
このセクションでは、サーバー側の Blazor プロジェクトを構成する方法について説明します。
次のOpenIdConnectOptions構成は、Program
の呼び出しのプロジェクトのAddOpenIdConnect ファイルにあります。
PushedAuthorizationBehavior: プッシュされた承認要求 (PAR) のサポートを制御します。 既定では、ID プロバイダーの検出ドキュメント (通常は .well-known/openid-configuration
にあります) が PAR のサポートをアドバタイズする場合は、PAR を使用します。 アプリの PAR サポートを必要とする場合は、 PushedAuthorizationBehavior.Require
の値を割り当てることができます。 PAR は Microsoft Entra ではサポートされておらず、Entra が今後サポートする予定はありません。
oidcOptions.PushedAuthorizationBehavior = PushedAuthorizationBehavior.UseIfAvailable;
SignInScheme: 認証が成功した後にユーザーの ID を保持するミドルウェアに対応する認証スキームを設定します。 OIDC ハンドラーでは、複数の要求にわたってユーザー資格情報を保持できるサインイン スキームを使用する必要があります。 次の行は、デモンストレーションの目的のためだけに存在します。 省略すると、DefaultSignInScheme がフォールバック値として使用されます。
oidcOptions.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
openid
と profile
のスコープ (Scope) (省略可能): openid
と profile
スコープも、OIDC ハンドラーが機能するために必要なので既定で構成されますが、スコープが Authentication:Schemes:MicrosoftOidc:Scope
の構成に含まれている場合は、これらを追加し直すことが必要になる場合があります。 一般的な構成ガイダンスについては、ASP.NET Core の構成 と ASP.NET Core Blazor 構成 を参照してください。
oidcOptions.Scope.Add(OpenIdConnectScope.OpenIdProfile);
SaveTokens: 認可が成功した後で、アクセス トークンと更新トークンを AuthenticationProperties 内に保存する必要があるかどうかを定義します。 この値は、バックエンド Web API プロジェクト (true
) からの気象データの要求を認証するために MinimalApiJwt
に設定されます。
oidcOptions.SaveTokens = true;
オフライン アクセスのスコープ (Scope): offline_access
スコープが更新トークンのために必要です。
oidcOptions.Scope.Add(OpenIdConnectScope.OfflineAccess);
Web API から気象データを取得するためのスコープ (Scope): 気象データ用の外部 Web API にアクセスするための Weather.Get
スコープを構成します。 次の例では、 {APP ID URI}
プレースホルダーは、Web API が公開されている Entra または Azure portal にあります。 その他の ID プロバイダーの場合は、適切なスコープを使用します。
oidcOptions.Scope.Add("{APP ID URI}/Weather.Get");
スコープの形式は、使用中のテナントの種類によって異なります。 次の例では、テナント ドメインが contoso.onmicrosoft.com
され、クライアント ID が 11112222-bbbb-3333-cccc-4444dddd5555
されています。
ME-ID テナントのアプリ ID URI の例:
oidcOptions.Scope.Add("api://11112222-bbbb-3333-cccc-4444dddd5555/Weather.Get");
AAD B2C テナントアプリ ID URI の例:
oidcOptions.Scope.Add("https://contoso.onmicrosoft.com/11112222-bbbb-3333-cccc-4444dddd5555/Weather.Get");
Authority と ClientId: OIDC 呼び出しのためのオーソリティとクライアント ID を設定します。
oidcOptions.Authority = "{AUTHORITY}";
oidcOptions.ClientId = "{CLIENT ID}";
次の例では、 aaaabbbb-0000-cccc-1111-dddd2222eeee
のテナント ID と 00001111-aaaa-2222-bbbb-3333cccc4444
のクライアント ID を使用します。
oidcOptions.Authority = "https://login.microsoftonline.com/aaaabbbb-0000-cccc-1111-dddd2222eeee/v2.0/";
oidcOptions.ClientId = "00001111-aaaa-2222-bbbb-3333cccc4444";
マルチテナント アプリの場合は、"共通" 機関を使用する必要があります。 "common" オーソリティをシングルテナント アプリに対して使用することもできますが、このセクションで後ほど示すように、カスタムの IssuerValidator が必要になります。
oidcOptions.Authority = "https://login.microsoftonline.com/common/v2.0/";
ResponseType: 認可コード フローのみを実行するように、OIDC ハンドラーを構成します。 このモードでは、暗黙的な許可とハイブリッド フローは必要ありません。 OIDC ハンドラーは、承認エンドポイントから返されたコードを使用して適切なトークンを自動的に要求します。
oidcOptions.ResponseType = OpenIdConnectResponseType.Code;
MapInboundClaims および NameClaimType と RoleClaimType の構成: 多くの OIDC サーバーは、name
の既定値 SOAP/WS-Fed ではなく、"role
" と "ClaimTypes" を使用します。
MapInboundClaims が false
に設定されていると、ハンドラーは要求のマッピングを実行せず、JWT からの要求名がアプリによって直接使用されます。 次の例では、ロール要求の種類を "roles
" に設定します。これは Microsoft Entra ID (ME-ID) に適しています。 詳細情報は、ID プロバイダーのドキュメントを参照してください。
メモ
ほとんどの OIDC プロバイダーでは、MapInboundClaims を false
に設定する必要があります。こうすることで、要求の名前は変更されなくなります。
oidcOptions.MapInboundClaims = false;
oidcOptions.TokenValidationParameters.NameClaimType = "name";
oidcOptions.TokenValidationParameters.RoleClaimType = "roles";
パスの構成: パスは、OIDC プロバイダーにアプリケーションを登録するときに構成された、リダイレクト URI (ログイン コールバック パス) およびログアウト後リダイレクト (サインアウト コールバック パス) パスと、一致している必要があります。 Azure portal では、アプリの登録の [認証 ] ブレードでパスが構成されます。 サインインとサインアウト両方のパスを、リダイレクト URI として登録する必要があります。 既定値は、/signin-oidc
と /signout-callback-oidc
です。
アプリの OIDC プロバイダー登録で、サインアウト時のコールバック パスを構成します。 次の例では、 {PORT}
のプレースホルダーはアプリのポートです。
https://localhost:{PORT}/signin-oidc
メモ
Microsoft Entra ID を使う場合、localhost
アドレスにポートは必要ありません。 他のほとんどの OIDC プロバイダーには、正しいポートが必要です。
SignedOutCallbackPath (構成キー: 「SignedOutCallbackPath
」): ユーザー エージェントが ID プロバイダーからサイン アウトした後に最初に返される OIDC ハンドラーによってインターセプトされたアプリのベース パス内の要求パス。 既定値の「/signout-callback-oidc
」が使用されるため、サンプル アプリではパスの値は設定されません。 要求をインターセプトした後、OIDC ハンドラーは SignedOutRedirectUri または RedirectUri(指定されている場合) にリダイレクトします。
アプリの OIDC プロバイダー登録で、サインアウト時のコールバック パスを構成します。 次の例では、 {PORT}
のプレースホルダーはアプリのポートです。
https://localhost:{PORT}/signout-callback-oidc
メモ
Microsoft Entra ID を使用する場合は、 Entra または Azure portal で Web プラットフォーム構成の リダイレクト URI エントリのパスを設定します。 Entra を使用する場合、localhost
アドレスにポートは必要ありません。 他のほとんどの OIDC プロバイダーには、正しいポートが必要です。 Entra でアプリの登録にサインアウト時のコールバック パス URI を追加しない場合、Entra はユーザーをアプリにリダイレクトすることを拒否し、単にブラウザー ウィンドウを閉じるよう要求します。
RemoteSignOutPath: このパスで要求を受け取ると、ハンドラーはサインアウト スキームを使用してサインアウトを呼び出します。
次の例では、 {PORT}
のプレースホルダーはアプリのポートです。
https://localhost/signout-oidc
メモ
Microsoft Entra ID を使用する場合は、Entra または Azure portal で フロント チャネルログアウト URL を 設定します。 Entra を使用する場合、localhost
アドレスにポートは必要ありません。 他のほとんどの OIDC プロバイダーには、正しいポートが必要です。
oidcOptions.CallbackPath = new PathString("{PATH}");
oidcOptions.SignedOutCallbackPath = new PathString("{PATH}");
oidcOptions.RemoteSignOutPath = new PathString("{PATH}");
例 (既定値):
oidcOptions.CallbackPath = new PathString("/signin-oidc");
oidcOptions.SignedOutCallbackPath = new PathString("/signout-callback-oidc");
oidcOptions.RemoteSignOutPath = new PathString("/signout-oidc");
("common" エンドポイントを使用する Microsoft Azure のみ) : 多くの OIDC プロバイダーは既定の発行者検証コントロールで動作しますが、TokenValidationParameters.IssuerValidator によって返されるテナント ID ({TENANT ID}
) でパラメーター化された発行者を考慮する必要があります。https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration
詳細については、 OpenID Connect を使用した SecurityTokenInvalidIssuerException と Azure AD の "共通" エンドポイント (AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet
#1731) に関するページを参照してください。
Microsoft Entra ID または Azure AD B2C の「共通」エンドポイントを使用するアプリの場合のみ:
var microsoftIssuerValidator = AadIssuerValidator.GetAadIssuerValidator(oidcOptions.Authority);
oidcOptions.TokenValidationParameters.IssuerValidator = microsoftIssuerValidator.Validate;
クライアント側 Blazor Web App プロジェクト (BlazorWebAppOidc.Client
)
BlazorWebAppOidc.Client
プロジェクトは、Blazor Web App のクライアント側プロジェクトです。
クライアントは AddAuthenticationStateDeserialization を呼び出して、サーバーによって渡された認証状態を逆シリアル化して使用します。 認証状態は、WebAssembly アプリケーションの有効期間中は変わりません。
PersistentAuthenticationStateProvider
クラス (PersistentAuthenticationStateProvider.cs
) は、ページがサーバーでレンダリングされたときにページに保持されていたデータを探すことでユーザーの認証状態を決定するクライアント側 AuthenticationStateProvider です。 認証状態は、WebAssembly アプリケーションの有効期間中は変わりません。
ユーザーがログインまたはログアウトする必要がある場合は、ページ全体の再読み込みが必要です。
サンプル アプリでは、表示のためにユーザー名とメールアドレスだけを提供します。
名前とロール要求のみをシリアル化する
このセクションは、BFF 以外のパターン (Interactive Auto) と BFF パターン (Interactive Auto) とそのサンプル アプリにのみ適用されます。
Program
ファイルでは、SerializeAllClaimsを true
に設定することで、すべての要求がシリアル化されます。 CSR の名前とロール要求のみをシリアル化する場合は、オプションを削除するか、 false
に設定します。
JSON 構成プロバイダーを使用して構成を指定する (アプリ設定)
サンプル ソリューション プロジェクトでは、C# オートコンプリートを使用して構成設定を検出できるように、 ファイルで OIDC と JWT ベアラー認証を構成します。 通常、プロフェッショナル アプリは 、既定の JSON 構成プロバイダー などの OIDC オプションを 構成するために構成プロバイダーを使用します。 JSON 構成プロバイダーは、アプリ設定ファイル appsettings.json
/appsettings.{ENVIRONMENT}.json
から構成を読み込みます。ここで、 {ENVIRONMENT}
プレースホルダーはアプリの ランタイム環境です。 このセクションのガイダンスに従って、構成にアプリ設定ファイルを使用します。
appsettings.json
またはBlazorWebAppOidc
プロジェクトのアプリ設定ファイル (BlazorWebAppOidcServer
) に、次の JSON 構成を追加します。
"Authentication": {
"Schemes": {
"MicrosoftOidc": {
"Authority": "https://login.microsoftonline.com/{TENANT ID (BLAZOR APP)}/v2.0/",
"ClientId": "{CLIENT ID (BLAZOR APP)}",
"CallbackPath": "/signin-oidc",
"SignedOutCallbackPath": "/signout-callback-oidc",
"RemoteSignOutPath": "/signout-oidc",
"SignedOutRedirectUri": "/",
"Scope": [
"openid",
"profile",
"offline_access",
"{APP ID URI (WEB API)}/Weather.Get"
]
}
}
},
上記の構成のプレースホルダーを、 Program
ファイルでアプリが使用する値と一致するように更新します。
-
{TENANT ID (BLAZOR APP)}
: Blazor アプリのテナント ID。 -
{CLIENT ID (BLAZOR APP)}
: Blazor アプリのクライアント ID。 -
{APP ID URI (WEB API)}
: Web API のアプリ ID URI。
マルチテナント アプリには、"共通" 機関 (https://login.microsoftonline.com/common/v2.0/
) を使用する必要があります。 シングルテナント アプリに対して "共通" 機関を使用するには、「 シングルテナント アプリの "共通" 機関を使用する」 セクションを参照してください。
上記の構成内の他の値を、 Program
ファイルで使用されているカスタム値または既定値以外の値と一致するように更新します。
構成は、認証ビルダーによって自動的に取得されます。
Program
ファイルから次の行を削除します。
- oidcOptions.Scope.Add(OpenIdConnectScope.OpenIdProfile);
- oidcOptions.Scope.Add("...");
- oidcOptions.CallbackPath = new PathString("...");
- oidcOptions.SignedOutCallbackPath = new PathString("...");
- oidcOptions.RemoteSignOutPath = new PathString("...");
- oidcOptions.Authority = "...";
- oidcOptions.ClientId = "...";
ConfigureCookieOidc
のCookieOidcServiceCollectionExtensions.cs
メソッドで、次の行を削除します。
- oidcOptions.Scope.Add(OpenIdConnectScope.OfflineAccess);
MinimalApiJwt
プロジェクトで、次のアプリ設定構成をappsettings.json
ファイルに追加します。
"Authentication": {
"Schemes": {
"Bearer": {
"Authority": "https://sts.windows.net/{TENANT ID (WEB API)}/",
"ValidAudiences": [ "{APP ID URI (WEB API)}" ]
}
}
},
上記の構成のプレースホルダーを、 Program
ファイルでアプリが使用する値と一致するように更新します。
-
{TENANT ID (WEB API)}
: Web API のテナント ID。 -
{APP ID URI (WEB API)}
: Web API のアプリ ID URI。
機関の形式には、次のパターンが採用されています。
- ME-ID テナントの種類:
https://sts.windows.net/{TENANT ID}/
- B2C テナントの種類:
https://login.microsoftonline.com/{TENANT ID}/v2.0/
対象ユーザー形式では、次のパターンが採用されます ({CLIENT ID}
は Web API のクライアント ID です。 {DIRECTORY NAME}
はディレクトリ名です (たとえば、 contoso
)。
- ME-ID テナントの種類:
api://{CLIENT ID}
- B2C テナントの種類:
https://{DIRECTORY NAME}.onmicrosoft.com/{CLIENT ID}
構成は、JWT ベアラー認証ビルダーによって自動的に取得されます。
Program
ファイルから次の行を削除します。
- jwtOptions.Authority = "...";
- jwtOptions.Audience = "...";
構成の詳細については、次のリソースを参照してください。
シングルテナント アプリの "共通" 機関を使用する
シングルテナント アプリには "共通" 機関を使用できますが、カスタム発行者検証ツールを実装するには次の手順を実行する必要があります。
Microsoft.IdentityModel.Validators
NuGet パッケージをBlazorWebAppOidc
、BlazorWebAppOidcServer
、またはBlazorWebAppOidcBff
プロジェクトに追加します。
メモ
.NET アプリへのパッケージの追加に関するガイダンスについては、「パッケージ利用のワークフロー」 (NuGet ドキュメント) の "パッケージのインストールと管理" に関する記事を参照してください。 NuGet.org で正しいパッケージ バージョンを確認します。
Program
ファイルの先頭で、Microsoft.IdentityModel.Validators名前空間を使用できるようにします。
using Microsoft.IdentityModel.Validators;
OIDC オプションが構成されている Program
ファイルで、次のコードを使用します。
var microsoftIssuerValidator =
AadIssuerValidator.GetAadIssuerValidator(oidcOptions.Authority);
oidcOptions.TokenValidationParameters.IssuerValidator =
microsoftIssuerValidator.Validate;
詳細については、 OpenID Connect を使用した SecurityTokenInvalidIssuerException と Azure AD の "共通" エンドポイント (AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet
#1731) に関するページを参照してください。
ログアウト時にホーム ページにリダイレクトする
LogInOrOut
コンポーネント (Layout/LogInOrOut.razor
) は、戻り値の URL (ReturnUrl
) の非表示フィールドを、現在の URL (currentURL
) に設定します。 ユーザーがアプリからサイン アウトすると、ID プロバイダーはログ アウト元のページにユーザーを返します。ユーザーがセキュリティで保護されたページからログ アウトすると、同じセキュリティで保護されたページに戻され、認証プロセスを通じて送信されます。 この認証フローは、ユーザーがアカウントを定期的に変更する必要がある場合に適しています。
または、次の LogInOrOut
コンポーネントを使用します。このコンポーネントでは、ログアウト時に戻り URL が提供されません。
Layout/LogInOrOut.razor
:
<div class="nav-item px-3">
<AuthorizeView>
<Authorized>
<form action="authentication/logout" method="post">
<AntiforgeryToken />
<button type="submit" class="nav-link">
<span class="bi bi-arrow-bar-left-nav-menu" aria-hidden="true">
</span> Logout
</button>
</form>
</Authorized>
<NotAuthorized>
<a class="nav-link" href="authentication/login">
<span class="bi bi-person-badge-nav-menu" aria-hidden="true"></span>
Login
</a>
</NotAuthorized>
</AuthorizeView>
</div>
トークンの更新
カスタム cookie リフレッシャー (CookieOidcRefresher.cs
) の実装では、期限が切れるとユーザーの要求が自動的に更新されます。 現在の実装では、更新トークンと引き換えにトークン エンドポイントか ID トークンを受け取る必要があります。 この ID トークン内の要求は、ユーザーの要求を上書きするために使用されます。
サンプル実装には、トークンの更新時に UserInfo エンドポイント から要求を要求するためのコードは含まれません。 詳細については、 BlazorWebAppOidc AddOpenIdConnect with GetClaimsFromUserInfoEndpoint = true doesn't propogate [sic] role claims to client
(dotnet/aspnetcore
#58826) を参照してください。
メモ
一部の ID プロバイダーは、 更新トークンを使用する場合にのみアクセス トークンを返します。
CookieOidcRefresher
を追加のロジックで更新して、認証 cookie に保存されている以前の要求セットを引き続き使用することも、アクセス トークンを使用して UserInfo エンドポイントからの要求をリクエストすることもできます。
暗号化 nonce
nonce は、リプレイ攻撃を軽減するためにクライアントのセッションを ID トークンに関連付ける文字列値です。
認証の開発とテスト中に nonce エラーが発生した場合は、古い cookie データによって nonce エラーが発生する可能性があるため、アプリまたはテスト ユーザーに加えられた変更がどれだけ小さくても、テスト実行ごとに新しい InPrivate/incognito ブラウザー セッションを使用します。 詳細については、「 Cookie とサイト データ 」セクションを参照してください。
更新トークンが新しいアクセス トークンと交換されるときには、nonce が要求されたり使用されることはありません。 サンプル アプリでは、CookieOidcRefresher
(CookieOidcRefresher.cs
) が意図的に OpenIdConnectProtocolValidator.RequireNonce を false
に設定しています。
Microsoft Entra (ME-ID) に登録されていないアプリのアプリケーション ロール
このセクションでは、ID プロバイダーとして Microsoft Entra ID (ME-ID) を使用しないアプリについて説明します。 ME-ID で登録されているアプリについては、「 Microsoft Entra に登録されているアプリのアプリケーション ロール (ME-ID) 」セクションを参照してください。
TokenValidationParameters.RoleClaimType の OpenIdConnectOptions でロールクレームタイプ (Program.cs
) を設定します。
oidcOptions.TokenValidationParameters.RoleClaimType = "{ROLE CLAIM TYPE}";
多くの OIDC ID プロバイダーの場合、ロール要求の種類は role
です。 ID プロバイダーのドキュメントで正しい値を確認します。
UserInfo
プロジェクトの BlazorWebAppOidc.Client
クラスを、次のクラスに置き換えます。
UserInfo.cs
:
using Microsoft.AspNetCore.Components.WebAssembly.Authentication;
using System.Security.Claims;
namespace BlazorWebAppOidc.Client;
// Add properties to this class and update the server and client
// AuthenticationStateProviders to expose more information about
// the authenticated user to the client.
public sealed class UserInfo
{
public required string UserId { get; init; }
public required string Name { get; init; }
public required string[] Roles { get; init; }
public const string UserIdClaimType = "sub";
public const string NameClaimType = "name";
private const string RoleClaimType = "role";
public static UserInfo FromClaimsPrincipal(ClaimsPrincipal principal) =>
new()
{
UserId = GetRequiredClaim(principal, UserIdClaimType),
Name = GetRequiredClaim(principal, NameClaimType),
Roles = principal.FindAll(RoleClaimType).Select(c => c.Value)
.ToArray(),
};
public ClaimsPrincipal ToClaimsPrincipal() =>
new(new ClaimsIdentity(
Roles.Select(role => new Claim(RoleClaimType, role))
.Concat([
new Claim(UserIdClaimType, UserId),
new Claim(NameClaimType, Name),
]),
authenticationType: nameof(UserInfo),
nameType: NameClaimType,
roleType: RoleClaimType));
private static string GetRequiredClaim(ClaimsPrincipal principal,
string claimType) =>
principal.FindFirst(claimType)?.Value ??
throw new InvalidOperationException(
$"Could not find required '{claimType}' claim.");
}
この時点で、 Razor コンポーネントは ロールベースおよびポリシーベースの承認を採用できます。 アプリケーション ロールは、role
要求 (ロールごとに 1 つの要求) に表示されます。
Microsoft Entra (ME-ID) に登録されているアプリのアプリケーション ロール
このセクションのガイダンスを使用して、 Microsoft Entra ID (ME-ID) を使用して、アプリのアプリケーション ロール、ME-ID セキュリティ グループ、および組み込みの管理者ロール ME-ID 実装します。
このセクションで説明しているアプローチでは、認証 cookie ヘッダーでグループとロールを送信するように ME-ID を構成します。 ユーザーが少数のセキュリティ グループおよびロールのメンバーのみである場合は、ほとんどのホスティング プラットフォームに対して次のアプローチが効果的で、ヘッダーが長すぎる問題 (たとえば、既定のヘッダー長制限が 16 KB (MaxRequestBytes
) の IIS ホスティングの場合など) が発生することはありません。 ヘッダーの長さが、グループまたはロールのメンバーシップ数が多いために問題となる場合は、このセクションのガイダンスに従うのではなく、Microsoft Graph を実装して、ME-ID からユーザーのグループとロールを個別に取得することをお勧めします。この方法は、認証ヘッダーのサイズを増やすことがありません。 詳細については、「 無効な要求 - 要求が長すぎます - IIS サーバー (dotnet/aspnetcore
#57545)」を参照してください。
TokenValidationParameters.RoleClaimType の OpenIdConnectOptions でロール要求の種類 (Program.cs
) を構成します。 その値を roles
に設定します。
oidcOptions.TokenValidationParameters.RoleClaimType = "roles";
ME-ID Premium アカウントを持たない グループにロールを割り当て ることはできませんが、ユーザーにロールを割り当て、標準の Azure アカウントを持つユーザーのロール要求を受け取ることができます。 このセクションのガイダンスでは、ME-ID Premium アカウントは必要ありません。
既定のディレクトリを使用する場合は、「appRoles
エントリでアプリのロールを手動で確立します。 詳細については、 ロール要求の構成 (ME-ID ドキュメント) を参照してください。
ユーザーの Azure セキュリティ グループはgroups
要求として届き、ユーザーの組み込みの ME-ID 管理者ロールの割り当ては既知の ID (wids
) 要求として届きます。 両方のクレームタイプの値は GUID です。 アプリによって受信されると、これらの要求を使用して、Razor コンポーネントでロールとポリシーの承認を確立できます。
Azure portal のアプリのマニフェストで、 groupMembershipClaims
属性 を All
に設定します。
All
の値を設定すると、ME-ID はサインインしたすべてのセキュリティおよび配布グループ (groups
要求) とロール (wids
要求) を送信します。
groupMembershipClaims
属性を設定するには、
- Azure portal で [アプリの登録] を開きます。
- サイドバーで [ 管理>Manifest ] を選択します。
-
groupMembershipClaims
属性を見つけます。 - 値を
All
に設定します ("groupMembershipClaims": "All"
)。 - [保存] ボタンを選択します。
UserInfo
プロジェクトの BlazorWebAppOidc.Client
クラスを、次のクラスに置き換えます。
UserInfo.cs
:
using Microsoft.AspNetCore.Components.WebAssembly.Authentication;
using System.Security.Claims;
namespace BlazorWebAppOidc.Client;
// Add properties to this class and update the server and client
// AuthenticationStateProviders to expose more information about
// the authenticated user to the client.
public sealed class UserInfo
{
public required string UserId { get; init; }
public required string Name { get; init; }
public required string[] Roles { get; init; }
public required string[] Groups { get; init; }
public required string[] Wids { get; init; }
public const string UserIdClaimType = "sub";
public const string NameClaimType = "name";
private const string RoleClaimType = "roles";
private const string GroupsClaimType = "groups";
private const string WidsClaimType = "wids";
public static UserInfo FromClaimsPrincipal(ClaimsPrincipal principal) =>
new()
{
UserId = GetRequiredClaim(principal, UserIdClaimType),
Name = GetRequiredClaim(principal, NameClaimType),
Roles = principal.FindAll(RoleClaimType).Select(c => c.Value)
.ToArray(),
Groups = principal.FindAll(GroupsClaimType).Select(c => c.Value)
.ToArray(),
Wids = principal.FindAll(WidsClaimType).Select(c => c.Value)
.ToArray(),
};
public ClaimsPrincipal ToClaimsPrincipal() =>
new(new ClaimsIdentity(
Roles.Select(role => new Claim(RoleClaimType, role))
.Concat(Groups.Select(role => new Claim(GroupsClaimType, role)))
.Concat(Wids.Select(role => new Claim(WidsClaimType, role)))
.Concat([
new Claim(UserIdClaimType, UserId),
new Claim(NameClaimType, Name),
]),
authenticationType: nameof(UserInfo),
nameType: NameClaimType,
roleType: RoleClaimType));
private static string GetRequiredClaim(ClaimsPrincipal principal,
string claimType) =>
principal.FindFirst(claimType)?.Value ??
throw new InvalidOperationException(
$"Could not find required '{claimType}' claim.");
}
この時点で、 Razor コンポーネントは ロールベースおよびポリシーベースの承認を採用できます。
- アプリケーション ロールは、
roles
要求 (ロールごとに 1 つの要求) に表示されます。 - セキュリティ グループは、グループごとに 1 つの要求として
groups
要求に表示されます。 セキュリティ グループ GUID は、セキュリティ グループの作成時に Azure portal に表示され、 Identity>Overview>Groups>View を選択すると一覧表示されます。 - 組み込みの ME-ID 管理者ロールは、
wids
要求 (ロールごとに 1 つの要求) に表示されます。wids
の値を持つb79fbf4d-3ef9-4689-8143-76b194e85509
要求は、テナントの非ゲストアカウントに対して常に ME-ID によって送信され、管理者ロールは参照されません。 管理者ロール GUID (ロール テンプレート ID) は、役割と管理者を選択した後に省略記号 (……) をクリックすると、Azure ポータルに一覧表示されているロールの>に表示されます。 ロール テンプレート ID は、 Microsoft Entra 組み込みロール (Entra ドキュメント) にも記載されています。
トラブルシューティング
ログの記録
サーバー アプリは標準の ASP.NET Core アプリです。 サーバー アプリでログ 記録レベルを下げるには、 ASP.NET Core のログ記録に関するガイダンスを参照してください。
Blazor WebAssembly認証のデバッグまたはトレースログを有効にするには、記事バージョンセレクターを.NET 7 以降のASP.NET Coreに設定して、のBlazorのセクションを参照してください。
一般的なエラー
Microsoft Entra 外部 ID を使用してログアウト中にデバッガーが例外で中断する
次の例外は、 Microsoft Entra External ID を使用したログアウト中に Visual Studio デバッガーを停止します。
Uncaught TypeError TypeError: Failed to execute 'postMessage' on 'Window': The provided value cannot be converted to a sequence.
例外は Entra JavaScript コードからスローされるため、これは ASP.NET Core の問題ではありません。 この例外は運用環境のアプリ機能に影響しないため、ローカル開発テスト中に例外を無視できます。
アプリまたは Identity プロバイダー (IP) の構成の誤り
最も一般的なエラーの原因は、構成の誤りです。 以下に例を示します。
- シナリオの要件によっては、権限、インスタンス、テナント ID、テナント ドメイン、クライアント ID、またはリダイレクト URI の欠落または誤りによって、アプリによるクライアントの認証ができなくなります。
- 要求スコープが正しくないと、クライアントはサーバー Web API エンドポイントにアクセスできません。
- サーバー API のアクセス許可が正しくないか、存在しないと、クライアントがサーバー Web API エンドポイントにアクセスできなくなります。
- IP のアプリ登録のリダイレクト URI で構成されているものとは異なるポートでアプリが実行されています。 Microsoft Entra ID と、
localhost
開発テスト アドレスで実行されるアプリにポートは必要ありませんが、アプリのポート構成とアプリが実行されているポートは、localhost
以外のアドレスと一致する必要があることに注意してください。
この記事の構成範囲では、正しい構成の例を示しています。 アプリと IP の構成に誤りがないか、構成を慎重に確認してください。
構成が正しい場合:
アプリケーション ログを分析します。
ブラウザーの開発者ツールを使用して、クライアント アプリと IP またはサーバー アプリの間のネットワーク トラフィックを確認します。 多くの場合、要求を行った後、IP またはサーバー アプリによって、問題の原因を特定する手掛かりを含む正確なエラー メッセージまたはメッセージがクライアントに返されます。 開発者ツールのガイダンスは、次の記事にあります。
- Google Chrome (Google ドキュメント)
- Microsoft Edge
- Mozilla Firefox (Mozilla ドキュメント)
ドキュメント チームは、ドキュメントのフィードバックや記事のバグ ( このページ のフィードバック セクションから問題を開く) に応答しますが、製品サポートを提供できません。 アプリのトラブルシューティングに役立つ、いくつかのパブリック サポート フォーラムが用意されています。 次をお勧めします。
上記のフォーラムは、Microsoft によって所有または管理されていません。
セキュリティに関するものでなく、機密性や守秘性のない再現可能なフレームワークのバグレポートについては、ASP.NET Core 製品ユニットに問題を報告してください。 問題の原因を徹底的に調査し、パブリック サポート フォーラムのコミュニティの助けを借りてもお客様自身で解決できない場合にのみ、製品単位でイシューを作成してください。 単純な構成の誤りやサードパーティのサービスに関連するユース ケースによって破損した個々のアプリのトラブルシューティングは、製品単位で行うことはできません。 レポートが機密性の高い性質のものである場合や、攻撃者が悪用するおそれのある製品の潜在的なセキュリティ上の欠陥が記述されている場合は、「
dotnet/aspnetcore
」 ( GitHub リポジトリ) をご覧ください。ME-IDに認可されていないクライアント
情報:Microsoft.AspNetCore.Authorization.DefaultAuthorizationService[2] 承認に失敗しました。 次の要件が満たされていません。DenyAnonymousAuthorizationRequirement:認証済みユーザーが必要です。
ME-ID からのログイン コールバック エラー:
- エラー:
unauthorized_client
- 説明:
AADB2C90058: The provided application is not configured to allow public clients.
このエラーを解決するには:
- Azure portal で、 アプリのマニフェストにアクセスします。
-
allowPublicClient
属性をnull
またはtrue
に設定します。
- エラー:
Cookie とサイト データ
Cookie とサイト データは、アプリが更新されても保持され、テストやトラブルシューティングに影響する可能性があります。 アプリ コードの変更、プロバイダーによるユーザー アカウントの変更、プロバイダー アプリの構成変更を行うときは、次のものをクリアしてください。
- ユーザーのサインインの Cookie
- アプリの Cookie
- キャッシュおよび保存されたサイト データ
残った Cookie とサイト データがテストとトラブルシューティングに影響しないようにする方法を、次に示します。
- ブラウザーを構成する
- ブラウザーが閉じるたびに cookie とサイト データをすべて削除するように構成できることをテストするために、ブラウザーを使用します。
- アプリ、テスト ユーザー、プロバイダー構成が変更されるたびにブラウザーが手動で、または IDE によって閉じられていることを確認します。
- カスタム コマンドを使用して、Visual Studio でブラウザーを InPrivate または Incognito モードで開きます:
- Visual Studio の
「実行」アイコンを使用して 「Browse With」 ダイアログ ボックスを開きます。 - [ 追加 ] ボタンを選択します。
- [ プログラム ] フィールドにブラウザーへのパスを指定します。 次の実行可能パスが、Windows 10 の一般的なインストール場所です。 ブラウザーが別の場所にインストールされている場合、または Windows 10 を使用していない場合は、ブラウザーの実行可能ファイルのパスを指定してください。
- Microsoft Edge:
C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe
- Google Chrome:
C:\Program Files (x86)\Google\Chrome\Application\chrome.exe
- Mozilla Firefox:
C:\Program Files\Mozilla Firefox\firefox.exe
- Microsoft Edge:
- [ 引数 ] フィールドで、ブラウザーが InPrivate または Incognito モードで開くために使用するコマンド ライン オプションを指定します。 ブラウザーによっては、アプリの URL が必要になる場合があります。
- Microsoft Edge:
-inprivate
を使用してください。 - Google Chrome:
--incognito --new-window {URL}
を使用します。プレースホルダー{URL}
は開く URL (たとえば、https://localhost:5001
など) です。 - Mozilla Firefox:
-private -url {URL}
を使用します。プレースホルダー{URL}
は開く URL (たとえば、https://localhost:5001
など) です。
- Microsoft Edge:
-
フレンドリ名フィールドに名前を入力してください。 たとえば、
Firefox Auth Testing
のようにします。 - [ OK ] ボタンを選択します。
- アプリを使用してテストを繰り返すたびにブラウザー プロファイルを選択する必要がないようにするには、[既定に設定] ボタンを使用してプロファイルを既定値 として設定 します。
- アプリ、テスト ユーザー、またはプロバイダー構成が変更されるたびに、ブラウザーが IDE によって閉じられていることを確認します。
- Visual Studio の
アプリのアップグレード
開発マシンで .NET Core SDK をアップグレードしたり、アプリ内のパッケージ バージョンを変更したりした直後に、機能しているアプリが失敗することがあります。 場合によっては、パッケージに統一性がないと、メジャー アップグレード実行時にアプリが破壊されることがあります。 これらの問題のほとんどは、次の手順で解決できます。
- コマンド シェルから
dotnet nuget locals all --clear
を実行して、ローカル システムの NuGet パッケージ キャッシュをクリアします。 - プロジェクトのフォルダー
bin
とobj
を削除します。 - プロジェクトを復元してリビルドします。
- アプリを再展開する前に、サーバー上の展開フォルダー内のすべてのファイルを削除します。
メモ
アプリのターゲット フレームワークと互換性のないパッケージ バージョンの使用はサポートされていません。 パッケージの詳細については、 NuGet ギャラリーを使用します。
適切なプロジェクトからソリューションを開始する
Blazor Web Apps:
- Backend-for-Frontend (BFF) パターン サンプルの 1 つについては、
Aspire/Aspire.AppHost
プロジェクトからソリューションを開始します。 - BFF 以外のパターン サンプルの 1 つについては、 サーバー プロジェクトからソリューションを開始します。
Blazor Server:
サーバー プロジェクトからソリューションを開始します。
ユーザーを検査する
次の UserClaims
コンポーネントは、アプリ内で直接使うことも、さらにカスタマイズするための基礎として使うこともできます。
UserClaims.razor
:
@page "/user-claims"
@using System.Security.Claims
@using Microsoft.AspNetCore.Authorization
@attribute [Authorize]
<PageTitle>User Claims</PageTitle>
<h1>User Claims</h1>
@if (claims.Any())
{
<ul>
@foreach (var claim in claims)
{
<li><b>@claim.Type:</b> @claim.Value</li>
}
</ul>
}
@code {
private IEnumerable<Claim> claims = Enumerable.Empty<Claim>();
[CascadingParameter]
private Task<AuthenticationState>? AuthState { get; set; }
protected override async Task OnInitializedAsync()
{
if (AuthState == null)
{
return;
}
var authState = await AuthState;
claims = authState.User.Claims;
}
}
その他のリソース
-
AzureAD/microsoft-identity-web
GitHub リポジトリ: Microsoft Identity の実装に関する役立つガイダンスサンプル アプリと関連する Azure ドキュメントへのリンクを含む、ASP.NET Core アプリ用の Microsoft Entra ID と Azure Active Directory B2C の Web。 現在、Azure のドキュメントでは Blazor Web App について明記されていませんが、ME-ID と Azure ホスティング向けの Blazor Web App のセットアップと構成は、ASP.NET Core Web アプリの場合と同じです。 -
AuthenticationStateProvider
サービス - Blazor Web Appの認証状態を管理する
-
OIDC (Blazor #55213) を使用
dotnet/aspnetcore
対話型サーバーでの http 要求中にトークンを更新する - Blazor Web App を使用して、のデータを保護する
-
AuthenticationStateProvider
からDelegatingHandler
にアクセスする方法
ASP.NET Core