Microsoft Graph APIでSites.Selectedを使ってもSharePointサイトにアプリ権限が付与できない時の対処法

Microsoft Graph API の Sites.Selected を使って「このアプリはこの SharePoint サイトだけ触れるようにしたい」と考えたのに、POST /sites/{site-id}/permissions が 403 で落ちてしまう――この記事では、その典型的な原因と、実運用で安全かつシンプルに構成するための手順をまとめます。


目次

Microsoft Graph API で SharePoint サイトにアプリ権限(Sites.Selected)を付与できない原因

まず、よくある状況を整理します。

  • 対象:SharePoint Online の特定サイト
  • やりたいこと:特定のアプリにだけ、そのサイトへのアクセスを許可したい
  • 実行している操作:
    POST /sites/{site-id}/permissions により、サイトにアプリのアクセス権を付与しようとしている
  • 発生している問題:
    • レスポンスが 403 Forbidden
    • メッセージ例:「サインインユーザーに権限が不足」など
  • 前提条件:
    • アプリには Sites.ReadWrite.All、Sites.Selected、User.Read を付与済み
    • 実行ユーザーは対象サイトのサイト管理者

一見、「サイト管理者だし Sites.ReadWrite.All もあるし、権限は足りているのでは?」と思いがちですが、サイトにアプリ権限を付与する操作には別の条件があります。

結論:付与操作を行う側には Sites.FullControl.All が必須

大事なポイントは次の 2 つです。

  • サイトへのアプリ権限付与(POST /sites/{site-id}/permissions)を実行する側には、Sites.FullControl.All が必須
  • 実際にアクセスさせたい「制限対象アプリ」には、Sites.Selected だけを付与する(広域権限を併用しない)

これを整理すると、次のようになります。

役割用途必要な権限備考
付与操作を行うアプリ / ユーザーサイトに「どのアプリをどの権限で許可するか」を登録するSites.FullControl.All(アプリケーション権限)委任の場合は、Graph アプリに Sites.FullControl.All を付与 & サイト コレクション管理者で実行
制限対象アプリ実際にサイト内のファイルやリストにアクセスするSites.Selected(アプリケーション権限) のみSites.ReadWrite.All などの広域権限を併用するとサイト限定にならない

今回 403 が出ていた原因は、多くの場合この「付与操作側に Sites.FullControl.All が無い」ことにあります。
実際に、Sites.FullControl.All を付けたら解消したという報告があり、再現性の高いパターンです。

Delegated(委任)と App-only での違い

POST /sites/{site-id}/permissions を実行する方法によって、必要条件が少し変わります。

実行方法必要な条件典型的な利用例
Delegated(委任)使用しているアプリ(例:Graph Explorer)に Sites.FullControl.All(委任権限) の管理者同意 サインインユーザーが対象サイトのサイト コレクション管理者Graph Explorer などで手動付与する場合
App-only(アプリのみ)管理用アプリに Sites.FullControl.All(アプリケーション権限) そのアプリのトークンで API を実行スクリプトやバッチで複数サイトをまとめて設定する場合

運用を考えると、管理用アプリを 1 つ作って App-only で実行する構成がもっともシンプルで安全です。

おすすめ構成:管理用アプリ + 制限対象アプリ

「管理用アプリ」と「制限対象アプリ」を分ける二段構えにすると、運用とセキュリティがぐっと楽になります。

構成イメージ

アプリ種別付与する権限(アプリケーション権限)役割
管理用アプリSites.FullControl.All (必要に応じて)Directory.Read.All などサイトに対して「どのアプリに read/write を許可するか」を設定する専用
制限対象アプリSites.Selected のみ実際に業務処理(ファイル取得・更新など)を行うアプリ

この構成にしておくと、誤って制限対象アプリに広域権限を付けてしまうリスクを避けられます。

最短手順(おすすめの実装フロー)

