Microsoft Graph で SharePoint リスト取得が 403 Forbidden になる原因と対処法徹底解説

Microsoft Graph で SharePoint Online のリストにアクセスしたときだけ 403 Forbidden になり、「PnP.PowerShell からはフルコントロールで見えているのになぜ?」と悩むケースは非常に多いです。この記事では、よくある勘違いポイントを整理しつつ、実務で役立つ切り分け手順と具体的な対処策を詳しく解説します。

目次

Microsoft Graph で SharePoint リスト取得が 403 Forbidden になる典型シナリオ

まずは、よくある前提条件を整理しておきます。あなたの環境も、かなり近い構成になっているはずです。

項目内容
用途SharePoint Online 上のドキュメント管理アプリ(独自 Web / SPA / バックエンド)
認証基盤Microsoft Entra ID(旧 Azure AD)のアプリ登録
付与した権限委任権限:Sites.ReadWrite.All(Microsoft Graph)
認可フロー認可コードフロー(Authorization Code Flow)でアクセストークン取得
呼び出し APIGET /sites/{site-id}/lists/{list-id}/items?$expand=fields
発生している事象Graph 呼び出しのみ 403 Forbidden(Access denied)
一方、PnP.PowerShell でサイトに接続すると、ユーザーはサイトに対してフルコントロールを持っているように見える

この状況の多くは、

  • 権限の種類
  • トークンの中身
  • テナント側の条件付きアクセス
  • 対象リソース(サイト/リスト)の指定

のどこかが噛み合っていないことが原因です。逆に言えば、この4つを順番に潰していくことで、かなりの確率で解決できます。

なぜ PnP.PowerShell は通るのに Microsoft Graph が 403 になるのか

