【C#/Blazor】実務Webアプリ開発編 (34)Blazorの認証の全体像 ~Cookie認証の仕組みと、設計の3つの論点~
実務Webアプリ開発編です。今回は、Blazor(サーバー側)の認証の全体像を整理します。
前回でDBの初期データまで入れ、永続化のパートが終わりました。今回から認証のパートです。
以下のような方に役立つ内容となっています。
この記事が役に立つ方
- Blazor(サーバー側)のCookie認証で、認証がどこで行われ、ログイン後の情報がどう保たれるのかを知りたい
- ロールを変えたのに画面に反映されないなど、ログイン後の情報が古いまま残る理由を知りたい
- 認証のコードをどの層に置くか、ログイン方式をあとから替えられるかで迷っている
以下のようなMentorAppを題材として進めます。

GitHubにドキュメント・コードの一式があります。
prota-p/MentorApp: Blazor Server × クリーンアーキテクチャ × DDD の学習用リファレンス実装(実務Webアプリ開発編の題材)
この回は全体像の回で、コードはほとんど読みません(見るのはインターフェースの宣言だけです)。認証がどこで起き、何を決めればよいかをつかみます。
認証は方式も用語も多くて、迷いやすいところです。この記事では、決めることを3つの論点に整理します。まず仕組みの基本を押さえて、その3つを順に見ていきましょう。
Blazorの認証の基本
最初に、サーバー側で動くBlazorで、認証がどこで起きるかを押さえます。この基本が、あとの3つの論点すべての土台です。
ここでのサーバー側とは、ページの表示も操作もサーバーで処理する構成(静的SSRとInteractiveServer)です。ブラウザで動かすWebAssemblyは含みません。
認証はHTTPの要求で行い、回線はつないだときの結果を持ち続ける
先に、この記事で使う用語を4つ確認します。
| 用語 | 意味 |
|---|---|
| 認証と認可 | 認証は「誰か」を確かめること。認可は「何をしてよいか」を決めること。ロールは認可の材料 |
| Cookie | ブラウザが保存し、同じサイトへの要求に自動で付ける小さなデータ。ログインすると、サーバーがログイン済みの印を入れる |
| クレーム | ログイン中のユーザーについての情報の1項目(ID・名前・ロールなど)。その一式がClaimsPrincipal |
| 回線(circuit) | ブラウザとサーバーをつなぎっぱなしにする接続(SignalR)。ボタンなどの操作をサーバーへ送り、画面の差分を受け取る |
認証は「誰か」を確かめること、認可は「何をしてよいか」を決めることです。この回の主役は認証で、ロールは認可の材料として出てきます。
Cookieは、ブラウザが保存して、同じサイトへの要求に自動で付ける小さなデータです。ログインすると、サーバーがここにログイン済みの印を入れます。
クレームは、ログイン中のユーザーについての情報の1項目(ID・名前・ロールなど)です。その一式がClaimsPrincipalで、Cookieにも暗号化して入ります。
回線(circuit)は、ブラウザとサーバーをつなぎっぱなしにする接続です。ボタンを押すと、操作がこの接続でサーバーへ届き、サーバーが処理して画面の差分を返します。
この記事は、図1の構成を前提にします。ページの枠は静的SSRで、その中のページの中身だけがInteractiveServerです。Blazor Web Appのテンプレートの既定の形です。

静的SSRは、サーバーがHTMLを作って返すだけの表示のしかたです。InteractiveServerは、回線でつながり、ボタンなどの操作にサーバーが応える表示のしかたです。
そのため、ページの枠は、開くときも移るときもHTTPの要求のたびに描かれ、ページの中身は、回線でつながって操作を受けます。
図2は、ログインからページの操作までの流れです。サーバー側は、HTTPの要求と回線の2つに分かれます。

