【ITニュース解説】Blazor: Bootstrap, AuthenticationStateProvider, and Repository Pattern
2026年10月14日に「Dev.to」が公開したITニュース「Blazor: Bootstrap, AuthenticationStateProvider, and Repository Pattern」について初心者にもわかりやすく解説しています。
ITニュース概要
Blazorでは、AuthenticationStateProviderでユーザー認証状態をコンポーネントに提供し、UI表示を制御する。Repositoryパターンはデータベース操作を抽象化し、保守性を高める。セキュリティ確保には、フロントエンドの認証だけでなく、バックエンドでの厳密な認証とビジネスルール適用が不可欠だ。
ITニュース解説
Blazorは、C#というプログラミング言語を使ってウェブアプリケーションのフロントエンド(ユーザーインターフェース)とバックエンド(サーバー側の処理)の両方を開発できるMicrosoftの技術である。通常、ウェブアプリケーションではJavaScriptがフロントエンドで使われるが、Blazorを使うとC#だけで完結できるため、システムエンジニアを目指す初心者にとっても学習コストを抑えやすい利点がある。
現代のウェブアプリケーションにおいて、ユーザー認証は非常に重要な機能だ。例えば、ジム会員管理システムでは、誰でも利用可能なジムやプランの閲覧はできるが、実際に会員登録やプランへの加入といった重要な操作には、そのユーザーが誰であるかを確認する「認証」が必要となる。Blazorには、AuthenticationStateProviderという仕組みがあり、これはコンポーネント(ウェブページの一部)が現在の認証状態、つまり「誰がログインしているのか」「そのユーザーはどんな情報(クレーム)を持っているのか」という情報を取得できるようにする抽象的な機能を提供する。
AuthenticationStateProviderは、現在の認証状態をコンポーネントに提供するBlazorのクラスで、主に以下のことを助ける。ユーザーが認証されているかどうかの判断、現在のユーザーに利用可能なクレーム(名前、IDなどの情報)の種類、認証されているユーザー向けのコンテンツを表示するか、未認証のユーザー向けのコンテンツを表示するかといったUIの切り替えである。この機能は、JWT(JSON Web Token)をバックエンドで検証したり、APIエンドポイントを保護したりするものではない。それらのセキュリティ検証はあくまでバックエンド側が責任を持って行う必要がある。AuthenticationStateProviderは、フロントエンド側で認証状態に応じたUIの表示を切り替えるためのものと理解すると良い。
具体的な例を見てみよう。あるBlazorのコンポーネント内で、現在の認証状態を読み取るには、まず@inject AuthenticationStateProvider AuthStateProviderという記述でAuthenticationStateProviderをコンポーネント内に注入する。コンポーネントが初期化される際に実行されるOnInitializedAsyncというライフサイクルメソッドの中で、await AuthStateProvider.GetAuthenticationStateAsync()を呼び出し、現在の認証状態を取得する。この認証状態から、authenticationState.Userというプロパティを通じて現在のユーザー情報(ClaimsPrincipalという型)にアクセスできる。そして、user.Identity?.IsAuthenticated == trueという条件でユーザーが認証されているかを確認し、その結果に基づいて表示するメッセージを変えることができる。例えば、認証されていれば「ようこそ、[ユーザー名]さん!」、未認証なら「メンバーシップにアクセスするにはログインしてください」といった具合だ。このように、コンポーネント内で認証システム全体を実装することなく、認証状態に応じてUIを動的に変更できるのがAuthenticationStateProviderのメリットである。
さらに、BlazorにはAuthorizeViewというコンポーネントも用意されている。これは、現在の認証状態や認可状態(そのユーザーが特定のアクションを実行する権限を持っているか)に基づいて、表示するコンテンツを切り替えることができる。例えば、<Authorized>タグの中には認証されたユーザーにだけ見せる内容を書き、<NotAuthorized>タグの中には未認証のユーザーに見せる内容を書くことで、簡単にUIを出し分けることができる。このようなUIの表示は、BootstrapのようなCSSフレームワークを使って見た目を整えることができる。ただし、ボタンやページの一部を隠すことは、あくまでユーザー体験を向上させるためのものであり、機密性の高い操作を保護するものではない。本当に重要な操作のセキュリティは、必ずバックエンドAPI側で認証と認可のチェックを行う必要がある。
次に、ジム会員管理システムの会員登録プロセスを例に、システム全体の流れを理解しよう。会員が登録フォームを送信すると、そのリクエストはBlazor WebAssemblyで構築されたフロントエンドから、ASP.NET Core Web APIで構築されたバックエンドへと送られる。バックエンドでは、まずSubscription Controllerがリクエストを受け取り、認証されたユーザーを特定する。その後、CreateSubscriptionCommandというオブジェクトを作成し、MediatR Command Handlerを通じてSubscription Serviceへと渡される。Subscription Serviceは、ビジネスルールに沿ってリクエストの検証を行い、必要な計算処理を実行する。データベースへのアクセスは、ISubscriptionRepositoryというインターフェースと、それを実装したSubscriptionRepositoryを通じて行われる。SubscriptionRepositoryは、Entity Framework CoreというO/Rマッパー(オブジェクトとリレーショナルデータベースのマッピングツール)を使って、SQL Serverデータベースからデータを取得したり、保存したりする役割を担う。このような層の分離は、アプリケーションを保守しやすく、テストしやすく、拡張しやすくするために非常に有効な設計手法である。
CreateSubscriptionCommand.csは、会員登録リクエストを処理するために必要なデータを含むクラスだ。例えば、UserId、GymId、MembershipPlanId、StartDate、PaymentMethod、TermsAcceptedといったプロパティを持つ。UserIdは、コントローラーが認証されたユーザーのIDをクレームから取得して設定する。これは、フロントエンドから送られてくるUserIdは信頼できないため、バックエンドで確実に検証されたIDを使用することがセキュリティ上不可欠だからだ。[Range]属性や[Required]属性は、入力値が有効な範囲内か、必須項目が入力されているかを自動的にチェックする機能だが、それだけでは不十分で、開始日が過去ではないか、利用規約に同意しているかなど、ビジネス固有の条件はSubscription Serviceで別途検証する必要がある。
ISubscriptionRepository.csは、Subscription Serviceがデータベース操作を行う際に必要となる「契約」を定義するインターフェースである。例えば、GetGymByIdAsync(特定のジムを取得)、GetMembershipPlanByIdAsync(特定のプランを取得)、HasActiveMembershipInGymAsync(指定されたジムにアクティブな会員資格があるか確認)、CountActiveMembershipsAsync(アクティブな会員資格の数をカウント)、AddSubscriptionAsync(新しい会員資格をデータベースに追加)といったメソッドが定義されている。このインターフェースは、具体的なデータベースへのアクセス方法(実装の詳細)を隠蔽し、サービス層がデータベースの詳細を意識せずに操作できるようにする。
SubscriptionRepository.csは、ISubscriptionRepositoryインターフェースの実装であり、実際にデータベース操作を行うクラスだ。コンストラクタでAppDbContextというEntity Framework Coreのデータベースコンテキストを受け取り、それを使ってデータベースとやり取りする。例えば、GetGymByIdAsyncメソッドでは、_context.GymCenters.AsNoTracking().FirstOrDefaultAsync(...)を使って、指定されたIDのジムをデータベースから検索する。ここで使われているAsNoTracking()は、取得したデータをEntity Framework Coreが変更追跡しないようにするもので、読み取り専用のクエリの場合にパフォーマンスを向上させる効果がある。FirstOrDefaultAsync()は条件に一致する最初の要素、または一致するものがなければnullを返す。HasActiveMembershipInGymAsyncメソッドでは、_context.Subscriptions.AnyAsync(...)を使って、指定された条件(ユーザーID、ジムID、ステータス、終了日など)に一致するアクティブな会員資格が存在するかどうかをtrueまたはfalseで返す。AddSubscriptionAsyncメソッドでは、新しい会員資格をデータベースに追加し、await _context.SaveChangesAsync(cancellationToken)を呼び出して変更を永続化する。CancellationTokenは、呼び出し元のリクエストがキャンセルされた場合に、データベース操作もキャンセルできるようにするための仕組みである。
認証と会員登録のプロセスを連携させる際には、フロントエンドとバックエンドで認証に関する責任が異なることを理解しておく必要がある。Blazor WebAssemblyのフロントエンドでは、カスタムのAuthenticationStateProviderが保存されたアクセストークンを読み取り、現在の認証状態を構築する。そして、保護されたAPIエンドポイントを呼び出す際には、そのアクセストークンをHTTPリクエストに付与して送信する。バックエンドでは、送られてきたトークンを検証し、認証されたユーザーを特定する。例えば、ASP.NET Coreのコントローラーでは、User.FindFirstValue(ClaimTypes.NameIdentifier)というコードを使って、認証されたトークンに含まれるユーザーID(GUID形式)を取得できる。この検証済みのユーザーIDをCreateSubscriptionCommand.UserIdに設定してからMediatRにコマンドを送信することが重要だ。フロントエンドが提供するユーザーIDは、セキュリティの観点から決して信用してはならない。
ビジネスルールも非常に重要だ。Subscription Serviceは、リポジトリから取得した情報に基づいて、会員資格の数に上限があるか、新しい会員資格が許可されるかといったビジネス上の判断を下す責任がある。有効期限の計算(例: startDate.Date.AddMonths(plan.DurationInMonths))や、会員プランの価格をデータベースから取得し、ブラウザから提供された価格を信用しないこと、サブスクリプションのタイムスタンプをバックエンドでUTC時間を使って割り当てることなども重要である。さらに、将来の日付で開始される会員資格は、その時点では「保留中」の状態である可能性があり、それがアクティブな会員資格の数にカウントされるべきかどうかのポリシーを明確にする必要がある。また、複数のリクエストが同時に発生した場合に、会員資格の数に関するチェックとデータベースへの挿入処理が競合し、意図しない結果を招く「競合状態」が発生する可能性がある。このような場合は、トランザクション管理やデータベースレベルの同時実行制御といった適切な戦略を用いて、データの一貫性を保証する必要がある。
結論として、BlazorのAuthenticationStateProviderは、フロントエンドのコンポーネントが現在の認証状態に応じてユーザーインターフェースを動的に変更するのに役立ち、Bootstrapは見た目の良いインターフェースを提供する。ジム会員管理システムでは、CreateSubscriptionCommandが登録詳細を伝え、ISubscriptionRepositoryがデータベース操作の定義を行い、SubscriptionRepositoryがEntity Framework Coreを使って実際のデータベース操作を実行する。このような全体的なアーキテクチャは、プレゼンテーション層、認証層、ビジネスロジック層、永続化層を明確に分離することで、システム全体の開発、保守、テストを容易にする。フロントエンドでの認証状態表示はユーザー体験のために重要だが、セキュアな会員登録処理を実現するためには、バックエンドでのトークン検証、信頼できるユーザーIDの取得、サービス層での厳密なビジネスルールの適用、そして正確なデータベース操作が不可欠である。