Microsoft Entra ID(旧Azure AD)のOAuthサインインを実装すると、ユーザーが後からアプリの権限を取り消したり「すべてのセッションを終了」した場合に、アプリ側で即座に検知できず困ることがあります。本記事では、Google RISCのようなWebhookがMicrosoftにあるのかを整理し、実運用での検知・対処パターンを具体的に紹介します。
結論:Microsoft Entra IDに「権限取り消し/セッション終了」をWebhookで通知する仕組みはある?
現時点では、ユーザーが自分の操作でアプリの同意(アクセス権)を取り消した、または「すべてのセッションを終了」などで強制的にサインアウトしたといった出来事を、アプリのエンドポイントへリアルタイムにプッシュ通知する公式のWebhook機能は提供されていません。
Microsoft GraphのWebhook(Change Notifications)や、Azure Event Gridの「Microsoft Entra events」は確かに“イベント駆動”を実現する仕組みですが、どちらも「特定アプリに対する同意の取り消し」や「ユーザーのトークン失効/セッション終了」そのものを、アプリ向けにピンポイント通知する用途には対応していないのが実情です。
つまり、設計としては「通知を待つ」のではなく「次のアクセス(API呼び出し/トークン更新)で気づく」方向に寄せるのが現実解になります。
まず整理:「アクセス取り消し」と「セッション終了」は似ているが別物
やりたいことを正確に定義すると、対策も決めやすくなります。特にMicrosoftでは、同じ“ログアウトっぽい事象”でも、アプリに返ってくるシグナルが異なります。
| 事象 | ユーザーの操作例 | アプリに起きること(典型) | リアルタイム通知の有無 |
|---|---|---|---|
| アプリへの同意(権限)取り消し | Microsoftアカウント/マイアプリでアプリを削除・許可を取り消す | 次回のトークン更新やGraph呼び出しで失敗し、再認証が必要になる | 標準ではなし |
| セッション終了(強制サインアウト) | 「すべてのセッションを終了」「サインアウトを強制」など | 既存のアプリ内セッションは残り得るが、トークン再取得で失敗に寄ることが多い | 標準ではなし |
| 管理者がユーザーのサインインセッションを無効化 | 管理者がセッション無効化(例:デバイス紛失対応) | 無効化されたリフレッシュトークンを使うとエラーになり、再認証が必要 | 標準ではなし |
この後の対処パターンは、上の「アプリに起きること」を意図的に拾いにいく発想で組み立てます。
Microsoft Graph Webhook(Change Notifications)でできること/できないこと
Microsoft GraphのChange Notificationsは、Microsoft Graph上の一部リソースの「作成・更新・削除」を、Webhook・Event Hubs・Event Gridへ配信する仕組みです。
ただし、ここで重要なのは“通知できる対象(リソース)が決まっている”点です。たとえばユーザーやグループ、Outlook、OneDrive、Teamsなどは対象になり得ますが、「ユーザーがアプリの同意を取り消した」という出来事を表すリソース(委任された権限付与=oAuth2PermissionGrantなど)や、サインインログ/監査ログは、Change Notificationsの対象として一般的に扱える形では提供されていません。
また、Change Notificationsで「user」リソースを購読する場合でも、個人向けMicrosoftアカウント(outlook.com等)では非対応という制約が明記されています。B2Cなどでも非対応ケースがあります。
「GraphのWebhookがある=何でも通知できる」ではない
GraphのWebhookは便利ですが、OAuthアプリ運用で本当に欲しくなる次の2つは、直球では満たせません。
- ユーザーが自分の意思で同意(アクセス許可)を取り消したことを、アプリへ即時通知
- ユーザーがすべてのセッションを終了したことを、アプリへ即時通知
| やりたいこと | Graph Change Notificationsで直接できる? | 補足 |
|---|---|---|
| 同意の取り消し(アプリ権限の剥奪)を検知 | 難しい | 購読対象リソースとして一般的に提供されない。別アプローチが必要。 |
| サインインログ/監査ログの新規発生をWebhookで受ける | 難しい | 少なくともEntra eventsとしては対象外。ログは別経路で扱う。 |
| ユーザー/グループの変更を検知 | 可能 | ただし対象アカウント種別など制約あり。 |
誤解されがち:Graphの「ライフサイクル通知」は“同意取り消し通知”ではない
Microsoft Graphには、サブスクリプションの更新や、エンドポイント側の再認可を促すためのライフサイクル通知があります。ここには「テナント管理者が、あなたのアプリの“リソース読み取り権限”を取り消した」場合の通知が含まれます。
ただしこれは、あくまでGraphのサブスクリプション(Webhook配信)を継続するための仕組みであり、あなたのアプリにサインインしたエンドユーザーが自分のMicrosoftアカウント側で同意を取り消した、という出来事を一般的にWebhookで受け取れることを意味しません。
Azure Event Gridの「Microsoft Entra events」でできること・できないこと
Azure Event Gridには「Microsoft Entra events」という枠があり、Microsoft Graph APIが発行する一部イベントをEvent Gridへ流す仕組みが提供されています。
しかし、そこで提供されるイベントタイプは現状、ユーザー/グループの作成・更新・削除が中心です。アプリ権限の取り消しや、サインイン/監査ログといった領域は、この枠では扱えません。
| 分類 | Event Grid(Microsoft Entra events)で扱える? | 代表例 |
|---|---|---|
| ディレクトリオブジェクトの変更 | 扱える | ユーザー/グループの更新・削除 |
| サインインログ/監査ログ | この枠では扱えない | ログ系は別経路での取り扱いが現実的 |
| アプリ同意(OAuth権限)取り消し | この枠では扱えない | ユーザー単位の同意取り消しをEvent Gridで直接受信は難しい |
Webhookが来ない前提での「現実的な検知」パターン
ここからは、Microsoft側から“通知が飛んでこない”前提で、実運用で破綻しにくい設計パターンを紹介します。ポイントは、ユーザー操作の瞬間を捉えるのではなく、あなたのアプリが次に動く瞬間に確実に検知することです。
パターンA:リフレッシュトークン更新の失敗を「取り消し/セッション終了の合図」として扱う
OAuthの世界では、アクセストークンは短命で、期限が切れたらリフレッシュトークンで更新します。Microsoftのドキュメントでも、リフレッシュトークンは長寿命だが、期限切れ・失効・権限不足などで更新に失敗し得るため、アプリ側でエラーハンドリングを前提にすべきと説明されています。
そして、リフレッシュトークンが失効した代表例として、AADSTS50173(grantがrevoked)が挙げられます。原因には、パスワードリセットや管理者による失効などが含まれます。
この性質を利用して、アプリ側では次のように割り切ります。
- バックエンドでGraph APIを呼ぶ前に、まず「期限チェック→必要なら更新」を行う
- 更新が失敗したら、ユーザーのアプリ内セッションを無効化し、再認証へ誘導する
- その際のUI文言は「権限が取り消された可能性があるため再ログインが必要」くらいに留め、断定しない
| よく見るエラー/状況 | 意味(ざっくり) | アプリ側の推奨アクション |
|---|---|---|
| AADSTS50173(The provided grant has expired due to it being revoked) | リフレッシュトークンが失効(revoked) | トークン破棄→再認証(interactive login)へ |
| AADSTS50089(flow token expired / revoked) | 認可コード/リフレッシュトークン/セッションが期限切れ or 失効 | 再ログイン要求(キャッシュを抱え続けない) |
| refresh token更新が失敗(一般論) | 期限切れ・失効・権限不足などがあり得る | 失敗を通常系として扱い、UXを崩さず再認証へ |
実装上のコツは、失敗時に「ログイン画面に飛ばす」だけで終わらせず、アプリ内の状態(DBに保存した紐付け・ジョブ・外部連携状態)も“安全側”に倒すことです。たとえば、バックグラウンド同期があるなら、そのユーザーの同期を一旦止めて、再認証完了後に再開する、などです。
(参考の擬似コード例)
// 例:リソースアクセス前に「トークン更新→Graph呼び出し」を行う
function callGraphWithAutoReauth(userId) {
token = loadTokenFromDB(userId)
if (token.isExpiredSoon()) {
result = refreshToken(token.refresh_token)
if (result.failed) {
// ここが「権限取り消し/セッション終了」を疑うポイント
invalidateAppSession(userId)
markIntegrationAsDisconnected(userId)
return "REAUTH_REQUIRED"
}
saveTokenToDB(userId, result.newToken)
token = result.newToken
}
response = graphGET("/me", token.access_token)
if (response.status == 401 || response.status == 403) {
// アクセストークンは持っているが、権限がなくなった等
invalidateAppSession(userId)
return "REAUTH_REQUIRED"
}
return response.data
}
パターンB:Graph API(/me)を“軽い疎通確認”として使い、失敗をトリガーにする
あなたのアプリがUser.Readでサインインしているなら、Graphの/meは最も軽い確認ポイントになりやすいです。
- 定期的(例:15分〜数時間に1回)に
/meを呼ぶ - ユーザー操作が発生したタイミングで
/meを呼ぶ(ログイン直後、重要操作の直前など)
ここで401/403が返った場合、原因は「トークン期限切れ」だけでなく、同意取り消しやセッション失効など複数あり得ます。よって、原因の特定より“安全に復帰できる導線”を用意することが優先です。
パターンC:アプリ内セッションを短命にして、検知のラグを縮める
「Microsoft側の状態変化を即時Webhookで取りたい」という要望の裏には、たいていアプリ側セッションが長生きし過ぎている問題が潜んでいます。
そこで、アプリ内セッションを次のように設計すると、体感の“取り消し反映の遅さ”をかなり減らせます。
- アプリ内セッション(Cookieや自前JWT)は短命(例:30分〜数時間)+スライディング更新
- 重要操作(課金、外部連携の再開、エクスポート等)の前に必ずトークン健全性チェック
- バックグラウンド処理は「最後にGraph疎通できた時刻」を見て動かす(古ければ止める)
「いつでも静かに動く」より、「壊れたら確実に止まる」設計のほうが、ユーザーの信頼も運用も安定します。
パターンD:企業テナント限定の現実解――監査ログ/サインインログをEvent Hubへストリーミングして準リアルタイム化
もしあなたのアプリが単一テナント(社内向け)、または顧客企業と合意のうえでテナント側に設定を入れられるなら、Webhookに近い運用は作れます。ポイントは、Graphの“Change Notifications”ではなく、Entraの診断設定(Diagnostic settings)でログをEvent Hubへ流すことです。
Microsoft Entraの診断設定では、少なくとも監査ログ(AuditLogs)とサインインログ(SignInLogs)を、Log Analytics/Storage/Event Hubへルーティングできます。
| 構成要素 | 役割 | ポイント |
|---|---|---|
| Microsoft Entra 診断設定 | ログ(監査・サインイン等)を出力 | 設定には管理者権限が必要。 |
| Azure Event Hubs | ログをストリームとして受ける | SIEM連携や自作のイベント処理に繋げやすい。 |
| Azure Functions / コンシューマ | ログを解析し、特定パターンを検知 | 「アプリ同意取り消しっぽいログ」「セッション無効化っぽいログ」をルール化する |
| あなたのアプリ | 検知したユーザーを“要再認証”状態にする | 自前セッションや連携状態を無効化する |
注意点として、診断設定の反映には時間がかかる場合がある旨も案内されています。導入直後に「すぐ来ない」ことがあっても、仕組みとして異常とは限りません。
この方式はあくまでテナント管理者と一体で運用できる場合の強い選択肢です。一般消費者のMicrosoftアカウントを相手にするSaaSでは、そもそもテナント側設定を依頼できないため、適用範囲を見極めてください。
パターンE:Graphで「同意状態(Delegated permission grant)」を棚卸しする(管理者向け)
「ユーザーが本当にまだ同意しているか」をシステム的に確認したい場合、Graphには委任権限付与(oAuth2PermissionGrant)を取得するAPIがあります。たとえばユーザー単位で/me/oauth2PermissionGrantsを参照する方法があります。
ただしこれは、一般的なサインインアプリ(User.Readだけ)で気軽に呼べるAPIではありません。必要権限が強く、ユーザー側にも特定のEntraロールが求められるなど、管理者運用を前提にした棚卸し手段です。また、個人向けMicrosoftアカウントではサポートされません。
棚卸し方式を採るなら、「リアルタイム通知」ではなく、
- 夜間バッチで同意状態をチェックして異常を検出
- 異常ユーザーを翌朝“要再認証”扱いにする
のように、運用に馴染む形に落とすと失敗しにくいです。
補足:revokeSignInSessionsは「検知」ではなく「強制無効化」のAPI
Microsoft Graphには、ユーザーのサインインセッション(実質的にリフレッシュトークン等)を無効化するrevokeSignInSessionsがあります。呼び出し後、無効化されたトークンを使うとエラーになり、再認証が必要になることがドキュメントに記載されています。
ただし、これは「ユーザーがどこかでセッション終了したことをアプリへ通知してくれる」機能ではありません。あなたの側が“無効化を実行したい”ときに使うものです。また、個人向けMicrosoftアカウントではサポートされない点にも注意が必要です。
「subで紐付け」はOK?IDトークンの識別子のベストプラクティス
質問の前提にある「IDトークンのsubクレームでユーザーを紐付ける」方針は、Microsoftのドキュメントでも言及があります。メールアドレス等の可変・再利用され得る属性ではなく、永続的で一意な識別子(subまたはoid)を使うべきとされています。
ただし、運用の広がり方によって最適解が変わります。
| あなたの要件 | 推奨されやすい識別子 | 理由 |
|---|---|---|
| アプリ単体でユーザーを一意に持てればよい | sub(またはoid) | GUIDで一意。subはアプリごとの“ペアワイズ”値。 |
| 複数サービス間で同じユーザーとして連携したい | oid + tid | 同一テナント内でアプリが違っても同じoid/tidになりやすい。 |
| ゲストユーザーや複数テナントに跨る動きがある | テナント境界を前提に設計 | テナントが変わると識別子の扱いも変わり得るため“別ユーザー扱い”が安全。 |
「取り消し検知」と直接は関係ないようで、実は関係があります。紐付けが不安定だと、再認証が起きたときに“別ユーザー扱い”になってしまい、復旧導線が崩れます。だからこそ、最初に識別子をきちんと決めておくのが重要です。
運用で差が出る:誤検知を減らしつつ“確実に復帰させる”設計のコツ
「取り消し」と断定しない
リフレッシュトークン失効は、同意取り消しだけでなく、パスワード変更・管理者操作・セキュリティ要件変更などでも起き得ます。たとえばAADS TS50173は「revoked」という結果を示しますが、原因は複数あり得ます。
ユーザー向け文言は、次のように“復旧行動が分かる”表現にしておくと揉めにくいです。
| 状況 | 画面に出す文言例 | ボタン |
|---|---|---|
| トークン更新失敗/401/403 | 認証情報の有効期限が切れたか、アクセス許可が変更された可能性があります。続行するには再ログインしてください。 | 再ログイン |
| バックグラウンド同期が止まった | Microsoftアカウントとの連携を更新する必要があります。更新後に同期を再開します。 | 連携を更新 |
SPAsは「24時間で再認証が必要」になり得る
フロントエンド中心の構成(SPA)では、ブラウザのプライバシー制限等の影響も受けます。Microsoftの説明では、SPAとして登録されたリダイレクトURI宛のリフレッシュトークンは、一定時間で期限を迎えることがあるため、アプリは定期的にインタラクティブな認証(ユーザー操作を伴う再認証)を前提にすべき、とされています。
つまり、「更新失敗=必ず取り消し」ではないので、更新失敗を検知したら、淡々と再認証へ誘導するのが正解です。
“要再認証”状態をアプリ内で持つ
実装としておすすめなのは、ユーザーごとに次の状態をDBで管理することです。
- connected:正常にGraphへアクセスできる
- reauth_required:トークン更新やGraph疎通が失敗したため、次回操作で再認証が必要
- disconnected:ユーザーが明示的に連携解除した(アプリ側のUIで解除した等)
この状態管理を入れるだけで、「どこを起点に再認証させるか」「バックグラウンド処理を止めるか」「問い合わせ対応で何を確認するか」が一気に整理されます。
まとめ
- Microsoft Entra IDには、ユーザーの同意取り消しやすべてのセッション終了を、アプリへリアルタイムWebhook通知する“Google RISC相当”の公式機能は現状ない。
- Microsoft GraphのChange Notificationsは強力だが、通知対象リソースが決まっており、アプリ権限取り消しやサインイン/監査ログの“そのもの”をピンポイント通知する用途には向かない。
- 現実的な検知は、トークン更新失敗(invalid_grant等)やGraph呼び出し失敗(401/403)を契機に再認証へ誘導する設計が堅い。
- 企業テナントで管理者運用ができるなら、Entraの診断設定で監査ログ/サインインログをEvent Hubへ流し、準リアルタイム検知という作り方もできる。
- ユーザー紐付けは、可変なメール等ではなく、sub(またはoid)を中心に設計し、必要ならoid+tidで安定させる。

コメント