Showing posts with label Microsoft Graph. Show all posts
Showing posts with label Microsoft Graph. Show all posts

April 27, 2026

How to Lock Down SharePoint Access with Sites.Selected Permissions

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:

  1. Select Microsoft Graph
  2. Choose Application permissions (not Delegated this is critical)
  3. Search for Sites.Selected
  4. 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.

March 3, 2026

Restricting Microsoft Graph Mail Access with Application Access Policies: A Complete Guide

When integrating Microsoft 365 workloads with Microsoft Graph, one of the most overlooked security risks is granting an application too much access. If an Azure AD app is assigned Mail.Read (Application) permissions, it can read email from every mailbox in the tenant using a simple Graph call:

GET https://graph.microsoft.com/v1.0/users/{id}/messages

This default behavior is powerful—but extremely risky.

Most applications only need access to a few mailboxes, not the entire directory. Without restrictions, your app could unintentionally access executive mail, HR communications, or legally sensitive data.

In this blog, we walk through how to restrict your application’s access so it can only read emails from a specific list of users, using Application Access Policies—a secure and recommended approach.

Relevant Microsoft Documentation

Understanding the Problem

Graph Application Permissions grant broad, tenant-wide access. This is ideal for:

  • Background services
  • Daemon applications
  • Automation tools

But it also means the app can access all mailboxes unless restricted.

Examples of apps that should NOT have tenant-wide access:

  • A ticketing system that reads only support@contoso.com
  • An Azure Function processing emails for one department
  • A compliance tool reading from a select shared inbox

To enforce the principle of least privilege, we need mailbox-level controls.

How Application Access Policies Solve This

Application Access Policies allow Exchange Online to limit an application to a specific set of mailboxes, defined by membership in a Mail-Enabled Security Group (MESG).

What the Policy Does

  • Allows Graph access only to users in the MESG
  • Blocks access to all other mailboxes (returns 403 Forbidden)
  • Applies automatically to all Graph calls using application permissions

Important Propagation Delay

After creating or updating a policy, it takes 30 minutes to 2 hours for the policy to fully propagate across the Microsoft 365 ecosystem.

Testing immediately after creation may produce inconsistent results until the policy finishes syncing.

End-to-End Implementation Flow

  1. Create the Azure AD App Registration
  2. Create a Mail-Enabled Security Group (MESG)
  3. Install and Connect Exchange Online PowerShell
  4. Create the Application Access Policy
  5. Validate the Policy
  6. Validate Using Microsoft Graph
  7. Review a Real-World Scenario
  8. Conclusion

1. Create the Azure AD App Registration

Steps

  • Go to Azure AD → App registrations → New registration
  • Enter a name
  • Select "Accounts in this organizational directory"
  • Register the app

Add a Client Secret

Create a client secret for authentication between your application and Microsoft identity platform.

Assign Microsoft Graph Application Permissions

  • Mail.Read
  • Mail.ReadWrite (if required)
  • User.Read.All (optional)

After adding permissions, Grant admin consent.

App registration permissions

2. Create a Mail-Enabled Security Group (MESG)

Only mail-enabled security groups are supported by Application Access Policies.

Unsupported Group Types

  • Microsoft 365 groups
  • Distribution lists
  • Standard security groups

Example MESG:

  • Name: AllowedMailboxAccess
  • Email: allowed.mailaccess@contoso.com

Add Allowed Mailboxes

  • john.doe@contoso.com
  • jane.smith@contoso.com

Do not add users who should be restricted.

MESG configuration

3. PowerShell Requirements

Install Exchange Online Module

Install-Module ExchangeOnlineManagement -Scope CurrentUser

Connect to Exchange Online

Connect-ExchangeOnline

Authentication is required before creating or managing Application Access Policies.

4. Create the Application Access Policy

New-ApplicationAccessPolicy `
  -AppId "YOUR-APP-ID-HERE" `
  -PolicyScopeGroupId "allowed.mailaccess@contoso.com" `
  -AccessRight RestrictAccess `
  -Description "Restrict Graph Mail.Read access to approved users"

What This Enforces

  • The application can only access mailboxes in the MESG
  • All other mailboxes return 403 Forbidden
  • Applies to client_credentials flow

5. Validate the Policy

Test an Allowed Mailbox

Test-ApplicationAccessPolicy `
  -Identity "john.doe@contoso.com" `
  -AppId "YOUR-APP-ID-HERE"

Expected: AccessCheckResult : Granted

Test a Blocked Mailbox

Test-ApplicationAccessPolicy `
  -Identity "unauthorized.user@contoso.com" `
  -AppId "YOUR-APP-ID-HERE"

Expected: AccessCheckResult : Denied

Note:If you receive unexpected results, allow 30 minutes to 2 hours for propagation.

6. Validate Using Microsoft Graph

Allowed mailbox:

GET https://graph.microsoft.com/v1.0/users/john.doe@contoso.com/messages

Should return 200 OK.

Blocked mailbox:

GET https://graph.microsoft.com/v1.0/users/unauthorized.user@contoso.com/messages

Should return 403 Forbidden.

7. Example: Real-World Scenario

An AI email classifier running as an Azure Function should only access:

  • support@contoso.com
  • billing@contoso.com

Without restrictions, it could access executive, HR, or legal mailboxes. With an Application Access Policy, access is limited strictly to approved accounts.

Conclusion

Application Access Policies are the safest and most effective way to restrict Microsoft Graph application permission access to specific mailboxes.

  • Least privilege access
  • Compliance-friendly restrictions
  • No accidental overexposure of user data
  • Server-enforced controls beyond application code

This creates a secure and controlled environment for Exchange Online mailbox access.