ログイン(⓪)はHTTPの要求です。サーバーはここで本人を確かめ、クレームを入れたCookieを返します。
ページを開く(①)のもHTTPの要求です。ブラウザがCookieを付け、サーバーはCookieからクレームを取り出します。
そのクレームを見て、ページを開いてよいかを判断し、ロールに応じてメニューを出し分けるなど、そのユーザーに合わせたページを描きます。
続けて、ブラウザは回線をつなぎます(②)。これもHTTPの要求から始まり、サーバーはCookieを読んで、クレームを回線へ渡します。
回線は、このとき受け取ったクレームの写しを持ちます。
画面を操作する(③)と、操作は回線を通ってサーバーへ届きます。HTTPの要求ではないので、Cookieは読み直しません。回線は写しを使います。
リンクで別のページへ移る(④)と、①と同じHTTPの要求になり、サーバーはまたCookieを読みます。一方、回線はそのまま続きます。
このように、HTTPの要求は毎回Cookieからクレームを読み、回線はつないだときの写しを持ち続けます。
⓪では、パスワードなどで本人を確かめます。①②④では、サーバーが自分で発行したCookieを検証して、誰の要求かを判定します。
この、要求のたびにCookieで誰の要求かを確かめる仕組みを、Cookie認証と呼びます。「認証はHTTPの要求で行う」とは、この意味です。
ログインしたあとも、ページを移るたびにCookieを読んでいるんだね。回線のほうは、ずっと最初の写しのまま?
そうです。回線の写しは、既定では自動で更新されません。新しくなるのは、F5(ページの再読み込み)などで回線をつなぎ直したときです。
この仕組みから言えることが2つあります。1つは、ログインとログアウトはCookieを返す・消す処理なので、HTTPの要求で行うことです。
もう1つは、ロールや表示名のような変わりうる情報を、回線の写しのまま使ってよいか、判断が要ることです(MentorAppはDBから読みます。詳しくはこの先の回で)。
この上で決める3つの論点
ここまでが、Blazor(サーバー側)におけるCookie認証と回線の基本です。この基本の上で決めることを、次の表の3つの論点に整理し、1つずつ見ていきます。
| 論点 | 主な選択肢 |
|---|---|
| ログインの仕組みをどこで持つか | 自前で作る(ASP.NET Core Identity)/外部の認証サービス(IdP)に任せる(OIDC) |
| IdPのアカウントとアプリのユーザーをどう結ぶか | 外部IDで結ぶ。ロールはIdPで持つ/アプリのDBで持つ |
| 認証をどの層に置くか | 画面やサービスが直接読む/実装はInfrastructureに置き、インターフェース越しに使う |
ログインを自前でするか、外部に任せるか
1つ目は、ログインで本人を確かめる仕組みを、どこで持つかです。
| 論点 | 主な選択肢 |
|---|---|
| ログインの仕組みをどこで持つか | 自前で作る(ASP.NET Core Identity)/外部の認証サービス(IdP)に任せる(OIDC) |
2つの選択肢
選択肢は、次の表の2つです。IdP(Identity Provider)は、本人を確かめる外部のサービスで、GoogleやMicrosoft Entra IDが代表です。
| 選択肢 | アプリが持つもの | 向いている場面 |
|---|---|---|
| 自前で作る(ASP.NET Core Identity) | パスワード・多要素認証・パスワードのリセット・アカウントの停止を、自分のDBと画面で持つ | このアプリだけでログインが完結する。利用者のデータを自分のサーバーに置きたい |
| 外部のIdPに任せる(OIDC) | 持たない。IdPが本人を確かめ、アプリは本人のクレームだけを受け取る | 利用者がすでにアカウントを持っている。1つのアカウントで複数のアプリに入れるようにする |
自前で作るなら、ASP.NET Core Identityが定番です。パスワードの保存や多要素認証、リセットの画面まで、アプリが自分で持ちます。
外部のIdPに任せるなら、OIDC(OpenID Connect)という標準の手順でIdPにログインしてもらい、アプリは本人のクレームだけを受け取ります。
Identityに外部ログインを足す、中間の形もあります。
公式は、そのアプリだけでログインが完結するならIdentityが最も簡単で、1つのアカウントで複数のアプリに入るなら外部が向く、としています。
それぞれの作り方は、入門編で扱っています。
自前で作る例です。ASP.NET Core Identityで、ユーザーの登録とログインを作ります。
外部のIdPに任せる例です。IdPの一つであるMicrosoft Entra IDで、ログインを付けます。
MentorAppは本人を確かめる仕組みごと外部に任せる
MentorAppは、外部のIdPに任せます。パスワードも多要素認証もアカウントの停止もIdPの側で行い、アプリは本人を確かめる仕組みを持ちません。
| 選択肢 | アプリが持つもの | 向いている場面 |
|---|---|---|
| 外部のIdPに任せる(OIDC) | 持たない。IdPが本人を確かめ、アプリは本人のクレームだけを受け取る | 利用者がすでにアカウントを持っている。1つのアカウントで複数のアプリに入れるようにする |
IdPに任せるのは、ログインのときに本人を確かめるところまでです。ログインしたあとは、アプリが自分のCookieでログイン状態を持ちます。
IdPのアカウントを、アプリのユーザーに結びつける
2つ目は、IdPのアカウントと、アプリのユーザーをどう結ぶかです。1つ目で、外部に任せるほうを選んだときに出てくる論点です。
| 論点 | 主な選択肢 |
|---|---|
| IdPのアカウントとアプリのユーザーをどう結ぶか | 外部IDで結ぶ。ロールはIdPで持つ/アプリのDBで持つ |
外部IDとアプリのユーザー
IdPがアプリに渡すクレームは、アカウントのID(外部ID)と、名前・メールアドレスなどです。アプリで何者かは、IdPは知りません。
アプリのユーザー(DBの1行)とは、この外部IDで結びます。名前やメールは変わりうるので、結びつけには使いません。
知らない外部IDでログインされたときの扱いも決めます。初回ログインで自動で作るか、招待した人だけを通すかです。
ロールをどこで持つか
ロールの置き場は、次の表の2つです。
| 置き場 | 特徴 | 向いている場面 |
|---|---|---|
| IdPで持つ(グループ・アプリロール) | ログインのときにクレームとして届く。管理はIdPの管理画面で行う | 組織の部署や役職がそのまま権限になる。複数のアプリで同じ権限を使う |
| アプリのDBで持つ | アプリの画面で変えられる。IdPには手を入れない | アプリ固有のロール。アプリの管理者が画面で変える |
IdPで持つと、ログインのときにロールがクレームとして届きます。組織の部署や役職をそのまま権限にするアプリに向きます。
アプリのDBで持つと、アプリの画面で変えられます。アプリ固有のロールを、アプリの管理者が変えるなら、こちらです。
MentorAppの結びつけ方
MentorAppは、初回ログインでMenteeとしてユーザーを作り、ロールはDBで持ちます。管理者が画面でロールを変える仕様だからです。
| 置き場 | 特徴 | 向いている場面 |
|---|---|---|
| アプリのDBで持つ | アプリの画面で変えられる。IdPには手を入れない | アプリ固有のロール。アプリの管理者が画面で変える |
最初の管理者だけは、ログインの前に設定の外部ID(ExternalId)から作ります。前回の初期管理者がそれです。
図3は、Googleでログインしたときの例です。Cookieには、IdPのクレーム(外部ID・名前・メール)がそのまま入ります。

