Introduction
If you've ever granted an app Sites.ReadWrite.All permissions in SharePoint, you know that sinking feeling you've just given it the keys to every single site in your tenant. For most applications, that's like using a sledgehammer to hang a picture frame.
There's a better way: Sites.Selected permissions. This feature lets you grant your application access to only the specific SharePoint sites it actually needs nothing more, nothing less. It's the principle of least privilege in action, and it's surprisingly straightforward to set up.
In this guide, I'll walk you through the complete process of configuring Sites.Selected with read-only access to a specific SharePoint site. We'll use Microsoft Graph Explorer (no PowerShell required), and by the end, you'll have a secure, properly scoped application that can only access the data it needs.
What Is Sites.Selected and Why Should You Care?
Sites.Selected is a Microsoft Graph permission that flips the traditional access model on its head. Instead of granting tenant-wide access upfront, you start with zero access and explicitly grant permissions to individual sites as needed.
Think of it this way:
- Sites.ReadWrite.All = Master key to every door in the building
- Sites.Selected = Specific key cards for only the rooms you need
This approach drastically reduces your attack surface. If your application's credentials are ever compromised, the blast radius is limited to just the sites you've explicitly granted access to not your entire SharePoint environment.
The Three-Phase Process
Setting up Sites.Selected permissions involves three distinct phases, each building on the last:
| Phase | Action | Where |
|---|---|---|
| 1 | Create app registration & add Sites.Selected permission | Azure Portal |
| 2 | Get SharePoint site ID | Microsoft Graph Explorer |
| 3 | Grant read permission to site | Microsoft Graph Explorer |
Here's the crucial thing to understand: after Phase 1, your app has Sites.Selected consent but zero actual access. It's not until Phase 3 that you grant permission to specific sites. This two-step process is what makes the permission model secure.
What You'll Need
Before we dive in, make sure you have:
- Global Admin or Application Admin role in Azure AD
- SharePoint Admin access
- The URL of the SharePoint site you want to grant access to
That's it. No PowerShell modules to install, no scripts to run just your browser and admin credentials.
Phase 1: Creating Your App Registration
The first phase happens entirely in the Azure Portal. We'll create a new app registration and add the Sites.Selected permission but remember, this doesn't grant access to anything yet.
Navigate to App Registrations
Head to https://portal.azure.com and sign in with your admin account. Use the search bar at the top to find App registrations and select it.
Register Your Application
Click + New registration and fill in these details:
| Field | Value |
|---|---|
| Name | SharePoint-Reader-App |
| Supported account types | Accounts in this organizational directory only |
| Redirect URI | Leave blank |
Click Register and you'll land on the app overview page.
Save Your Application ID
On the overview page, you'll see an Application (client) ID. This is a GUID that looks something like a3f44e63-e46e-4c31-9213-888c172ca160. Copy this and save it somewhere safe you'll need it in Phase 3.
Add the Sites.Selected Permission
In the left menu, click API permissions, then + Add a permission. Here's where it gets specific:
- Select Microsoft Graph
- Choose Application permissions (not Delegated this is critical)
- Search for Sites.Selected
- Check the box and click Add permissions
Application permissions are for background services and daemon apps that run without a signed-in user. Delegated permissions are for apps that act on behalf of a user. For Sites.Selected to work, you must use Application permissions.
Grant Admin Consent
Back on the API permissions page, click Grant admin consent for [Your Organization] and confirm. You'll see a green checkmark appear next to Sites.Selected with a status of Granted.
Important: At this point, your app has Sites.Selected consent, but it still can't access any sites. That's by design. Phase 3 is where you grant the actual site-level permissions.
Phase 2: Getting the SharePoint Site ID
To grant permissions to a specific site, you need its Site ID. This is a long, comma-separated string that uniquely identifies the site in your tenant. We'll use Microsoft Graph Explorer to retrieve it.
Open Graph Explorer and Sign In
Navigate to https://developer.microsoft.com/en-us/graph/graph-explorer and click Sign in to Graph Explorer in the top right. Use your admin account.
Consent to Permissions (Critical Step)
Here's something that trips people up: Graph Explorer needs its own permissions to make API calls. These are separate from your app's permissions.
Click the Modify permissions tab below the query box. Find these two permissions and consent to both:
- Sites.Read.All (needed to retrieve the site ID)
- Sites.FullControl.All (needed in Phase 3 to grant permissions)
For each permission, click Consent, then Accept in the pop-up. Verify both show a Consented status before proceeding.
Construct Your Query
Set the method dropdown to GET. Now you need to build the URL. The format is:
https://graph.microsoft.com/v1.0/sites/{tenant}.sharepoint.com:/sites/{sitename}
Let's say your site is https://contoso.sharepoint.com/sites/ProjectAlpha. Your Graph API URL would be:
https://graph.microsoft.com/v1.0/sites/contoso.sharepoint.com:/sites/ProjectAlpha
Note: Use the site name from the URL, not the display name. If your site is called "Project Alpha Site" but the URL is ProjectAlpha, use ProjectAlpha.
Run the Query and Extract the Site ID
Click Run query. In the Response preview, you'll see JSON that looks like this:
{
"id": "contoso.sharepoint.com,a1b2c3d4-1234-5678-abcd-111122223333,e5f6g7h8-4321-8765-dcba-444455556666",
"name": "ProjectAlpha",
"displayName": "Project Alpha",
"webUrl": "https://contoso.sharepoint.com/sites/ProjectAlpha"
}
Copy the entire id value the whole thing, including the commas. This is your Site ID. Save it alongside your Application ID.
The Site ID format is {hostname},{site-collection-id},{web-id}. Don't try to reconstruct it manually or use just part of it you need the complete string.
Phase 3: Granting Read Permission to Your Site
This is where everything comes together. We'll use a POST request in Graph Explorer to grant your application read access to the specific site.
Set Up the POST Request
In Graph Explorer, change the method to POST. The URL format is:
https://graph.microsoft.com/v1.0/sites/{siteId}/permissions
Replace {siteId} with the full Site ID you copied in Phase 2. For our example:
https://graph.microsoft.com/v1.0/sites/contoso.sharepoint.com,a1b2c3d4-1234-5678-abcd-111122223333,e5f6g7h8-4321-8765-dcba-444455556666/permissions
Add the Request Body
Click the Request body tab and enter this JSON:
{
"roles": ["read"],
"grantedToIdentities": [
{
"application": {
"id": "YOUR-APPLICATION-CLIENT-ID",
"displayName": "SharePoint-Reader-App"
}
}
]
}
Critical: Replace YOUR-APPLICATION-CLIENT-ID with the Application ID you saved in Phase 1. Using our example ID, the complete JSON would be:
{
"roles": ["read"],
"grantedToIdentities": [
{
"application": {
"id": "a3f44e63-e46e-4c31-9213-888c172ca160",
"displayName": "SharePoint-Reader-App"
}
}
]
}
The roles array specifies the permission level. We're using read for read-only access. Other options include write, manage, and fullcontrol.
Execute and Verify
Click Run query. If everything is configured correctly, you'll receive a 201 Created response with JSON that looks like:
{
"id": "aTowaS50fG1zLnNwLmV4dHxlYTVmMDVlZ...",
"roles": ["read"],
"grantedToIdentitiesV2": [...],
"grantedToIdentities": [...]
}
That id field in the response is the permission ID. Save it if you think you might need to update or revoke this permission later.
Verifying Everything Works
To confirm the permission was granted successfully, you can query the permissions endpoint. Change the method back to GET and use:
https://graph.microsoft.com/v1.0/sites/{siteId}/permissions
Click Run query. You should see your application listed in the response with roles set to ["read"].
Understanding Available Permission Roles
We used read in this guide, but Sites.Selected supports four permission levels:
| Role | Description |
|---|---|
| read | Read-only access (used in this guide) |
| write | Read and write access |
| manage | Manage lists and libraries |
| fullcontrol | Full control over the site |
Choose the most restrictive role that meets your application's needs. If you only need to read documents, stick with read.
Common Issues and How to Fix Them
Here are the most common problems people run into and their solutions:
403 Forbidden Error
If you get a 403 error when trying to grant permissions in Phase 3, you likely haven't consented to Sites.FullControl.All in Graph Explorer. Go back to the Modify permissions tab and consent to it.
Site Not Found
Double-check that you're using the site name from the URL, not the display name. If your site URL is /sites/proj-alpha but the display name is Project Alpha Team Site, use proj-alpha.
Insufficient Privileges
Make sure you've clicked Modify permissions in Graph Explorer and consented to both Sites.Read.All and Sites.FullControl.All. These are Graph Explorer's permissions, not your app's.
Invalid Request Body
Check your JSON syntax carefully. Common mistakes include missing commas, mismatched brackets, or forgetting to replace YOUR-APPLICATION-CLIENT-ID with your actual Application ID.
Wrapping Up
Sites.Selected permissions represent a massive improvement in security posture for SharePoint integrations. Instead of granting blanket access to your entire tenant, you can scope applications down to exactly what they need and nothing more.
The process involves three phases: creating an app registration with Sites.Selected consent in Azure Portal, retrieving the Site ID using Graph Explorer, and granting site-specific permissions via a POST request. Each phase builds on the last, and by the end, you have a properly scoped application that follows the principle of least privilege.
If you're building SharePoint integrations, this should be your default approach.
If you have any questions you can reach out our SharePoint Consulting team here.

No comments:
Post a Comment