まず押さえておきたいのは、「PnP で見えている=Graph でも必ず見える」ではないということです。

  • PnP.PowerShell は主に SharePoint REST API(/_api) に対してアクセスする
  • Graph 呼び出しは Microsoft Graph(https://graph.microsoft.com) に対してアクセスする

この2つは同じ SharePoint データを扱っていても、

  • 適用される 条件付きアクセス(CA)ポリシー が違う
  • 必要になる Graph スコープ/アプリロール が違う
  • トークンの aud(受信者) がそもそも別

といった違いがあります。

そのため、たとえば次のようなことが起こり得ます。

  • ブラウザや PnP.PowerShell から SharePoint サイトにアクセスするのは許可されている
  • しかし Microsoft Graph へのアクセスには「準拠デバイス必須」などの CA が追加で掛かっている
  • 結果として、SharePoint REST は成功するのに Graph は 403 という差が出る

ここから先は、よくある原因を 1 つずつ深掘りしながら、どう切り分けていくかを解説します。

よくある原因パターン一覧

まずは全体像を俯瞰できるように、原因パターンを表にまとめます。

原因パターン概要優先度
トークンのスコープ不足・管理者同意不足トークンの scp に Sites.ReadWrite.All が入っていない/管理者同意が付与されていない★★★★★(最優先で確認)
条件付きアクセス・デバイス準拠ポリシーGraph だけ CA ポリシーが強く、トークンがブロックされている★★★★☆
リストの固有権限サイトではフルコントロールだが、対象リストだけ権限継承が切られている★★★☆☆
site-id / list-id の取り違え実は別サイト/別リストを指しており、権限不足扱いになっている★★★☆☆
委任権限 vs アプリケーション権限の誤解バックエンドからアプリ単独で呼んでいるのに、委任権限しか設定していない(または逆)★★★☆☆

トークンの中身を確認する(最優先ステップ)

403 Forbidden が出たら、まずやるべきは 実際に使っているアクセストークンをデコードして中身を見ること です。ここを見ずに手探りで設定を変え続けると、原因にたどり着くまで非常に時間が掛かります。

確認すべき主なクレーム

  • aud(Audience)
    トークンの宛先。Microsoft Graph 用なら GUID で 00000003-0000-0000-c000-000000000000 など Graph を示す値、または https://graph.microsoft.com になっていること。
  • scp(Scope)
    委任権限で発行されたトークンに付く権限の一覧。
    例:Sites.ReadWrite.All User.Read openid profile
  • roles
    アプリケーション権限で発行されたトークンに入るアプリロールの一覧。委任トークンの場合は通常空、または存在しません。
  • exp / nbf
    有効期限・有効開始時刻。単にトークンが古くて期限切れになっている場合もあります。

委任権限で Graph を呼んでいる場合、最低限次のような状態になっているかチェックします。

  • aud が Microsoft Graph を指している
  • scp に Sites.ReadWrite.All(または少なくとも Sites.Read.All)が含まれている

逆に、アプリケーション権限で動かす設計にしているなら、

  • scp は空(または存在しない)
  • roles に Sites.ReadWrite.All や Sites.Selected などのアプリロールが入っている

この時点で 期待する権限がまったく入っていない 場合、アプリ登録の設定か、スコープ要求の仕方、または管理者同意が怪しいと絞り込めます。

WWW-Authenticate ヘッダーの claims チャレンジ

403 のレスポンスヘッダーの中に WWW-Authenticate が含まれていたら、その中の claims= を必ず確認しましょう。条件付きアクセスが原因の場合、ここに

  • 多要素認証を要求
  • 準拠デバイスが必要
  • 特定のサインインリスクレベル以上をブロック

といった追加条件が JSON 形式で埋め込まれていることがあります。これがある場合、単に権限を増やすだけでは解決せず、「アプリ側が claims を含めてリダイレクトし直す」などの対応が必要になります。

管理者同意(Admin Consent)が付与されているか確認する

委任権限の Sites.ReadWrite.All は管理者同意必須 のスコープです。ユーザーが自分でサインインしただけでは、トークンにこのスコープが載らないケースがよくあります。

管理者同意の確認と付与手順

  1. Microsoft Entra 管理センター(旧 Azure ポータル)で「アプリ登録」を開く
  2. 該当アプリを開き、「API のアクセス許可」を表示
  3. Microsoft Graph の項目に Sites.ReadWrite.All(委任)があることを確認
  4. 画面上部の「管理者の同意を付与」ボタンをクリック

管理者同意を付与した後は、ユーザーに一度サインアウトしてもらい、改めてサインインしてトークンを取り直す ことも重要です。古いトークンを握ったままだと、いつまでも 403 が続きます。

よくある落とし穴

  • 開発テナントでは自分がグローバル管理者なので気付かないが、本番テナントでは開発者が管理者同意を付与できずに詰まる
  • リダイレクト URI を追加したタイミングなどで再同意が必要になっているが、誰も気付いていない

委任権限とアプリケーション権限の違いを整理する

403 トラブルのかなりの割合は、「委任でやるつもりだったのか、アプリ単独でやるつもりだったのか」がチーム内で曖昧なまま進んでいることに起因します。

項目委任権限(Delegated)アプリケーション権限(Application)
実行主体サインインしているユーザー + アプリアプリのみ(バックエンド・バッチ)
トークンのクレームscp にスコープが入るroles にアプリロールが入る
動作する条件ユーザーが対象サイト/リストを読む権限を持っていることが前提SharePoint 管理側でアプリにサイト権限を明示的に割り当てる必要がある
典型的な用途ユーザーがブラウザから操作する業務アプリ夜間バッチ、同期処理、Webhook など

「バックエンドからサーバーサイドで自動実行したいのに、委任権限しか付いていない」状態で呼ぶと、期待とまったく違うトークンが発行され、403 に繋がります。設計の前提を一度整理して、必要に応じて Sites.Selected(アプリケーション) などに切り替えることを検討しましょう。

サイト/リスト固有の権限を確認する

「PnP.PowerShell でサイトに接続するとフルコントロールなのに、Graph からは 403」という場合でも、よく調べると 対象リストだけ権限継承が切られている ケースは少なくありません。

PnP.PowerShell で確認する例

# サイトに接続
Connect-PnPOnline -Url https://<tenant>.sharepoint.com/sites/<siteName> -UseWebLogin

# リストの情報を取得
Get-PnPList -Identity "<ListName>" | Select Title, HasUniqueRoleAssignments

# リストの権限割り当てを確認
Get-PnPRoleAssignment -List "<ListName>"
  • HasUniqueRoleAssignments が True の場合、そのリストはサイトからの権限継承を停止している
  • 委任トークンの主体になっているユーザー(upn クレームなど)が、ここに含まれているか確認する

もし対象ユーザーに権限が無い場合、Graph からのアクセスは 403 になります。PnP の実行ユーザーと、アプリでサインインしているユーザーが 別アカウント なこともあるので、ここは丁寧に突き合わせてください。

site-id / list-id を Graph で取り直して確認する

URL の ID を一文字でも間違えると、Graph は「権限がない」「リソースが存在しない」等のエラーを返します。見た目は 403 でも、実際は ID 取り違えが原因ということも多いです。

サイト ID の取得

GET https://graph.microsoft.com/v1.0/sites/{hostname}:/sites/{site-path}?$select=id,name,webUrl
  • {hostname} 例:contoso.sharepoint.com
  • {site-path} 例:sites/DocManagement

リスト ID の取得

GET https://graph.microsoft.com/v1.0/sites/{site-id}/lists?$select=id,name,displayName

ここで返ってきた id をそのまま、リスト項目取得の API に差し込むようにします。

リスト項目取得の再試行

GET https://graph.microsoft.com/v1.0/sites/{site-id}/lists/{list-id}/items
  ?$expand=fields($select=Id,Title)

この 3 ステップで ID を「Graph ベースで」取り直すことで、手入力ミスやコピペミスを潰すことができます。

条件付きアクセス(CA)・デバイス準拠ポリシーの影響を疑う

トークンの中身も ID も問題ないのに 403 が続く場合、次に疑うべきは 条件付きアクセス です。特に企業テナントでは、SharePoint と Graph に対して別々のポリシーが掛かっていることがよくあります。

よくある設定例

  • SharePoint Online:社外 IP からもブラウザアクセス可(ただし MFA 必須)
  • Microsoft Graph:社内 IP または準拠デバイスからのみ許可

この場合、PnP.PowerShell は「ブラウザ相当」と見なされて SharePoint 側のルールに従いますが、Graph 呼び出しはより厳格なポリシーで評価され、結果として 403 になります。

CA 由来かどうかを見分けるポイント

  • サインインログ を確認し、「条件付きアクセス」の欄でブロックされていないかを見る
  • レスポンスヘッダーの WWW-Authenticate に claims チャレンジが含まれていないか確認する
  • 同じユーザー・同じネットワークから Graph Explorer で同 API を呼んでみて成功するかどうかを試す

Graph Explorer で通るのに自作アプリからは 403 の場合、アプリが CA の評価条件(クライアントアプリ種別、フロー、claims 対応など)を満たしていない可能性が高いです。

Graph Explorer / Graph CLI での再現確認

アプリ固有の問題なのか、テナント/ポリシー全体の問題なのかを見分けるために、Graph Explorer(ブラウザ)や Microsoft Graph CLI で同じ API を叩いてみるのは非常に有効です。

Graph Explorer での確認例

  1. 同じユーザーアカウントで Graph Explorer にサインイン
  2. リクエスト欄に以下のように入力
    GET /v1.0/sites/{site-id}/lists/{list-id}/items?$expand=fields
  3. 実行結果が 200 で返るかどうかを確認
  • ここで成功する → アプリ側の設定・実装の問題 に絞られる
  • ここでも 403 → ユーザー権限か条件付きアクセス/テナント設定の問題 に絞られる

アプリケーション権限 + Sites.Selected で運用する場合

バックエンドやバッチ処理で「人がログインしていなくても SharePoint リストを見に行きたい」場合は、委任権限ではなく アプリケーション権限 を使う方が自然です。その際、最近のベストプラクティスは Sites.Selected を使って、アプリごとにアクセスできるサイトを絞る方法です。

大まかな設定手順

  1. アプリ登録で Microsoft Graph に アプリケーション権限 Sites.Selected を追加
  2. 管理者同意を付与
  3. SharePoint 管理者として、対象サイトにアプリ権限を割り当て

SharePoint 側での権限付与には、PnP.PowerShell の Grant-PnPAzureADAppSitePermission コマンドがよく使われます。

Grant-PnPAzureADAppSitePermission `
  -AppId <appId> `
  -DisplayName "<display-name>" `
  -Site https://<tenant>.sharepoint.com/sites/<siteName> `
  -Permissions Write

この手順を踏むと、トークンの roles に Sites.Selected が入り、対象サイトに対してのみ Graph からアクセスできるようになります。

デバッグのときに必ず取っておきたい情報

403 Forbidden の原因を後から追いかけるためにも、次の情報はログなどに必ず残しておくと便利です。

  • HTTP ステータスコード(例:403)
  • レスポンスボディのエラーメッセージ(error.code, error.message)
  • 重要ヘッダー:x-ms-* 系、WWW-Authenticate
  • 使用したトークンの内容(ヘッダーと署名を除いた ペイロード部分だけ をデコードして記録)
  • 呼び出した URL(クエリストリングも含めて)

cURL での再現テスト

アプリコードをいじる前に、まずは純粋な HTTP リクエストで再現させてみると、余計な要因を排除できます。

curl -i -H "Authorization: Bearer <access_token>" \
  "https://graph.microsoft.com/v1.0/sites/<site-id>/lists/<list-id>/items?$expand=fields"

ここで 403 が返るなら、コードではなく構成(権限・CA・ID)側に問題があると判断できます。

403 Forbidden を解消するための実践的チェックリスト

最後に、実務での「つぶしこみ順」を表にまとめます。上から順に確認していくことで、無駄なく原因にたどり着けるはずです。

ステップ確認内容具体的なアクション
1. トークン内容の検査aud / scp / roles / exp を確認し、期待する権限が入っているかJWT をデコードしてログに貼り付け、Sites.ReadWrite.All などが入っているか目視で確認
2. 管理者同意の再付与委任権限の Sites.ReadWrite.All に Admin Consent が付いているかEntra ポータルの「API のアクセス許可」で「管理者の同意を付与」を実行後、ユーザーに再サインインさせる
3. Graph Explorer での動作確認同じユーザー・同じ API で 200 が返るかGraph Explorer にサインインし、同一エンドポイントを叩く
4. リスト固有権限の確認対象リストが固有権限を持っていないか、ユーザーに権限があるかPnP で HasUniqueRoleAssignments や Get-PnPRoleAssignment を確認
5. site-id / list-id の再取得ID の取り違えや手入力ミスがないかGraph からサイト ID → リスト ID → 項目取得の順で再取得し、URL を修正
6. 条件付きアクセスの評価CA ポリシーで Graph アクセスがブロックされていないかサインインログの「条件付きアクセス」の項目と WWW-Authenticate の claims チャレンジを確認
7. 設計の見直し(任意)バックエンド処理ならアプリケーション権限の方が適切かどうかSites.Selected + サイトへのアプリ権限割り当てを検討

まとめ:403 Forbidden を怖がらず、情報から冷静に切り分ける

Microsoft Graph で SharePoint リストを取得しようとして 403 Forbidden に遭遇すると、「何をどう直せばよいのか分からない」状態になりがちです。しかし、

  • トークンの内容(aud / scp / roles / exp)
  • 管理者同意の有無
  • サイト/リストの実際の権限
  • site-id / list-id の整合性
  • 条件付きアクセスの評価結果

といった情報を 1 つずつ確認していけば、多くの場合は原因を特定できます。

特に、「PnP では見えるのに Graph では見えない」ケースは、API の違いよりもテナント側ポリシーや権限設計の違いが原因になっていることが多い です。PnP の実行ユーザーとアプリが利用しているユーザー/アプリケーション権限を丁寧に見比べ、今回紹介したチェックリストを順番に辿っていけば、403 Forbidden は決して怖い相手ではありません。

もしそれでも解決しない場合は、使用しているトークンペイロード(aud と scp だけ抜き出したもの)や、サインインログの「条件付きアクセス」の評価結果を整理し、チーム内やコミュニティで共有すると、より具体的なアドバイスを得やすくなります。

この記事を書いた人

実務の現場で詰まりがちなポイントを地図にするITブログ「IT trip」を運営。Windows/Office(Teams・Excel)からSQL、サーバ運用、ガジェットまで、再現性のある手順と“なぜそうなるか”を丁寧に解説します。読んだらすぐ試せること、そして迷った人の次の一歩が見えることを大切にしています。

コメント

コメントする

目次