外部IDは、Googleではsubです。表示名やロールといったアプリの情報は、Cookieに入れません。
アプリは、要求のたびにCookieの外部IDでDBを引き、表示名やロールをクレームに足します。DBの行には、アプリの主キーが別にあります。
IdPの名前が「Hanako Yamada」でも、表示名は「山田 花子」のままです。DBが正本なので、IdPの名前が変わっても追いません。
これがMentorAppの結びつけ方です。IdPとは外部IDだけで結び、アプリのユーザー(ID・表示名・ロール)はDBで持ちます。
IdPは「誰か」を教えてくれるだけで、その人がアプリで何者かは、アプリのDBが決めるんだね。
そのとおりです。だから、IdPを替えても、アプリのユーザーを持つ仕組みは変わりません。
認証をどの層に置くか
3つ目は、クリーンアーキテクチャで、認証をどの層に置くかです。
| 論点 | 主な選択肢 |
|---|---|
| 認証をどの層に置くか | 画面やサービスが直接読む/実装はInfrastructureに置き、インターフェース越しに使う |
認証は層を越えやすい
何も決めずに書くと、画面の部品がClaimsPrincipalからIdPのクレームを直接読む形になりがちです。
// 画面の部品やサービスが、IdPのクレーム名を直接読む
var user = (await AuthState).User; // 認証状態
var externalId = user.FindFirst("oid")?.Value; // Entra ID の外部ID
var dbUser = await db.Users.SingleAsync(u => u.ExternalId == externalId);
if (dbUser.Role == Role.Admin) { /* 管理者だけの処理 */ }この4行に、フレームワークの認証の型、IdPのクレーム名、DBへの問い合わせ、ロールの判断が混ざっています。
IdPをEntra IDからGoogleに替えるとクレーム名が変わり、読んでいる場所をすべて直すことになります。
認証は、ミドルウェア・フレームワークの型・DB・画面にまたがります。だから、どの層にも染み出しやすいのです。
MentorAppの分け方
MentorAppは、認証の部品を図4のように4層へ置きます。4層の役割は第15回で扱いました。

