Microsoft Graph 実務入門 #2 アプリ登録と認証 — Entra ID、delegated と application 権限

読了 8分

第 1 回で Graph Explorer から最初のリクエストを実行してみたなら、次は自分のコードに同じことをさせる番です。その間にある関門が認証で、Graph 学習で実質的に最も難しいのがここです。API 自体は単純なのに、「私のアプリはどんな資格で、誰のデータに、どこまでアクセスするのか」という問いに答える構造に馴染みがないからです。今回はその構造を、実務で決めていく順序どおりに整理します。

先に全体像を描くと、次のようになります。① Entra ID にアプリを登録して身分証を作り、② delegated か application かを決め(この決定が残り全部を左右します)、③ 必要な権限を追加して同意を得て、④ コードでトークンを取得して呼び出します。

なぜアプリ登録からか — ユーザーアカウントとアプリの分離 #

Graph を呼ぶ主体は人ではなくプログラムです。Entra ID はプログラムにも身元を要求し、その身元を作る手続きがアプリ登録(app registration)です。登録すると二つの識別子が生まれます。

  • アプリケーション(クライアント)ID — アプリのユーザー名に当たるものです。秘密ではありません。
  • テナント ID — 組織(テナント)の識別子です。自社の Microsoft 365 空間を指します。

登録の手続き自体は簡単です。Microsoft Entra 管理センター(entra.microsoft.com)→ App registrations → New registration で名前を決め、サポートされるアカウントの種類は社内自動化なら「この組織のディレクトリのアカウントのみ」を選べば済みます。登録直後の Overview 画面で上の二つの ID を確認できます。ここまでは何の権限もない空の身分証です。

なお、アプリ登録には組織の設定によって管理者権限が必要な場合があります。会社アカウントで登録メニューが制限されているなら、IT 部門に開発用のアプリ登録を依頼するのが正規の手順です。そのとき下の権限の区別を知って依頼すると、話がずっと早くなります。

最も重要な決定 — delegated か application か #

Graph のすべての権限は二つの型のどちらかです。同じ「メールを読む」にも二つのバージョンがあり、意味がまったく違います。

Delegated (委任)Application (アプリ専用)
実行の主体サインインしたユーザーを代理するアプリユーザーなしのアプリ自身
届く範囲そのユーザーが見られるものまで権限が許せば組織全体
典型的な用途ユーザーが操作するアプリ、CLI ツール、ウェブアプリサーバーバッチ、デーモン、夜間の自動化
トークンの取得ユーザーのサインインの過程が必要シークレット/証明書で即時 (client credentials)
同意ユーザー本人の同意 (+必要なら管理者)常に管理者の同意が必要

判断の基準は一つの問いに集約できます。「このコードが動く瞬間、画面の前に人がいるか?」 いるなら delegated、いないなら(スケジューラが夜中の 3 時に回すスクリプトなら)application です。

この区別が重要な理由は、セキュリティの重さが違うからです。delegated は二重に制限されます。アプリの権限とユーザーの権限の共通部分にしかアクセスできないので、アプリに Mail.Read があってもサインインしたユーザー自身のメールしか読めません。application は違います。Mail.Read を application 権限で受ければ、組織全員のメールボックスを読めます。だから application 権限は常に管理者の同意が必要で、IT 部門が慎重になるポイントもまさにここです。

シリーズの後半で、この区別が実際の問題として戻ってきます。たとえば Teams のチャネルにメッセージを送る API は delegated にしか開いていないので、「サーバーが自動でお知らせを投稿する」シナリオは別の経路を使う必要があります(第 6 回)。API ドキュメントの Permissions の表で二つの列を確認する習慣が必要な理由です。

権限の追加と同意 — スコープの実務 #

アプリ登録画面の API permissions で Microsoft Graph の権限を追加します。権限の名前は リソース.動作 のパターンで読みやすくなっています。User.Read(自分のプロフィール)、Mail.Read(メールの読み取り)、Mail.Send(メールの送信)、Files.ReadWrite(ファイルの読み書き)、Calendars.ReadWrite(予定の読み書き)のような形で、後ろに .All が付くと範囲が「全体」に広がります(delegated の Files.ReadWrite.All はユーザーがアクセスできるすべてのファイル、application のそれは組織のすべてのファイルです)。

同意(consent)は「このアプリがこの権限を使うことを承認する」という手続きです。

  • delegated 権限の一部は、ユーザー本人がサインイン時に同意できます(組織のポリシーによります)。
  • application 権限と機微な delegated 権限は、管理者の同意(admin consent)が必要です。API permissions 画面の「Grant admin consent」ボタンがそれで、権限一覧の Status 列が緑になったかで確認します。

