SharePoint のファイルをアプリから読みたいだけなのに、「Sites.Read.All と Sites.Selected をどう組み合わせるべきか」「管理者同意が毎回必要なのか」で迷うケースは少なくありません。本記事では Azure App registration(Microsoft Entra ID アプリ登録)の API 権限と SharePoint アクセス制御の考え方を、委任アクセスと Sites.Selected を中心に整理します。
結論のざっくりまとめ(先に答えだけ知りたい人向け)
最初に、この記事の要点を「何をしたいときに、どの権限をどう使うか」でまとめます。
| やりたいこと | 推奨の権限設計 | ポイント |
|---|---|---|
| ユーザーが自分で見える SharePoint サイトのファイルだけを読みたい | 委任権限を使う Sites.Read.All(Delegated) または Files.Read.All(Delegated) | Sites.Selected は不要 トークンは「ユーザーのアクセス範囲」に自動的に制限される |
| アプリが触れるサイトを 特定サイトだけに厳密に絞りたい | Sites.Selected(Delegated or Application) + サイト単位の明示割り当て | Sites.Selected を付けただけでは何も読めない POST /sites/{site-id}/permissions や PnP PowerShell でサイトごとに権限付与が必須 |
| バックグラウンドでサインイン無しで SharePoint を操作したい | アプリケーション権限+Sites.Selected(Application) クライアント資格情報フロー(client_credentials) | ユーザー権限は一切絡まず、「アプリに割り当てたサイト権限」がそのまま有効 |
そして今回の質問に対する最重要ポイントは次の 3 つです。
- 「ユーザーが見える範囲だけ読みたい」のであれば、基本は Delegated の
Sites.Read.All/Files.Read.Allだけで足りる。Sites.Selected は不要。 scope=Sites.Selectedと指定しても、トークンにSites.Read.Allなど別の強いスコープが入っていれば、そちらが効いてしまう。結果として「Sites.Selected だけで動いている」と勘違いしやすい。- 管理者を一切介さずに「ユーザー X が見えるサイト」を読む“抜け道”はない。テナントの「ユーザー同意ポリシー」が許可していない限り、管理者同意は必須。
Azure App registration と SharePoint 権限モデルの基本
細かい話に入る前に、Entra ID(旧 Azure AD)のアプリ登録と Microsoft Graph / SharePoint の権限モデルを整理します。
委任権限(Delegated)とアプリケーション権限(Application)
Microsoft Graph の権限は大きく 2 種類です。
| 種類 | 想定シナリオ | 具体例 | 特徴 |
|---|---|---|---|
| 委任権限 (Delegated) | ユーザーがサインインしている Web アプリ / SPA / ネイティブ アプリ | ユーザーがブラウザからログイン → そのユーザーが見える SharePoint のファイルを一覧表示 | トークンには scp クレーム(スコープ)が入る 「ユーザーの権限 ∩ アプリに付与したスコープ」が実効権限 |
| アプリケーション権限 (Application) | バッチ、デーモン、バックグラウンド ジョブ | 夜間バッチで全サイト横断のレポートを生成 | トークンには roles クレーム(アプリ ロール)が入る ユーザーの権限とは無関係に、アプリに与えた権限そのまま |
今回の主題は「ユーザーの代理で SharePoint を読む」なので、基本的には 委任権限(Delegated)の話になります。
SharePoint 関連でよく出てくる権限
Graph 権限リファレンスを確認すると、SharePoint 関連では主に次の権限を使います。
| 権限名(Delegated) | 説明(要約) | 管理者同意 |
|---|---|---|
Sites.Read.All | サインイン ユーザーがアクセスできるすべてのサイトのドキュメント・リスト項目を読み取り | No(仕様上は不要) |
Sites.ReadWrite.All | サインイン ユーザーがアクセスできる全サイトで読み書き | No(仕様上は不要) |
Files.Read.All | サインイン ユーザーがアクセスできるすべてのファイルを読み取り | No(仕様上は不要) |
Sites.Selected | 管理者(またはサイト所有者)が明示的に許可した一部サイトのみにアクセス | No(Delegated) / Yes(Application) |
ここで注意したいのは、「管理者同意が不要」と書かれていても、テナント側の「ユーザー同意ポリシー」設定によってはユーザー単独で同意できない点です。
シナリオ 1:ユーザーの権限内だけで SharePoint を読みたい
最もよくある要件は「ユーザーがブラウザで見えているのと同じ範囲だけ、アプリからも読めればよい」というパターンです。
このシナリオでは Sites.Selected は不要
ユーザーの権限内だけを読みたいのであれば、単純に 委任権限を使えばよく、Sites.Selected を使う必要はありません。
- サイトもライブラリも横断して読みたい →
Sites.Read.All(Delegated) - 「ファイル本文が取れれば十分」で、サイト情報はあまり使わない →
Files.Read.All(Delegated)
委任権限のアクセストークンには「トークンの所有者」であるユーザー情報が含まれます。そのため、Graph に対して SharePoint の API を呼び出すと、SharePoint 側は 「ユーザー本人が呼んだ」と見なしてアクセス制御を行います。
例えば、次のリクエストは、ユーザーがアクセスできるサイトから検索してくれます。
GET https://graph.microsoft.com/v1.0/sites?search=contoso
Authorization: Bearer <user-delegated-access-token>
このとき、トークンに Sites.Read.All が含まれていれば、Graph / SharePoint は「ユーザーがアクセスできるサイトに対して検索を実行」しますが、ユーザー自身にアクセス権のないサイトは返ってきません。
OAuth クライアントで指定すべき scope
典型的な SPA / Web アプリの認可コードフローでは、認証時の scope に以下のようなスコープを指定します。
User.Read(プロフィール取得)offline_access(リフレッシュトークン)openid profile(ID トークンが必要な場合)Sites.Read.AllまたはFiles.Read.All
scope=openid profile offline_access Sites.Read.All
ポイント:アプリ登録の「API 権限」にこれらのスコープを追加し、ユーザー(または管理者)が一度同意すれば、以降はトークン取得のたびに同じスコープが返ってきます。
シナリオ 2:アプリが触れるサイトを特定サイトに絞りたい(Sites.Selected)
次の段階として、「組織としては Sites.Read.All のような広い権限をアプリに与えたくないので、アプリが触れるサイトを限定したい」というニーズが出てきます。ここでようやく Sites.Selected の出番です。
Sites.Selected は「選択されたサイトだけ」のための特別な権限
Microsoft Graph の Selected Permissions(*.Selected)は、アプリに対して「リソース単位の明示割り当て」を必須にする特殊な権限です。
Sites.Selected の場合、動かすためのステップは次の 3 つです。
- アプリ登録に
Sites.Selected(Delegated または Application)を追加し、同意を得る。 - SharePoint 側で、アプリに対して「どのサイトに」「どのロール(read / write / manage / fullcontrol)」を許すかを設定する。
- Sites.Selected を含むアクセストークンを取得し、そのトークンで Graph を呼び出す。
どれか 1 つでも欠けていると、アプリはどのサイトにもアクセスできません。Selected 系の権限は、単にアプリ登録に追加しただけでは何もできない、という点が非常に重要です。
委任版 Sites.Selected とアプリ版 Sites.Selected の違い
当初 Sites.Selected は「アプリケーション権限専用」でしたが、その後 SharePoint が 委任版 Sites.Selected にも対応しました。
| 種類 | 最終的な権限の決まり方 | 典型的な用途 |
|---|---|---|
| Sites.Selected(Delegated) | 「ユーザーの権限」 × 「アプリに割り当てたサイト権限」の交差 | ユーザーがサインインしているアプリだが、アプリが触れるサイトをさらに絞りたい |
| Sites.Selected(Application) | 「アプリに割り当てたサイト権限」のみ(ユーザーは関係ない) | バッチ処理、デーモン、PowerShell スクリプトなどのバックグラウンド処理 |
委任版 Sites.Selected の場合、ユーザーにそのサイトへのアクセス権がなければ、アプリにもアクセス権はありません。アプリ版 Sites.Selected は逆に「ユーザーを介さない」、いわゆるサービス プリンシパル用の制御です。
サイト単位の権限割り当て方法
Sites.Selected でアプリをサイトに紐付けるには、Graph API または PnP PowerShell を使います。
- Graph API の例:
POST https://graph.microsoft.com/v1.0/sites/{site-id}/permissions Content-Type: application/json { "roles": [ "read" ], // write | manage | fullcontrol も選択可能 "grantedToIdentities": [ { "application": { "id": "", "displayName": "My SharePoint App" } } ] } - PnP PowerShell の例:
Grant-PnPAzureADAppSitePermission ` -AppId <GUID> ` -Site https://contoso.sharepoint.com/sites/foo ` -Permissions Read
この割り当てを行わない限り、Sites.Selected がトークンに入っていても、そのアプリはどのサイトにもアクセスできません。
「scope=Sites.Selected」なのに読めたのはなぜか?
ここからは、質問者の方が遭遇した具体的な挙動を紐解きます。
Sites.Read.Allに管理者同意を付けたうえで、OAuth のリクエストにscope=Sites.Selectedを指定したら、非管理者でも同意できてユーザーが見えるサイトにアクセスできた。これは推奨か?
結論から言うと、多くの場合は実際には Sites.Read.All が効いているにも関わらず、「scope パラメータに Sites.Selected を書いているから、Sites.Selected で動いている」と勘違いしているケースです。
アクセストークンの中身(scp クレーム)を確認する
委任権限の場合、アクセストークンには scp クレームに有効なスコープがスペース区切りで入ります。例えば:
"scp": "Sites.Read.All User.Read offline_access"
ここで重要なのは、実際にトークンに入っているスコープだけが有効という点です。認可エンドポイントに scope=Sites.Selected と指定していても、アプリ登録や既存の同意内容に基づき、Entra ID が Sites.Read.All を含めて発行することがあります。
また、既にユーザーが Sites.Read.All に同意済みで、リフレッシュトークンから新しいアクセストークンを発行しているケースでは、クライアントが改めて scope を指定しなくても、以前のスコープがそのまま引き継がれます。
そのため、以下を確認するのがおすすめです。
- https://jwt.ms/ などでアクセストークンをデコードし、
scpを確認する。 Sites.Read.AllやFiles.Read.Allが入っていないかをチェックする。- テナントの「エンタープライズ アプリケーション > <対象アプリ> > 権限」から、既にどの権限に同意が出ているか確認する。
もし scp に Sites.Read.All が含まれていれば、今回の挙動は「Sites.Selected とは無関係に、Sites.Read.All の権限でアクセスできている」だけです。この状態は、「アプリがユーザーの見える全サイトにアクセスできる」という意味で、最小権限設計とは言い難いため、設計として推奨はできません。
幅広い権限は Sites.Selected の制限を“上書き”し得る
Microsoft Q&A でも議論されている通り、Files.Read.All や User.Read.All など、より広い権限をアプリに付けてしまうと、Sites.Selected による制限を事実上上書きしてしまうケースがあります。
つまり、本当に Sites.Selected でサイトを絞りたいなら、同じアプリに Files.Read.All や Sites.Read.All などの広い権限を混ぜないほうが安全です。「Selected で絞ったつもりが、他の権限で全部見えていた」という事態になりかねません。
管理者を介さずに「ユーザーが見えるサイト」へアクセスさせたい場合
質問の 3 つ目は、「ユーザー X が見えるサイト」にアプリからアクセスするのに、管理者を介さず設定する方法はあるのかという点でした。
仕様レベルの答え:抜け道はない
Graph 権限リファレンスを見ると、Sites.Read.All(Delegated) や Files.Read.All(Delegated)、Sites.Selected(Delegated) はいずれも AdminConsentRequired = No になっています。
これは「技術仕様上は、ユーザー自身の同意だけで利用可能」という意味ですが、実際のテナントでは次のようなポリシーが設定されていることがあります。
- ユーザーは「低リスクな権限」のみ同意可能
- 組織データに広くアクセスする権限(Sites.Read.All など)は、常に管理者同意が必要
- 特定のアプリのみユーザー同意を許可する
これらは組織のセキュリティ方針であり、アプリ開発者がアプリ側の工夫だけで回避することはできません。
どうしても管理者の手を減らしたい場合は、次のようなアプローチを検討します。
- 管理者に対し、「このアプリは Sites.Read.All を使うが、アプリ側でさらに業務ロジックでアクセス範囲を制限している」ことを説明し、事前にテナント全体への同意をもらう。
- 一部の権限だけをユーザー同意可能なポリシーに含めてもらう(Entra ID の「ユーザー同意設定」「アクセス許可分類」の見直し)。
いずれにしても、管理者を完全に介さずに済ませる方法はなく、テナント ポリシーと相談するしかありません。
Sites.Selected を使っても「サイト単位の割り当て」は誰かがやる必要がある
Sites.Selected(Delegated)を使えば、「ユーザーの権限 ∩ アプリに割り当てたサイト」という形で絞り込みできますが、サイト単位の割り当て作業(2. で説明した Graph / PnP PowerShell による権限付与)は、サイト所有者や管理者が行う必要があります。
つまり、「組織としてどのサイトにアプリを入れてよいか」を決める人の関与は、やはり不可欠です。
実装パターンごとの構成例
ここからは、もう少し実装寄りに構成例とステップを整理します。
パターン A:委任権限+Sites.Read.All で「ユーザーが見えるサイト」を読む
1. アプリ登録(Entra ID)
- プラットフォーム構成(Web / SPA / モバイルなど)を追加し、リダイレクト URI を登録。
- 認証フローに応じて、機密クライアントであればクライアント シークレットや証明書も登録。
2. API 権限の追加
- Microsoft Graph > 委任された権限
User.Read,offline_access,openid,profileSites.Read.AllまたはFiles.Read.All
テナントのポリシー次第で、ここで管理者同意が必要かどうかが決まります。
3. OAuth 2.0 認可コード フローでトークンを取得
典型的なリクエスト例:
GET https://login.microsoftonline.com/{tenant}/oauth2/v2.0/authorize
?client_id=<client-id>
&response_type=code
&redirect_uri=https%3A%2F%2Fapp.contoso.com%2Fsignin-oidc
&scope=openid%20profile%20offline_access%20Sites.Read.All
&state=12345
返ってきたコードを使ってトークン エンドポイントに POST し、アクセストークンを取得します。
4. Graph から SharePoint を呼び出す例
例えば、「ユーザーが見えるサイトのうち、特定キーワードで検索」する場合:
GET https://graph.microsoft.com/v1.0/sites?search=contoso
Authorization: Bearer <access_token>
特定サイトのドキュメント ライブラリ配下のファイル一覧:
GET https://graph.microsoft.com/v1.0/sites/{site-id}/drive/root/children
Authorization: Bearer <access_token>
ここで アクセスできるのは、あくまでユーザー本人が権限を持つサイトだけです。
パターン B:委任権限+Sites.Selected で「ユーザーかつ特定サイト」の両方で絞り込む
より厳密に、「ユーザーが見えていて、かつアプリに許可されたサイトだけ」に絞り込みたいときの構成です。
1. API 権限に Sites.Selected(Delegated)を追加
アプリ登録の「API 権限」で、Microsoft Graph > 委任された権限から Sites.Selected を追加します。Graph 権限リファレンス上、Delegated の AdminConsentRequired は No ですが、テナント設定により管理者同意が必要な場合があります。
2. サイト単位でアプリに権限を付与
前述の Graph API か PnP PowerShell を使って、アプリを特定サイトに紐付けます。
Grant-PnPAzureADAppSitePermission `
-AppId <app-id> `
-Site https://contoso.sharepoint.com/sites/project-x `
-Permissions Read
これで「AppId に対して、このサイトに Read ロールを付ける」という設定が入ります。
3. サインイン ユーザー側にもサイト権限が必要
委任版 Sites.Selected の最終的な権限は、あくまで「ユーザー権限との交差」です。ユーザーにサイトへのアクセス権がなければ、アプリからもアクセスできません。
4. 実際の呼び出し
scope に Sites.Selected を指定してトークンを取得し、そのトークンで例えば次のように呼びます。
GET https://graph.microsoft.com/v1.0/sites/{selected-site-id}/drive/root/children
Authorization: Bearer <access_token-with-Sites.Selected>
別サイトの site-id を指定して呼ぶと、403 Forbidden などで拒否されます(ユーザーが見えていても、アプリに対してサイト割り当てが行われていないため)。
パターン C:アプリケーション権限+Sites.Selected でバッチ処理
最後に補足として、ユーザーのサインインなしで SharePoint を処理したい場合の構成です。
- アプリ登録に
Sites.Selected(Application)を追加し、管理者同意を得る。 - サイト単位でアプリに権限を割り当てる(PnP PowerShell / Graph)。
- クライアント資格情報フロー(client_credentials)でアクセストークンを取得し、Graph を呼び出す。
このパターンは「ユーザーの代理」ではなく、あくまで「アプリとして」動くため、本記事の質問とは少し趣旨が異なりますが、アーキテクチャ検討時には頻繁に比較対象になります。
よくある落とし穴とチェックリスト
最後に、SharePoint と Graph 権限まわりでハマりがちなポイントをチェックリスト形式でまとめます。
| チェック項目 | 確認ポイント |
|---|---|
| トークンの scp / roles を見ているか | scope パラメータに指定しているスコープではなく、実際に発行されたアクセストークンに何が入っているかを必ず確認する。 |
| Sites.Selected を“付けただけ”で満足していないか | Sites.Selected は、アプリ登録への追加に加え、サイト単位の権限割り当てと、Sites.Selected を含むトークン取得が揃って初めて効く。 |
| 広い権限(Files.Read.All / Sites.Read.All)と Selected を混在させていないか | 同じアプリに広い権限と Selected 系を混ぜると、広い権限の方が有効になり、Selected による制限が意味を持たなくなるケースがある。 |
| テナントのユーザー同意ポリシーを確認したか | Graph のドキュメント上は AdminConsentRequired = No でも、組織ポリシーでユーザー同意が制限されている場合がある。 |
| Sites.Selected だけでサイト一覧を取ろうとしていないか | Sites.Selected は「アクセス許可されたサイトを列挙する」用途には向かない。サイト一覧や検索には別の権限(Sites.Read.All など)が必要。 |
| PnP PowerShell のコマンドを把握しているか | Grant-PnPAzureADAppSitePermission / Set-PnPAzureADAppSitePermission / Revoke-PnPAzureADAppSitePermission などで Sites.Selected の割り当てを自動化できる。 |
まとめ:どう使い分けるのがベストか
最後に、本記事のポイントをコンパクトに整理します。
- 「ユーザーの見えるサイトだけをユーザーの代理で読みたい」のであれば、基本は Delegated の
Sites.Read.AllかFiles.Read.Allを使う。Sites.Selected は不要。 - 「アプリが触れるサイトを特定サイトに限定したい」場合には Sites.Selected を使う。委任版なら「ユーザー権限 ∩ アプリのサイト割り当て」で交差を取り、アプリ版ならバックグラウンド処理向けの制御になる。
- Sites.Selected は、アプリ登録に追加しただけでは意味がなく、「サイト単位の権限割り当て」「Sites.Selected を含むトークン取得」の 3 ステップがそろって初めて効く。
- scope パラメータだけを見て「この権限で動いているはず」と判断しない。実際に発行されたトークンの
scp/rolesを必ず確認し、どの権限が効いているかを検証する。 - 管理者を完全に介さずに SharePoint へのアクセスを許可する方法はない。どの権限をユーザー同意で許すかは、Entra ID のユーザー同意ポリシー次第であり、開発者が勝手に回避することはできない。
このあたりの設計を最初にきちんと整理しておくと、「なぜかアクセスできてしまう」「なぜか 403 になる」といったトラブルシュートの時間を大幅に減らせます。アプリが扱うデータの機密度と業務要件に応じて、委任権限 / アプリケーション権限・Sites.Read.All / Files.Read.All / Sites.Selected を組み合わせ、常に「最小権限」を意識した設計を心がけてください。

コメント