1. 管理用アプリを作成し、Sites.FullControl.All に管理者同意

  1. Azure AD(Entra ID)でアプリ登録を作成(名前例:sp-sites-permission-admin)。
  2. API のアクセス許可で「Microsoft Graph」を選択し、アプリケーション権限から Sites.FullControl.All を追加。
  3. 管理者として 管理者の同意を付与(テナント全体に承認)。
  4. クライアントシークレット or 証明書を登録し、後でスクリプトから使えるようにしておく。

Graph Explorer で Delegated 実行する場合は、Graph Explorer アプリ自体に管理者が Sites.FullControl.All を同意しておき、実行ユーザーはサイト コレクション管理者である必要があります。

2. 制限対象アプリを作成し、Sites.Selected のみを付与

  1. 別のアプリ登録を作成(名前例:line-of-business-app)。
  2. API のアクセス許可で「Microsoft Graph」→ アプリケーション権限から Sites.Selected を追加。
  3. 必ず Sites.Read.All や Sites.ReadWrite.All を付けないよう注意。
    • これらを付けると、「サイト限定」ではなく「全サイトアクセス可能」になってしまう。
  4. 管理者として 管理者の同意を付与。
  5. このアプリのアプリケーション ID(クライアント ID)を控えておく(後で JSON に記述)。

3. 管理用アプリのトークンで、対象サイトにアプリ権限を付与

管理用アプリの資格情報を使ってトークンを取得し、以下のようなリクエストを実行します。

POST https://graph.microsoft.com/v1.0/sites/{site-id}/permissions
Content-Type: application/json
Authorization: Bearer <管理用アプリのアクセストークン>

{
  "roles": ["read"],          // 読み取りのみ: "read" / 更新も許可: "write"
  "grantedToIdentities": [{
    "application": {
      "id": "<制限対象アプリのアプリケーションID(クライアントID)>",
      "displayName": "LOB App for Project X"
    }
  }]
}

重要なポイント:

  • roles は "read" または "write" だけ(複数指定も可能だが、通常はいずれか)。
  • grantedToIdentities.application.id に入れるのは、オブジェクト ID ではなくアプリケーション ID(クライアント ID)。
  • {site-id} は SharePoint サイトの URL ではなく、Graph 上の Site ID。

Site ID は例えば次のように取得できます。

GET https://graph.microsoft.com/v1.0/sites/{hostname}:/sites/{sitePath}?$select=id
  • {hostname} 例:contoso.sharepoint.com
  • {sitePath} 例:sites/ProjectX

4. 付与結果の確認

対象サイトに正しくアプリ権限が付与されたかどうかは、次のリクエストで確認できます。

GET https://graph.microsoft.com/v1.0/sites/{site-id}/permissions
Authorization: Bearer <管理用アプリのアクセストークン>

レスポンス内に、先ほど指定した displayName と application.id を含むエントリが追加されていれば成功です。

5. 制限対象アプリのトークンでアクセス検証

最後に、制限対象アプリの資格情報でトークンを発行し、実際にサイト配下の API を呼び出して確認します。

GET https://graph.microsoft.com/v1.0/sites/{site-id}/drive/root/children
Authorization: Bearer <制限対象アプリのアクセストークン>
  • 対象サイトについては、200 OK でファイル一覧が取得できることを確認。
  • 別のサイト ID で試したときには、403 Forbidden になることを確認。

この 2 つの動きが確認できれば、Sites.Selected によるサイト限定アクセスが正しく機能していると言えます。

Sites.Selected の動作を整理する

ここで一度、Sites.Selected の意味を整理します。

  • Sites.Selected は、「このアプリは SharePoint にアクセスしても良いが、どのサイトにアクセスできるかはサイト側の設定で決まる」という「土台」の権限。
  • アプリに Sites.Selected を付けただけでは、どのサイトにもアクセスできません。
  • アクセスを許可したいサイトごとに、POST /sites/{site-id}/permissions でエントリを追加して初めてアクセスできるようになる。

逆に言うと、サイト側に何もエントリを作っていない状態では、どのサイトにもアクセスできないため、/sites/{site-id}/drive などを呼ぶと 403 になります。