実務の原則は最小権限です。ドキュメントの Permissions の表には「least privileged」が明示されているので、常にそこから始めます。Mail.Read で足りる自動化に Mail.ReadWrite を受けておくのは、事故のときの被害範囲を自分で広げることになり、IT 部門の承認も遅くなります。なお application 権限には範囲を特定の資源に絞る仕組み(Exchange の ApplicationAccessPolicy、SharePoint の Sites.Selected など)もあり、「組織全体は広すぎる」という懸念にはこうした選択肢を併せて示すと、承認の議論が楽になります。

資格情報 — シークレットと証明書 #

application 権限(と一部の delegated フロー)では、アプリが自分を証明する資格情報が必要です。二つあります。

  • クライアントシークレット — 文字列のパスワードです。作るのが簡単で、始めるにはこれで十分です。有効期間(最大 2 年、短く取ることを推奨)があるので、期限切れがそのまま障害になります。期限日をカレンダーに書くのではなく、期限前の通知を自動化するところまでが運用です。
  • 証明書 — 公開鍵をアプリに登録し、秘密鍵で署名する方式です。シークレットより漏えいに強く、本番の推奨で、証明書しか許さないポリシーの組織もよくあります。

どちらにせよ、資格情報はコードとリポジトリに入れません。 環境変数か秘密の管理サービス(Azure Key Vault など)が置き場所で、Git に秘密鍵をコミットしたときの手順を経験しないのが最善です。Azure の中で動くワークロードなら、シークレットそのものが存在しないマネージド ID が一番よい答えだということも覚えておく価値があります。

トークンのフロー — コードからはどう見えるか #

概念の整理をコードの観点でまとめるとこうなります。詳細な実装は第 3 回で行い、ここでは流れだけ見ます。

二つの権限の型のトークンフロー
[delegated]
アプリ → ユーザーをサインインページへ案内 → ユーザーが認証・同意
  → アプリが「そのユーザーを代理する」トークンを取得 → Graph を呼ぶ
  (CLI ツールなら device code flow: コードを表示し、ユーザーがブラウザで入力)

[application]
アプリ → テナント ID + クライアント ID + シークレット/証明書でトークンを要求
  → 「アプリ自身の」トークンを即時取得 → Graph を呼ぶ
  (client credentials flow。人の介在なし)

トークンは寿命が短く(約 1 時間)、更新は SDK が処理するので、実務でトークンの文字列を直接扱うことはまれです。問題が起きたときにトークンの中の権限(scp または roles クレーム)を確認するデバッグの要領だけ、第 3 回で押さえます。

認証で詰まったら — エラーの読み方 #

認証段階のエラーはパターンが決まっています。先に知っておくと数時間を節約できます。

  • 401 Unauthorized — トークン自体がないか間違っています。テナント ID・クライアント ID・シークレットの値を確認し直します。
  • 403 Forbidden — トークンは有効なのに権限がありません。大半は三つのどれかです。権限を追加しただけで管理者の同意を得ていない(Status 列を確認)、delegated と application を取り違えた、その API が要求する権限がそもそも別。ドキュメントの Permissions の表と突き合わせるのが一番の近道です。
  • AADSTS で始まるエラーコード — Entra ID のエラーです。コードで検索すると公式ドキュメントが出て、メッセージの中に原因がかなり具体的に書かれていることが多いエラーです。

まとめ #

  • 認証の順序は、アプリ登録(身分証)→ 権限の型の決定 → 権限の追加と同意 → コードでトークン、です。クライアント ID・テナント ID は識別子で秘密ではなく、シークレット・証明書が秘密です。
  • 最も重要な決定は delegated か application かです。画面の前に人がいれば delegated(ユーザー権限との共通部分)、無人の自動化なら application(組織全体に届く、管理者の同意が必須)です。
  • 権限はドキュメントの least privileged から始めます。application 権限の範囲を絞る仕組み(ApplicationAccessPolicy、Sites.Selected)を知っておくと承認の話が早く進みます。
  • シークレットは期限切れがそのまま障害です。期限の通知を自動化し、本番は証明書またはマネージド ID を推奨します。
  • 401 は資格情報、403 は権限(同意の欠落、型の取り違え)です。AADSTS コードは検索するのが一番早い解決策です。

次回はこの認証の上に Python SDK を載せ、実際のコードで最初の呼び出しをします。delegated(device code)と application(client credentials)の両方を実装します。

X