認証の実装はInfrastructureに置きます。Cookie認証、DBからクレームを足す処理、ログイン方式の差し込み口です。
Applicationには、認証のインターフェースを2つ置きます。
Webがするのは2つだけです。起動時に設定の値を渡すことと、画面の部品がICurrentUserService越しに操作している人の情報を読むことです。
ICurrentUserServiceは、Infrastructureが実装します。Applicationが口を決め、Infrastructureが中身を書く、第22回の依存性逆転の形です。
IUserAuthenticationServiceは逆に、Applicationが実装します。中身が「初回ログインならMenteeとして作る」という業務のルールだからです。
Infrastructureは、クレームを足すときにこれを使う側です。Webが画面からApplicationを使うのと同じで、依存はどちらもApplicationに向きます。
Domainのユーザーとロールは、認証を知りません。
認証の実装はInfrastructureに置き、ほかの層はインターフェース越しに使う。これが図4の分け方です。
ICurrentUserServiceの宣言です。メソッドは1つだけです。
using MentorApp.Domain.Models.Users;
namespace MentorApp.Application.Contracts.Authentication;
public record CurrentUser(
Guid UserId,
string ExternalId,
string DisplayName,
Role Role);
// …(コメント・10 行省略)
public interface ICurrentUserService
{
public Task<CurrentUser?> GetCurrentUserAsync();
}返すのは、操作している人の情報(ID・外部ID・表示名・ロール)です。コードではCurrentUserという型です。
クレームをそのまま渡さず、アプリが使う4項目にまとめた形です。認証の型は宣言にも返り値にも出ず、Webはクレームの名前を知らずに済みます。
ログイン方式を取り替えられるようにする
図5は、Infrastructureの中を開いた図です。Cookie認証やDBからクレームを足す処理は、ログイン方式によらない共通の部品です。

方式ごとに違うのは、ログインで本人を確かめる部分と、IdPごとのクレーム名の対応です。Mock・Google・Entra IDの3つがあり、どれもIProviderSetupの実装です。
設定のProviderで1つを選び、差し込み口に挿します。新しいIdPを足すときも、方式ごとの部品を1つ書き、選べる一覧に登録して、設定で選びます。
{
…(DBの設定・4 行省略)
"Authentication": {
"Provider": "Mock",
…(初期管理者・5 行省略)
"Providers": {
"EntraId": {
"TenantId": "YOUR_TENANT_ID",
"ClientId": "YOUR_CLIENT_ID",
"ClientSecret": "YOUR_CLIENT_SECRET"
},
"Google": {
"ClientId": "YOUR_GOOGLE_CLIENT_ID",
"ClientSecret": "YOUR_GOOGLE_CLIENT_SECRET"
}
}
},
…(ログの設定・8 行省略)
}ログイン方式を変えても、取り替えるのは差し込み口に挿す1つだけです。共通の部品は、そのまま使います。
取り替えられるから、開発用のMockを置けます。開発環境のログイン画面には、IdPを通さずに入るボタンが3つあります。

IdPの設定なしで開発や画面を通したテスト(E2E)ができ、ボタン1つで管理者・メンター・メンティーを切り替えて試せます。
ただし、誰にでもなりすませる方式です。本番の環境でMockが選ばれていたら、MentorAppは起動せずに止まります。
本物のIdPを用意しなくても、認証の仕組みは全部動かせるってこと?
ログイン以外は全部です。だからこの先の回も、Mockのまま読み進められます。
補足:認証の設計に決まった答えがない理由
層に分ける設計では、認証は層の交差点になります。ミドルウェア、クレーム、DB、誰の操作かを見て進める処理、ロールが、層をまたいで関わるからです。
そのため、どの層に何を置くかは、設計の流儀によって判断が分かれます。この記事の分け方は、設計の一例です。
もう1つ、ロールの変更をいつ反映するかも、Web開発一般のトレードオフです。Cookieの期限まで待つ、要求ごとにDBを見る、などの選択があります。
Blazor(サーバー側)では、ここに回線が加わります。要求の時点で作ったクレームが、操作の時点では古くなりうるからです。
まとめ
今回は、Blazor(サーバー側)のCookie認証の基本と、その上で決める3つの論点を整理しました。重要なポイントは以下です。
今回のポイント
- 基本:HTTPの要求のたびにCookieからクレームを読む(Cookie認証)。回線は、つないだときの写しを持ち続ける(自動では新しくならない)
- 論点1:MentorAppは、ログインで本人を確かめる仕組みを、外部のIdPに任せる
- 論点2:IdPのアカウントとは外部IDだけで結び、アプリのユーザーとロールはDBで持つ
- 論点3:認証の実装はInfrastructureに置き、ほかの層はインターフェース越しに使う。ログイン方式は差し込み口で取り替える
次回からは、今回の仕組みを1つずつ、コードを見ながら詳しく見ていきます。まずMockのままで進め、最後にログインだけを外部のIdPに取り替えます。
認証はHTTPの要求のたびに行い、回線はつないだときの結果を持ち続ける。この基本を押さえておけば、この先の回で迷いません。
引き続き、Webアプリ・Blazorの認証の仕組みについて一緒に学んでいきましょう!