read と write の違い

サイトにアプリを付与する際の roles の値で、アプリがどこまで操作できるかが変わります。

roles の値意味主な操作例運用上のおすすめ度
["read"]読み取り専用ファイル一覧取得、ダウンロード、メタデータ参照などまずは 最小権限としてこれを使うのが基本
["write"]読み書き・削除も可能ファイルのアップロード、更新、削除、フォルダー作成など本当に書き込みが必要な場合だけ限定して付与

多くのケースでは、まず ["read"] で実装 → どうしても必要な範囲に限って ["write"] にするというステップが安全です。

よくある落とし穴とチェックリスト

Sites.Selected 周りでハマりやすいポイントを、チェックリスト形式でまとめます。

カテゴリ落とし穴症状確認・対処ポイント
付与側権限付与操作側に Sites.FullControl.All が無いPOST /sites/{site-id}/permissions が 403 Forbidden管理用アプリまたは Graph Explorer に Sites.FullControl.All が付いているか確認
制限対象アプリSites.ReadWrite.All などの広域権限も付けてしまう他サイトにもアクセスできてしまい、サイト限定になっていない制限対象アプリは Sites.Selected のみにし、他の Sites.* 系権限を外す
アプリ ID 指定grantedToIdentities.application.id にオブジェクト ID を指定付与は成功したように見えるが、期待通りに動かない / 設定がずれるポータルで「アプリケーション(クライアント)ID」と表示されている GUID を使う
ユーザー権限Delegated 実行時に、ユーザーがサイト コレクション管理者ではない403 Forbidden、「サインインユーザーに権限が不足」サイトの「サイト コレクション管理者」に該当ユーザーを追加する
Site ID 指定{site-id} にサイト URL を入れてしまう404 Not Found / 400 Bad RequestGET /sites/{hostname}:/sites/{sitePath}?$select=id で取得した id を使う
構成方針管理用アプリと制限対象アプリを 1 つにまとめてしまうアプリが過剰な権限(FullControl)を持ったまま運用される権限付与専用の管理用アプリと、実業務用の制限対象アプリを分ける

実装イメージ:App-only でのサンプルフロー

実際のコードでは言語ごとに異なりますが、App-only 認証の一般的なフローは次のようになります。

1. 管理用アプリでアクセストークンを取得

(疑似コード)

// 管理用アプリのクライアントID / シークレット / テナントID
var clientId     = "<admin-app-client-id>";
var clientSecret = "<admin-app-secret>";
var tenantId     = "<tenant-id>";

// トークンエンドポイント
var tokenUrl = $"https://login.microsoftonline.com/{tenantId}/oauth2/v2.0/token";

// scope は Microsoft Graph の .default
var body = new {
    client_id     = clientId,
    client_secret = clientSecret,
    scope         = "https://graph.microsoft.com/.default",
    grant_type    = "client_credentials"
};

// tokenUrl に POST して access_token を取得

2. アクセストークンを使って /permissions を呼び出し

POST https://graph.microsoft.com/v1.0/sites/{site-id}/permissions
Authorization: Bearer <access_token>
Content-Type: application/json

{
  "roles": ["read"],
  "grantedToIdentities": [{
    "application": {
      "id": "<restricted-app-client-id>",
      "displayName": "LOB App"
    }
  }]
}

3. 制限対象アプリ側も同様にアクセストークンを取得して動作確認

制限対象アプリでも同じように client_credentials フローでトークンを取得し、そのトークンでサイト API を呼び出します。
このとき、付与されているのが Sites.Selected だけであれば、付与したサイト以外にはアクセスできない状態になります。

トラブルシューティングの具体例

実際にありがちな「ハマり方」と、そのときに確認すべき順番を具体的に挙げます。

ケース1:どうしても 403 Forbidden が消えない

  1. POST を実行している「主体」を特定
    • Graph Explorer か、管理用アプリか、別のアプリか
  2. その主体に対して、Azure ポータルで Sites.FullControl.All が付与されているか確認
  3. 委任の場合は、サインインユーザーがサイト コレクション管理者かどうかを SharePoint 管理センターで確認
  4. 一度 GET /sites/{site-id}/permissions を呼び、そもそも {site-id} が正しいかどうかを確かめる

