OpenID Connect (OIDC) を使用して ASP.NET Core Blazor Web App をセキュリティで保護する
Note
これは、この記事の最新バージョンではありません。 現在のリリースについては、この記事の .NET 9 バージョンを参照してください。
重要
この情報はリリース前の製品に関する事項であり、正式版がリリースされるまでに大幅に変更される可能性があります。 Microsoft はここに示されている情報について、明示か黙示かを問わず、一切保証しません。
現在のリリースについては、この記事の .NET 9 バージョンを参照してください。
この記事では、Blazor Web App GitHub リポジトリ (.NET 8 以降) のサンプル アプリを使用して、dotnet/blazor-samples
で をセキュリティ保護する方法について説明します (ダウンロード方法)。
このバージョンの記事では、Backend for Frontend (BFF) パターンを採用せずに OIDC を実装する方法について説明します。 BFF パターンは、外部サービスに対して認証された要求を行う場合に役立ちます。 アプリの仕様上 BFF パターンの採用が必要な場合は、記事のバージョン セレクターを OIDC と BFF パターンに切り替えてください。
以下の仕様をカバーします。
- Blazor Web App は、グローバル インタラクティビティを持つ自動レンダー モードを使用します。
- ユーザーの認証状態をキャプチャし、それをサーバーとクライアントの間でフローするために、サーバーおよびクライアント アプリによってカスタム認証状態プロバイダー サービスが使用されます。
- このアプリは、すべての OIDC 認証フローの開始点です。 OIDC は、アプリ内で手動により構成され、Microsoft Entra ID や Microsoft Identity Web パッケージに依存せず、サンプル アプリも Microsoft Azure ホスティングを必要としません。 ただし、サンプル アプリは Entra や Microsoft Identity Web で使用でき、Azure でホストできます。
- 非対話形式でのトークンの自動更新。
- データのサーバー プロジェクトで (Web) API を安全に呼び出します。
サンプル アプリ
サンプル アプリは、以下の 2 つのプロジェクトで構成されます。
BlazorWebAppOidc
: 気象データ用の Blazor Web App エンドポイントの例を含む、 のサーバー側プロジェクト。BlazorWebAppOidc.Client
: Blazor Web App のクライアント側プロジェクト。
次のリンクを使用して、リポジトリのルートから最新バージョンのフォルダーを介してサンプル アプリにアクセスします。 これらのプロジェクトは、.NET 8 以降用の BlazorWebAppOidc
フォルダー内にあります。
サンプル コードを表示またはダウンロードします (ダウンロード方法)。
サーバー側の Blazor Web App プロジェクト (BlazorWebAppOidc
)
BlazorWebAppOidc
プロジェクトは、Blazor Web App のサーバー側プロジェクトです。
BlazorWebAppOidc.http
ファイルは、気象データの要求のテストに使用できます。 エンドポイントをテストするには BlazorWebAppOidc
プロジェクトが実行されている必要があり、エンドポイントはファイルにハードコーディングされていることに注意してください。 詳細については、「Visual Studio 2022 で .http ファイルを使う」を参照してください。
Note
サーバー プロジェクトは IHttpContextAccessor/HttpContext を使いますが、対話形式でレンダリングされるコンポーネントには使いません。 詳細については、対話型サーバー側の ASP.NET Core Blazor レンダリングの脅威軽減策に関するガイダンスに関する記事を参照してください。
構成
このセクションでは、サンプル アプリを構成する方法について説明します。
Note
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 の実装の詳細については、リンクされているリソースを参照してください。
クライアント シークレットを確立する
警告
アプリ シークレット、接続文字列、資格情報、パスワード、個人識別番号 (PIN)、プライベート C#/.NET コード、秘密キー/トークンをクライアント側コードに格納しないでください。これは安全ではありません。 テスト/ステージング環境と運用環境では、サーバー側の Blazor コードと Web API は、プロジェクト コードまたは構成ファイル内で資格情報を維持しないように、セキュリティで保護された認証フローを使用する必要があります。 ローカル開発テスト以外では、環境変数が最も安全なアプローチではないため、環境変数を使用して機密データを格納しないようにすることをお勧めします。 ローカル開発テストでは、機密データをセキュリティで保護するために、 Secret Manager ツール をお勧めします。 詳細については、「 機密データと資格情報を安全に管理するを参照してください。
ローカル開発テストの場合は、 Secret Manager ツール を使用して、サーバー アプリのクライアント シークレットを構成キー Authentication:Schemes:MicrosoftOidc:ClientSecret
の下に格納します。
Note
アプリで Microsoft Entra ID または Azure AD B2C を使用する場合は、Entra または Azure portal でアプリの登録にクライアント シークレットを作成します (Manage>Certificates & secrets>New クライアント シークレット)。 次のガイダンスでは、新しいシークレットの Value を使用します。
サンプル アプリはシークレット マネージャー ツール用に初期化されていません。 Visual Studio の Developer PowerShell コマンド シェルなどのコマンド シェルを使用して、次のコマンドを実行します。 コマンドを実行する前に、 cd
コマンドを使用してサーバー プロジェクトのディレクトリにディレクトリを変更します。 このコマンドは、ユーザー シークレット識別子 (サーバー アプリのプロジェクト ファイル内の<UserSecretsId>
) を確立します。
dotnet user-secrets init
次のコマンドを実行して、クライアント シークレットを設定します。 {SECRET}
プレースホルダーは、アプリの登録から取得したクライアント シークレットです。
dotnet user-secrets set "Authentication:Schemes:MicrosoftOidc:ClientSecret" "{SECRET}"
Visual Studio を使用している場合は、ソリューション エクスプローラーでサーバー プロジェクトを右クリックし、[ユーザー シークレットの管理] 選択することで、シークレットが設定されていることを確認。
Configure the app
次の OpenIdConnectOptions の構成は、プロジェクトの Program
ファイルに含まれる AddOpenIdConnect の呼び出しにあります。
SignInScheme: 認証の成功後に、ユーザーの identity の保持を担当するミドルウェア対応の認証スキームを設定します。 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 内に保存する必要があるかどうかを定義します。 このプロパティは、最終的な認証
false
のサイズを小さくするために、cookie に設定されます。oidcOptions.SaveTokens = false;
オフライン アクセスのスコープ (Scope):
offline_access
スコープが更新トークンのために必要です。oidcOptions.Scope.Add(OpenIdConnectScope.OfflineAccess);
Authority と ClientId: OIDC 呼び出しのためのオーソリティとクライアント ID を設定します。
oidcOptions.Authority = "{AUTHORITY}"; oidcOptions.ClientId = "{CLIENT ID}";
例:
- オーソリティ (
{AUTHORITY}
):https://login.microsoftonline.com/aaaabbbb-0000-cccc-1111-dddd2222eeee/v2.0/
(テナント IDaaaabbbb-0000-cccc-1111-dddd2222eeee
を使用) - クライアント ID (
{CLIENT ID}
):00001111-aaaa-2222-bbbb-3333cccc4444
oidcOptions.Authority = "https://login.microsoftonline.com/aaaabbbb-0000-cccc-1111-dddd2222eeee/v2.0/"; oidcOptions.ClientId = "00001111-aaaa-2222-bbbb-3333cccc4444";
Microsoft Azure の "common" オーソリティの例:
"common" オーソリティは、マルチテナント アプリに対して使用する必要があります。 "common" オーソリティをシングルテナント アプリに対して使用することもできますが、このセクションで後ほど示すように、カスタムの IssuerValidator が必要になります。
oidcOptions.Authority = "https://login.microsoftonline.com/common/v2.0/";
- オーソリティ (
ResponseType: 認可コード フローのみを実行するように、OIDC ハンドラーを構成します。 このモードでは、暗黙的な許可とハイブリッド フローは必要ありません。
Entra または Azure portal の暗黙的な許可とハイブリッド フローのアプリ登録構成では、アクセス トークンまたは ID トークンを返すための承認エンドポイントのチェックボックスはどちらも選択しないでください。 OIDC ハンドラーは、承認エンドポイントから返されたコードを使用して適切なトークンを自動的に要求します。
oidcOptions.ResponseType = OpenIdConnectResponseType.Code;
MapInboundClaims および NameClaimType と RoleClaimType の構成: 多くの OIDC サーバーは、
name
の既定値 SOAP/WS-Fed ではなく、"role
" と "ClaimTypes" を使用します。 MapInboundClaims がfalse
に設定されていると、ハンドラーは要求のマッピングを実行せず、JWT からの要求名がアプリによって直接使用されます。 次の例では、ロール要求の種類を "roles
" に設定します。これは、Microsoft Entra ID (ME-ID) に適しています。 詳細については、お使いの identity プロバイダーのドキュメントを調べてください。Note
ほとんどの 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: ユーザーエージェントが返される、アプリのベース パス内の要求パス。
Entra または Azure portal で、以下のように Web プラットフォーム構成のリダイレクト URI 内のパスを設定します。
https://localhost/signin-oidc
Note
Microsoft Entra ID を使う場合、
localhost
アドレスにポートは必要ありません。 他のほとんどの OIDC プロバイダーでは、正しいポートが必要です。SignedOutCallbackPath: アプリのベース パス内の要求パス。identity プロバイダーからサインアウトした後でユーザー エージェントが返される。
Entra または Azure portal で、以下のように Web プラットフォーム構成のリダイレクト URI 内のパスを設定します。
https://localhost/signout-callback-oidc
Note
Microsoft Entra ID を使う場合、
localhost
アドレスにポートは必要ありません。 他のほとんどの OIDC プロバイダーでは、正しいポートが必要です。Note
Microsoft Identity Web を使用していて、SignedOutCallbackPath オーソリティ (
microsoftonline.com
) が使用されている場合、プロバイダーは現在、https://login.microsoftonline.com/{TENANT ID}/v2.0/
にのみリダイレクトを行います。 Microsoft Identity Web で "common" オーソリティを使用できる場合は、この制限は存在しません。 詳細については、「オーソリティ URL にテナント ID が含まれていると postLogoutRedirectUri が機能しない (AzureAD/microsoft-authentication-library-for-js
#5783)」を参照してください。RemoteSignOutPath: このパスで要求を受け取ると、ハンドラーはサインアウト スキームを使用してサインアウトを呼び出します。
Entra または Azure portal で、以下のようにフロントチャネル ログアウト URL を設定します。
https://localhost/signout-oidc
Note
Microsoft Entra ID を使う場合、
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 と Azure AD の "common" エンドポイントでの SecurityTokenInvalidIssuerException (AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet
#1731)」を参照してください。Microsoft Entra ID または Azure AD B2C と "common" エンドポイントを使用するアプリの場合のみ:
var microsoftIssuerValidator = AadIssuerValidator.GetAadIssuerValidator(oidcOptions.Authority); oidcOptions.TokenValidationParameters.IssuerValidator = microsoftIssuerValidator.Validate;
サンプル アプリ コード
サンプル アプリで以下の機能を調べます。
- カスタム cookie リフレッシャー (
CookieOidcRefresher.cs
) を使用する非対話形式でのトークンの自動更新。 - サーバー プロジェクトは AddAuthenticationStateSerialization を呼び出して、 PersistentComponentState を使用して認証状態をクライアントにフローするサーバー側認証状態プロバイダーを追加します。 クライアントは AddAuthenticationStateDeserialization を呼び出して、サーバーによって渡された認証状態を逆シリアル化して使用します。 認証状態は、WebAssembly アプリケーションの有効期間中は変わりません。
- Blazor Web App への気象データの要求の例は、
/weather-forecast
ファイル (Program
) の Minimal API エンドポイント (Program.cs
) によって処理されます。 このエンドポイントは、RequireAuthorization を呼び出すことで認可を要求します。 プロジェクトに追加するすべてのコントローラーについて、[Authorize]
属性をコントローラーまたはアクションに追加します。 - アプリは、気象データのサーバー プロジェクトで (Web) API を安全に呼び出します。
Weather
コンポーネントをサーバーでレンダリングする場合、コンポーネントはサーバーでServerWeatherForecaster
を使用して気象データを直接取得します (Web API 呼び出しを使用しません)。- コンポーネントをクライアントでレンダリングする場合、コンポーネントは
ClientWeatherForecaster
サービス実装を使用します。この実装では、事前に構成した HttpClient (クライアント プロジェクトのProgram
ファイル内) を使用して、サーバー プロジェクトへの Web API 呼び出しが行われます。 サーバー プロジェクトの/weather-forecast
ファイルで定義されている最小 API エンドポイント (Program
) は、ServerWeatherForecaster
から気象データを取得し、そのデータをクライアントに返します。
- カスタム cookie リフレッシャー (
CookieOidcRefresher.cs
) を使用する非対話形式でのトークンの自動更新。 PersistingAuthenticationStateProvider
クラス (PersistingAuthenticationStateProvider.cs
) は、AuthenticationStateProvider を使用してクライアントに認証状態をフローするサーバー側 PersistentComponentState であり、WebAssembly アプリケーションの有効期間中は固定されます。- Blazor Web App への気象データの要求の例は、
/weather-forecast
ファイル (Program
) の Minimal API エンドポイント (Program.cs
) によって処理されます。 このエンドポイントは、RequireAuthorization を呼び出すことで認可を要求します。 プロジェクトに追加するすべてのコントローラーについて、[Authorize]
属性をコントローラーまたはアクションに追加します。 - アプリは、気象データのサーバー プロジェクトで (Web) API を安全に呼び出します。
Weather
コンポーネントをサーバーでレンダリングする場合、コンポーネントはサーバーでServerWeatherForecaster
を使用して気象データを直接取得します (Web API 呼び出しを使用しません)。- コンポーネントをクライアントでレンダリングする場合、コンポーネントは
ClientWeatherForecaster
サービス実装を使用します。この実装では、事前に構成した HttpClient (クライアント プロジェクトのProgram
ファイル内) を使用して、サーバー プロジェクトへの Web API 呼び出しが行われます。 サーバー プロジェクトの/weather-forecast
ファイルで定義されている最小 API エンドポイント (Program
) は、ServerWeatherForecaster
から気象データを取得し、そのデータをクライアントに返します。
Blazor Web App のサービス抽象化を利用した (Web) API 呼び出しの詳細については、ASP.NET Core Blazor アプリからの Web API の呼び出しに関するページをご覧ください。
クライアント側 Blazor Web App プロジェクト (BlazorWebAppOidc.Client
)
BlazorWebAppOidc.Client
プロジェクトは、Blazor Web App のクライアント側プロジェクトです。
クライアントは AddAuthenticationStateDeserialization を呼び出して、サーバーによって渡された認証状態を逆シリアル化して使用します。 認証状態は、WebAssembly アプリケーションの有効期間中は変わりません。
PersistentAuthenticationStateProvider
クラス (PersistentAuthenticationStateProvider.cs
) は、ページがサーバーでレンダリングされたときにページに保持されていたデータを探すことでユーザーの認証状態を決定するクライアント側 AuthenticationStateProvider です。 認証状態は、WebAssembly アプリケーションの有効期間中は変わりません。
ユーザーがログインまたはログアウトする必要がある場合は、ページ全体の再読み込みが必要です。
サンプル アプリでは、表示のためにユーザー名とメールアドレスだけを提供します。 以降の要求を行うときにサーバーに対する認証を行うトークンは含まれません。これは、サーバーへの cookie 要求に含まれる HttpClient を使用して別に処理されます。
このバージョンの記事では、Backend for Frontend (BFF) パターンを使用して OIDC を実装する方法について説明します。 アプリの仕様上 BFF パターンを採用する必要がない場合は、記事のバージョン セレクターを BFF パターンを使用しない OIDC に変更してください。
以下の仕様をカバーします。
- Blazor Web App は、グローバル インタラクティビティを持つ自動レンダー モードを使用します。
- ユーザーの認証状態をキャプチャし、それをサーバーとクライアントの間でフローするために、サーバーおよびクライアント アプリによってカスタム認証状態プロバイダー サービスが使用されます。
- このアプリは、すべての OIDC 認証フローの開始点です。 OIDC は、アプリ内で手動により構成され、Microsoft Entra ID や Microsoft Identity Web パッケージに依存せず、サンプル アプリも Microsoft Azure ホスティングを必要としません。 ただし、サンプル アプリは Entra や Microsoft Identity Web で使用でき、Azure でホストできます。
- 非対話形式でのトークンの自動更新。
- Backend for Frontend (BFF) パターンは、サービス検出のために .NET Aspire を使用し、要求をバックエンド アプリの天気予報エンドポイントにプロキシするために YARP を使用することで実現されます。
- バックエンド Web API は、JWT ベアラー認証を使用して、サインイン Blazor Web App の cookie によって保存された JWT トークンを検証します。
- Aspire は、.NET クラウドネイティブ アプリを構築するエクスペリエンスを強化します。 これは、分散型アプリを構築して実行するための、一貫性のある、厳格なツールとパターンのセットを提供します。
- YARP (Yet Another Reverse Proxy) は、リバース プロキシ サーバーの作成に使用されるライブラリです。
.NET Aspire の詳細については、「.NET Aspire の一般提供: .NET クラウドネイティブ開発の簡略化 (2024 年 5 月)」をご覧ください。
前提条件
.NET Aspire には、Visual Studio バージョン 17.10 以降が必要です。
サンプル アプリ
サンプル アプリは、以下の 5 つのプロジェクトで構成されます。
- .NET Aspire:
Aspire.AppHost
: アプリの高レベルのオーケストレーションに関することを管理するために使用されます。Aspire.ServiceDefaults
: 必要に応じて拡張してカスタマイズできる ..NET Aspire の既定のアプリ構成が含まれます。
MinimalApiJwt
: 気象データ用の Minimal API エンドポイントの例を含むバックエンド Web API。BlazorWebAppOidc
: Blazor Web App のサーバー側プロジェクト。BlazorWebAppOidc.Client
: Blazor Web App のクライアント側プロジェクト。
次のリンクを使用して、リポジトリのルートから最新バージョンのフォルダーを介してサンプル アプリにアクセスします。 これらのプロジェクトは、.NET 8 以降用の BlazorWebAppOidcBff
フォルダー内にあります。
サンプル コードを表示またはダウンロードします (ダウンロード方法)。
.NET Aspire 件のプロジェクト
.NET Aspire の使用方法の詳細と、サンプル アプリの .AppHost
と .ServiceDefaults
プロジェクトの詳細については、「.NET Aspire のドキュメント」をご覧ください。
.NET Aspire の前提条件を満たしていることを確認します。 詳細については、.NET Aspireの前提条件セクションをご覧ください。
サンプル アプリでは、開発テスト中に使用する、安全でない HTTP 起動プロファイル (http
) のみが構成されます。 安全でない起動設定プロファイルと安全な起動設定プロファイルの例を含む詳細については、.NET Aspire で安全でない転送を許可する (.NET Aspire ドキュメント) をご覧ください。
サーバー側の Blazor Web App プロジェクト (BlazorWebAppOidc
)
BlazorWebAppOidc
プロジェクトは、Blazor Web App のサーバー側プロジェクトです。 このプロジェクトでは、バックエンド Web API プロジェクト () の天気予報エンドポイントに要求をプロキシするための MinimalApiJwt
と、認証 access_token
内に保存された cookie を使用します。
BlazorWebAppOidc.http
ファイルは、気象データの要求のテストに使用できます。 エンドポイントをテストするには BlazorWebAppOidc
プロジェクトが実行されている必要があり、エンドポイントはファイルにハードコーディングされていることに注意してください。 詳細については、「Visual Studio 2022 で .http ファイルを使う」を参照してください。
Note
サーバー プロジェクトは IHttpContextAccessor/HttpContext を使いますが、対話形式でレンダリングされるコンポーネントには使いません。 詳細については、対話型サーバー側の ASP.NET Core Blazor レンダリングの脅威軽減策に関するガイダンスに関する記事を参照してください。
構成
このセクションでは、サンプル アプリを構成する方法について説明します。
Note
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 の実装の詳細については、リンクされているリソースを参照してください。
クライアント シークレットを確立する
警告
アプリ シークレット、接続文字列、資格情報、パスワード、個人識別番号 (PIN)、プライベート C#/.NET コード、秘密キー/トークンをクライアント側コードに格納しないでください。これは安全ではありません。 テスト/ステージング環境と運用環境では、サーバー側の Blazor コードと Web API は、プロジェクト コードまたは構成ファイル内で資格情報を維持しないように、セキュリティで保護された認証フローを使用する必要があります。 ローカル開発テスト以外では、環境変数が最も安全なアプローチではないため、環境変数を使用して機密データを格納しないようにすることをお勧めします。 ローカル開発テストでは、機密データをセキュリティで保護するために、 Secret Manager ツール をお勧めします。 詳細については、「 機密データと資格情報を安全に管理するを参照してください。
ローカル開発テストの場合は、 Secret Manager ツール を使用して、サーバー アプリのクライアント シークレットを構成キー Authentication:Schemes:MicrosoftOidc:ClientSecret
の下に格納します。
Note
アプリで Microsoft Entra ID または Azure AD B2C を使用する場合は、Entra または Azure portal でアプリの登録にクライアント シークレットを作成します (Manage>Certificates & secrets>New クライアント シークレット)。 次のガイダンスでは、新しいシークレットの Value を使用します。
サンプル アプリはシークレット マネージャー ツール用に初期化されていません。 Visual Studio の Developer PowerShell コマンド シェルなどのコマンド シェルを使用して、次のコマンドを実行します。 コマンドを実行する前に、 cd
コマンドを使用してサーバー プロジェクトのディレクトリにディレクトリを変更します。 このコマンドは、ユーザー シークレット識別子 (サーバー アプリのプロジェクト ファイル内の<UserSecretsId>
) を確立します。
dotnet user-secrets init
次のコマンドを実行して、クライアント シークレットを設定します。 {SECRET}
プレースホルダーは、アプリの登録から取得したクライアント シークレットです。
dotnet user-secrets set "Authentication:Schemes:MicrosoftOidc:ClientSecret" "{SECRET}"
Visual Studio を使用している場合は、ソリューション エクスプローラーでサーバー プロジェクトを右クリックし、[ユーザー シークレットの管理] 選択することで、シークレットが設定されていることを確認。
Configure the app
次の OpenIdConnectOptions の構成は、プロジェクトの Program
ファイルに含まれる AddOpenIdConnect の呼び出しにあります。
SignInScheme: 認証の成功後に、ユーザーの identity の保持を担当するミドルウェア対応の認証スキームを設定します。 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):
Weather.Get
スコープは、Azure portal または Entra ポータルの [APIの公開] で構成します。 これは、バックエンド Web API プロジェクト (MinimalApiJwt
) がベアラー JWT でアクセス トークンを検証するために必要です。oidcOptions.Scope.Add("{APP ID URI}/{API NAME}");
例:
- アプリ ID URI (
{APP ID URI}
):https://{DIRECTORY NAME}.onmicrosoft.com/{CLIENT ID}
- ディレクトリ名 (
{DIRECTORY NAME}
):contoso
- アプリケーション (クライアント) ID (
{CLIENT ID}
):00001111-aaaa-2222-bbbb-3333cccc4444
- ディレクトリ名 (
MinimalApiJwt
({API NAME}
) からの気象データ用に構成されたスコープ:Weather.Get
oidcOptions.Scope.Add("https://contoso.onmicrosoft.com/00001111-aaaa-2222-bbbb-3333cccc4444/Weather.Get");
前の例は、種類が AAD B2C テナントであるテナントに登録されたアプリに関するものです。 アプリが ME-ID テナントに登録されている場合、アプリ ID URI が異なるため、スコープも異なります。
例:
- アプリ ID URI (
{APP ID URI}
):api://{CLIENT ID}
、アプリケーション (クライアント) ID ({CLIENT ID}
):00001111-aaaa-2222-bbbb-3333cccc4444
MinimalApiJwt
({API NAME}
) からの気象データ用に構成されたスコープ:Weather.Get
oidcOptions.Scope.Add("api://00001111-aaaa-2222-bbbb-3333cccc4444/Weather.Get");
- アプリ ID URI (
Authority と ClientId: OIDC 呼び出しのためのオーソリティとクライアント ID を設定します。
oidcOptions.Authority = "{AUTHORITY}"; oidcOptions.ClientId = "{CLIENT ID}";
例:
- オーソリティ (
{AUTHORITY}
):https://login.microsoftonline.com/aaaabbbb-0000-cccc-1111-dddd2222eeee/v2.0/
(テナント IDaaaabbbb-0000-cccc-1111-dddd2222eeee
を使用) - クライアント ID (
{CLIENT ID}
):00001111-aaaa-2222-bbbb-3333cccc4444
oidcOptions.Authority = "https://login.microsoftonline.com/aaaabbbb-0000-cccc-1111-dddd2222eeee/v2.0/"; oidcOptions.ClientId = "00001111-aaaa-2222-bbbb-3333cccc4444";
Microsoft Azure の "common" オーソリティの例:
"common" オーソリティは、マルチテナント アプリに対して使用する必要があります。 "common" オーソリティをシングルテナント アプリに対して使用することもできますが、このセクションで後ほど示すように、カスタムの IssuerValidator が必要になります。
oidcOptions.Authority = "https://login.microsoftonline.com/common/v2.0/";
- オーソリティ (
ResponseType: 認可コード フローのみを実行するように、OIDC ハンドラーを構成します。 このモードでは、暗黙的な許可とハイブリッド フローは必要ありません。
Entra または Azure portal の暗黙的な許可とハイブリッド フローのアプリ登録構成では、アクセス トークンまたは ID トークンを返すための承認エンドポイントのチェックボックスはどちらも選択しないでください。 OIDC ハンドラーは、承認エンドポイントから返されたコードを使用して適切なトークンを自動的に要求します。
oidcOptions.ResponseType = OpenIdConnectResponseType.Code;
MapInboundClaims および NameClaimType と RoleClaimType の構成: 多くの OIDC サーバーは、
name
の既定値 SOAP/WS-Fed ではなく、"role
" と "ClaimTypes" を使用します。 MapInboundClaims がfalse
に設定されていると、ハンドラーは要求のマッピングを実行せず、JWT からの要求名がアプリによって直接使用されます。 次の例では、ロール要求の種類を "roles
" に設定します。これは、Microsoft Entra ID (ME-ID) に適しています。 詳細については、お使いの identity プロバイダーのドキュメントを調べてください。Note
ほとんどの 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: ユーザーエージェントが返される、アプリのベース パス内の要求パス。
Entra または Azure portal で、以下のように Web プラットフォーム構成のリダイレクト URI 内のパスを設定します。
https://localhost/signin-oidc
Note
localhost
アドレスにはポートは必要ありません。SignedOutCallbackPath: アプリのベース パス内の要求パス。identity プロバイダーからサインアウトした後でユーザー エージェントが返される。
Entra または Azure portal で、以下のように Web プラットフォーム構成のリダイレクト URI 内のパスを設定します。
https://localhost/signout-callback-oidc
Note
localhost
アドレスにはポートは必要ありません。Note
Microsoft Identity Web を使用していて、SignedOutCallbackPath オーソリティ (
microsoftonline.com
) が使用されている場合、プロバイダーは現在、https://login.microsoftonline.com/{TENANT ID}/v2.0/
にのみリダイレクトを行います。 Microsoft Identity Web で "common" オーソリティを使用できる場合は、この制限は存在しません。 詳細については、「オーソリティ URL にテナント ID が含まれていると postLogoutRedirectUri が機能しない (AzureAD/microsoft-authentication-library-for-js
#5783)」を参照してください。RemoteSignOutPath: このパスで要求を受け取ると、ハンドラーはサインアウト スキームを使用してサインアウトを呼び出します。
Entra または Azure portal で、以下のようにフロントチャネル ログアウト URL を設定します。
https://localhost/signout-oidc
Note
localhost
アドレスにはポートは必要ありません。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 と Azure AD の "common" エンドポイントでの SecurityTokenInvalidIssuerException (AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet
#1731)」を参照してください。Microsoft Entra ID または Azure AD B2C と "common" エンドポイントを使用するアプリの場合のみ:
var microsoftIssuerValidator = AadIssuerValidator.GetAadIssuerValidator(oidcOptions.Authority); oidcOptions.TokenValidationParameters.IssuerValidator = microsoftIssuerValidator.Validate;
サンプル アプリ コード
サンプル アプリで以下の機能を調べます。
- カスタム cookie リフレッシャー (
CookieOidcRefresher.cs
) を使用する非対話形式でのトークンの自動更新。 - サーバー プロジェクトは AddAuthenticationStateSerialization を呼び出して、 PersistentComponentState を使用して認証状態をクライアントにフローするサーバー側認証状態プロバイダーを追加します。 クライアントは AddAuthenticationStateDeserialization を呼び出して、サーバーによって渡された認証状態を逆シリアル化して使用します。 認証状態は、WebAssembly アプリケーションの有効期間中は変わりません。
- Blazor Web App への要求は、バックエンド Web API プロジェクト (
MinimalApiJwt
) にプロキシされます。MapForwarder
ファイル内のProgram
は、発信要求の既定の構成、カスタマイズされた変換、既定の HTTP クライアントを使用して、指定されたパターンに一致する HTTP 要求の特定の宛先への直接転送を追加します。Weather
コンポーネントをサーバーでレンダリングする場合、コンポーネントはServerWeatherForecaster
を使用し、ユーザーのアクセス トークンで気象データの要求をプロキシします。- コンポーネントをクライアントでレンダリングする場合、コンポーネントは
ClientWeatherForecaster
サービス実装を使用します。この実装では、事前に構成した HttpClient (クライアント プロジェクトのProgram
ファイル内) を使用して、サーバー プロジェクトへの Web API 呼び出しが行われます。 サーバー プロジェクトの/weather-forecast
ファイルで定義されている最小 API エンドポイント (Program
) は、ユーザーのアクセス トークンで要求を変換し、気象データを取得します。
- カスタム cookie リフレッシャー (
CookieOidcRefresher.cs
) を使用する非対話形式でのトークンの自動更新。 PersistingAuthenticationStateProvider
クラス (PersistingAuthenticationStateProvider.cs
) は、AuthenticationStateProvider を使用してクライアントに認証状態をフローするサーバー側 PersistentComponentState であり、WebAssembly アプリケーションの有効期間中は固定されます。- Blazor Web App への要求は、バックエンド Web API プロジェクト (
MinimalApiJwt
) にプロキシされます。MapForwarder
ファイル内のProgram
は、発信要求の既定の構成、カスタマイズされた変換、既定の HTTP クライアントを使用して、指定されたパターンに一致する HTTP 要求の特定の宛先への直接転送を追加します。Weather
コンポーネントをサーバーでレンダリングする場合、コンポーネントはServerWeatherForecaster
を使用し、ユーザーのアクセス トークンで気象データの要求をプロキシします。- コンポーネントをクライアントでレンダリングする場合、コンポーネントは
ClientWeatherForecaster
サービス実装を使用します。この実装では、事前に構成した HttpClient (クライアント プロジェクトのProgram
ファイル内) を使用して、サーバー プロジェクトへの Web API 呼び出しが行われます。 サーバー プロジェクトの/weather-forecast
ファイルで定義されている最小 API エンドポイント (Program
) は、ユーザーのアクセス トークンで要求を変換し、気象データを取得します。
Blazor Web App のサービス抽象化を利用した (Web) API 呼び出しの詳細については、ASP.NET Core Blazor アプリからの Web API の呼び出しに関するページをご覧ください。
クライアント側 Blazor Web App プロジェクト (BlazorWebAppOidc.Client
)
BlazorWebAppOidc.Client
プロジェクトは、Blazor Web App のクライアント側プロジェクトです。
クライアントは AddAuthenticationStateDeserialization を呼び出して、サーバーによって渡された認証状態を逆シリアル化して使用します。 認証状態は、WebAssembly アプリケーションの有効期間中は変わりません。
PersistentAuthenticationStateProvider
クラス (PersistentAuthenticationStateProvider.cs
) は、ページがサーバーでレンダリングされたときにページに保持されていたデータを探すことでユーザーの認証状態を決定するクライアント側 AuthenticationStateProvider です。 認証状態は、WebAssembly アプリケーションの有効期間中は変わりません。
ユーザーがログインまたはログアウトする必要がある場合は、ページ全体の再読み込みが必要です。
サンプル アプリでは、表示のためにユーザー名とメールアドレスだけを提供します。 以降の要求を行うときにサーバーに対する認証を行うトークンは含まれません。これは、サーバーへの cookie 要求に含まれる HttpClient を使用して別に処理されます。
バックエンド Web API プロジェクト (MinimalApiJwt
)
MinimalApiJwt
プロジェクトは、複数のフロントエンド プロジェクト用のバックエンド Web API です。 このプロジェクトでは、気象データ用の Minimal API エンドポイントを構成します。 Blazor Web App サーバー側プロジェクト (BlazorWebAppOidc
) からの要求は、MinimalApiJwt
プロジェクトにプロキシされます。
構成
プロジェクトの JwtBearerOptions ファイルに含まれる AddJwtBearer 呼び出しの Program
で以下のようにプロジェクトを構成します。
Audience: 受け取った OpenID Connect トークンに対して対象ユーザーを設定します。
Azure portal または Entra ポータル: 以下のように [API の公開] で
Weather.Get
スコープを追加するときに構成されたアプリケーション ID URI のパスに値を一致させます。jwtOptions.Audience = "{APP ID URI}";
例:
アプリ ID URI (
{APP ID URI}
):https://{DIRECTORY NAME}.onmicrosoft.com/{CLIENT ID}
:- ディレクトリ名 (
{DIRECTORY NAME}
):contoso
- アプリケーション (クライアント) ID (
{CLIENT ID}
):00001111-aaaa-2222-bbbb-3333cccc4444
jwtOptions.Audience = "https://contoso.onmicrosoft.com/00001111-aaaa-2222-bbbb-3333cccc4444";
前の例は、種類が AAD B2C テナントであるテナントに登録されたアプリに関するものです。 アプリが ME-ID テナントに登録されている場合、アプリ ID URI が異なるため、対象ユーザーも異なります。
例:
アプリ ID URI (
{APP ID URI}
):api://{CLIENT ID}
、アプリケーション (クライアント) ID ({CLIENT ID}
):00001111-aaaa-2222-bbbb-3333cccc4444
jwtOptions.Audience = "api://00001111-aaaa-2222-bbbb-3333cccc4444";
- ディレクトリ名 (
Authority: OpenID Connect の呼び出しを行うためのオーソリティを設定します。 以下のように
BlazorWebAppOidc/Program.cs
で OIDC ハンドラーに対して構成されているオーソリティに値を一致させます。jwtOptions.Authority = "{AUTHORITY}";
例:
オーソリティ (
{AUTHORITY}
):https://login.microsoftonline.com/aaaabbbb-0000-cccc-1111-dddd2222eeee/v2.0/
(テナント IDaaaabbbb-0000-cccc-1111-dddd2222eeee
を使用)jwtOptions.Authority = "https://login.microsoftonline.com/aaaabbbb-0000-cccc-1111-dddd2222eeee/v2.0/";
前の例は、種類が AAD B2C テナントであるテナントに登録されたアプリに関するものです。 アプリが ME-ID テナントに登録されている場合、オソリティは
iss
プロバイダーから返された JWT の発行者 (identity) と一致する必要があります。jwtOptions.Authority = "https://sts.windows.net/aaaabbbb-0000-cccc-1111-dddd2222eeee/";
気象データ用の Minimal API
以下のようにプロジェクトの 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]
属性をコントローラーまたはアクションに追加します。
サインアウト時に home ページにリダイレクトする
ユーザーがアプリ内を移動すると、LogInOrOut
コンポーネント (Layout/LogInOrOut.razor
) によって、戻り URL (ReturnUrl
) の非表示フィールドが現在の URL (currentURL
) の値に設定されます。 ユーザーがアプリからサインアウトすると、identity プロバイダーはサインアウト元のページにユーザーを返します。
ユーザーがセキュリティで保護されたページからサインアウトする場合、サインアウト後に同じセキュリティで保護されたページに戻され、認証プロセスを再び受けることになります。 この動作は、ユーザーが頻繁にアカウントを切り替える必要がある場合には問題ありません。 しかし、アプリの仕様によっては、サインアウト後にユーザーをアプリの home ページまたは他のページに戻すことが必要な場合があります。 次の例は、サインアウト操作の戻り URL としてアプリの home ページを設定する方法を示しています。
次の例では、LogInOrOut
コンポーネントに対する重要な変更が示されています。 既定のパスであるため、ReturnUrl
のhome ページに設定されている/
の非表示フィールドを指定する必要はありません。 IDisposable は実装されなくなりました。 NavigationManager は挿入されなくなりました。 @code
ブロック全体が削除されます。
Layout/LogInOrOut.razor
:
@using Microsoft.AspNetCore.Authorization
<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 @context.User.Identity?.Name
</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 role claims to client
(dotnet/aspnetcore
#58826)を参照してください。
Note
一部の identity プロバイダー 、更新トークンを使用する場合にのみアクセス トークンを返します。 CookieOidcRefresher
を追加のロジックで更新して、認証 cookie に格納されている要求の以前のセットを引き続き使用するか、アクセス トークンを使用して UserInfo エンドポイントから要求を要求できます。
暗号化 nonce
nonce は、リプレイ攻撃を軽減するためにクライアントのセッションを ID トークンに関連付ける文字列値です。
認証の開発とテスト中に nonce エラーが発生した場合は、古い cookie データによって nonce エラーが発生する可能性があるため、アプリまたはテスト ユーザーに加えられた変更がどれだけ小さくても、テスト実行ごとに新しい InPrivate/incognito ブラウザー セッションを使用します。 詳細については、「Cookie とサイト データ」セクションをご覧ください。
更新トークンが新しいアクセス トークンと交換されるときには、nonce が要求されたり使用されることはありません。 サンプル アプリでは、CookieOidcRefresher
(CookieOidcRefresher.cs
) が意図的に OpenIdConnectProtocolValidator.RequireNonce を false
に設定しています。
Microsoft Entra (ME-ID) に登録されていないアプリのアプリケーション ロール
このセクションは、 プロバイダーとして identity を使用しないアプリに関連しています。 ME-ID で登録されたアプリについては、「Microsoft Entra (ME-ID) に登録されているアプリのアプリケーション ロール」セクションをご覧ください。
TokenValidationParameters.RoleClaimType の OpenIdConnectOptions でロール要求の種類 (Program.cs
) を構成します。
oidcOptions.TokenValidationParameters.RoleClaimType = "{ROLE CLAIM TYPE}";
多くの OIDC identity プロバイダーの場合、ロール要求の種類は role
になります。 identity プロバイダーのドキュメントで正しい値を確認します。
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 から個別に取得することをお勧めします。このアプローチでは、認証 cookie のサイズが大きくなりません。 詳細については、「要求が正しくありません - 要求が長すぎます - IIS サーバー (dotnet/aspnetcore
#57545)」をご覧ください。
TokenValidationParameters.RoleClaimType の OpenIdConnectOptions でロール要求の種類 (Program.cs
) を構成します。 その値を roles
に設定します。
oidcOptions.TokenValidationParameters.RoleClaimType = "roles";
ME-ID Premium アカウントがないとグループにロールを割り当てることはできませんが、Standard Azure アカウントを使用して、ユーザーにロールを割り当て、ユーザーに対するロール要求を受け取ることができます。 このセクションのガイダンスでは、ME-ID Premium アカウントは必要ありません。
既定のディレクトリを使用する場合は、「アプリケーションにアプリ ロールを追加してトークンで受け取る (ME-ID ドキュメント) 」のガイダンスに従い、ロールを構成して割り当てます。 既定のディレクトリを使用していない場合は、Azure portal でアプリのマニフェストを編集し、マニフェスト ファイルの appRoles
エントリでアプリのロールを手動で確立します。 詳しくは、「ロール要求の構成 (ME-ID ドキュメント)」をご覧ください。
ユーザーの Azure セキュリティ グループが groups
要求に到着し、ユーザーの組み込みの ME-ID 管理者ロールの割り当てが既知の ID (wids
) 要求に到着します。 両方の要求の種類の値は GUID です。 アプリで受信されると、これらの要求を使用して、Razor コンポーネントでロールとポリシーの承認を確立できます。
Azure portal のアプリ マニフェストで、groupMembershipClaims
属性を All
に設定します。 All
の値を設定すると、ME-ID はサインインしたすべてのセキュリティおよび配布グループ (groups
要求) とロール (wids
要求) を送信します。 groupMembershipClaims
属性を次に設定します。
- Azure portal で [アプリの登録] を開きます。
- サイド バーで [管理]>[マニフェスト] を選択します。
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 portal で Roles & admins を選択すると表示され、その後に、省略記号 (...)、>Descriptionで表示されるロールの説明が続きます。 ロール テンプレート ID は、「Microsoft Entra 組み込みロール (Entra ドキュメント)」にも記載されています。
トラブルシューティング
ログ機能
サーバー アプリは標準の ASP.NET Core アプリです。 サーバー アプリでより下位のログ レベルを有効にするには、ASP.NET Core ログのガイダンスを参照してください。
Blazor WebAssembly 認証のデバッグまたはトレース ログを有効にするには、記事バージョン セレクターを ASP.NET Core 7.0 以降に設定して、Blazorの "クライアント側認証ログ" セクションを参照してください。
一般的なエラー
アプリまたは 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 の [実行] ボタンをクリックして [ブラウザーの選択] ダイアログボックスを開きます。
- [追加] ボタンを選びます。
- [プログラム] フィールドでブラウザーのパスを指定します。 次の実行可能パスが、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 によって閉じられていることを確認します。
アプリのアップグレード
開発マシンで .NET Core SDK をアップグレードしたり、アプリ内のパッケージ バージョンを変更したりした直後に、機能しているアプリが失敗することがあります。 場合によっては、パッケージに統一性がないと、メジャー アップグレード実行時にアプリが破壊されることがあります。 これらの問題のほとんどは、次の手順で解決できます。
- コマンド シェルから
dotnet nuget locals all --clear
を実行して、ローカル システムの NuGet パッケージ キャッシュをクリアします。 - プロジェクトのフォルダー
bin
とobj
を削除します。 - プロジェクトを復元してリビルドします。
- アプリを再展開する前に、サーバー上の展開フォルダー内のすべてのファイルを削除します。
Note
アプリのターゲット フレームワークと互換性のないパッケージ バージョンの使用はサポートされていません。 パッケージの詳細については、NuGet ギャラリーまたは FuGet パッケージ エクスプローラーを使用してください。
サーバー アプリを実行する
Blazor Web App のテストとトラブルシューティングを行うときは、サーバー プロジェクトからアプリを実行していることを確認してください。
ユーザーを検査する
次の 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 リポジトリ: ASP.NET Core アプリのための Microsoft Entra ID と Azure Active Directory B2C 向けの Microsoft Identity Web の実装に関して役に立つガイダンス。サンプル アプリと関連する Azure ドキュメントへのリンクが含まれます。 現在、Azure のドキュメントでは Blazor Web App について明記されていませんが、ME-ID と Azure ホスティング向けの Blazor Web App のセットアップと構成は、ASP.NET Core Web アプリの場合と同じです。AuthenticationStateProvider
サービス (service)- Blazor Web App で認証状態を管理する
- OIDC を使用した Blazor Interactive Server での http 要求中の更新トークン(
dotnet/aspnetcore
#55213)
ASP.NET Core