ケース2:Sites.Selected にしたはずなのに、他サイトにもアクセスできてしまう

  1. 制限対象アプリの API アクセス許可を再確認
    • Sites.Read.All / Sites.ReadWrite.All が付いていないかをチェック
  2. もし付いていた場合は削除し、Sites.Selected のみにした上で、管理者同意を再度実行
  3. アプリのトークンを取り直して、再度 API を呼び出す(古いトークンがキャッシュされていないか注意)

ケース3:/permissions のレスポンスにアプリのエントリが見つからない

  1. POST /sites/{site-id}/permissions のレスポンスが 201 など成功になっているか確認
  2. JSON で指定した grantedToIdentities.application.id に、アプリケーション ID(クライアント ID)を入れているか確認
    • オブジェクト ID を入れていると、意図しない動作になる可能性があります。
  3. 数秒待ってから GET /sites/{site-id}/permissions を再実行し、反映されているかを確認

運用のベストプラクティス

最後に、Sites.Selected を本番環境で運用する際のおすすめポイントをまとめます。

1. 権限は「付与する主体」と「利用する主体」を分離

  • 権限付与専用の「管理用アプリ」は Sites.FullControl.All を持つが、実業務には使わない。
  • 実業務用の「制限対象アプリ」は Sites.Selected のみを持ち、アクセスを許可されたサイトだけを操作。
  • この分離により、万が一制限対象アプリの資格情報が漏えいしても、全サイトが危険にさらされるリスクを抑えられます。

2. まずは read で検証し、必要最小限で write を付与

  • 最初から ["write"] で運用すると、誤削除や誤上書きのリスクが高まります。
  • 要件を満たすかどうかを ["read"] で確認し、それでも不足する場合だけ ["write"] を検討。
  • アプリごとに「read 専用」「write もできる」などの役割を分けるのも有効です。

3. Sites.Selected のポリシーをドキュメント化

  • どのアプリに、どのサイトへの read / write を許可したかを一覧化しておく。
  • 新しいサイトや新しいアプリが追加されたときの「付与申請の流れ」も明文化。
  • 監査ログやアクセスログとあわせて管理すると、トラブル時の原因特定が容易になります。

4. 検証環境で「403 を再現できる状態」を保持

  • あえて Sites.FullControl.All を外したアプリやユーザーを用意し、「403 になるパターン」を意図的に再現できる環境を作っておくと、教育・検証に役立ちます。
  • 新メンバーや別チームに仕組みを説明する際、実際に 403 → 権限追加 → 成功の流れを見せると理解が早まります。

まとめ:403 Forbidden を解消するためのポイント

Microsoft Graph API で SharePoint サイトにアプリ権限(Sites.Selected)を付与しようとして 403 Forbidden になる場合、次のポイントを確認してください。

  • 付与操作を行う側に Sites.FullControl.All が付いているか?
    • 管理用アプリ(App-only)に Sites.FullControl.All を付与し、そのトークンで POST /sites/{site-id}/permissions を実行する。
    • Delegated の場合は、アプリに Sites.FullControl.All(委任) + ユーザーはサイト コレクション管理者であること。
  • 制限対象アプリには Sites.Selected だけを付けているか?
    • Sites.ReadWrite.All などの広域権限が付いていると、サイト限定の制御が効かなくなります。
  • grantedToIdentities.application.id はアプリケーション ID(クライアント ID)になっているか?
  • {site-id} は正しい Site ID か?

これらを押さえておけば、Sites.Selected を使った「特定サイトだけにアクセスできるアプリ」の設計・運用が格段にやりやすくなります。
本記事を参考に、自分のテナントでも一度「管理用アプリ + 制限対象アプリ」の構成を試してみてください。

この記事を書いた人

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

コメント

コメントする

目次