Tag Archives: Application Permissions

Using Microsoft Graph SelectedOperations Permissions

WIP – work in progress

Initially, *.Selected permissions scope in Graph API only restricted app access to a site collection. Now, it also supports lists, items, folders, and files, and all ‘Selected’ scopes work in both delegated and application modes. This means more granular SharePoint content access for custom apps using the Graph API. Below, I’ll deep-dive into SelectedOperations.Selected permissions and include Python and PowerShell samples for using them.

Client Application

There must be an App Registration for client application that would have access to a SharePoint list.

At this moment (we have a client app and secret, and “” API permissions, but did not provide for this app access to specific sites or libraries) – we should be able to authenticate to Microsoft 365, but not able to get any kind of data (we can get token, but other call to Graph API would return 403 error – Error: b'{“error”:{“code”:”accessDenied”,”message”:”Request Doesn\’t have the required Permission scopes to access a site.”,):

This app registration should have Microsoft Graph “Lists.SelectedOperations.Selected” (or “ListItems.SelectedOperations.Selected” or “Files.SelectedOperations.Selected”) API permissions consented. Registered app Microsoft Graph API permissions should look like:

I use Python console app as a client application. Link to the code at the GitHub is shared below under References, but the core part of the Python code is (I do not use any Microsoft or other libraries here, just plain requests to Microsoft Graph for authentication and for data):

import requests
import json
from secrets import clientSc, clientId, tenantId, siteId, listId 

# specify client id, client secret and tenant id
# clientId = ""
# clientSc = "" 
# tenantId = "" 

apiUri = "https://login.microsoftonline.com/" + tenantId + "/oauth2/v2.0/token"

body = {
    "client_id"     : clientId,
    "client_secret" : clientSc,
    "scope"         : "https://graph.microsoft.com/.default",
    "grant_type"    : "client_credentials" 
}

try: 
    response = requests.post(apiUri, data=body)
    token = json.loads(response.content)["access_token"]
except:
    print("Error: ", json.loads(response.content)["error_description"])
    exit()

print("Got token: ", token[0:10], "...")
headers={'Authorization': 'Bearer {0}'.format(token)}

# Get specific site list
print("Geting specific site list")
# graph_url = 'https://graph.microsoft.com/v1.0/sites/' + siteId + '/lists/' + listId
graph_url = 'https://graph.microsoft.com/beta/sites/' + siteId + '/lists/' + listId
graphResponse = requests.get(   graph_url, headers=headers )
print(" Response status code: ", graphResponse.status_code)
if graphResponse.status_code == 200:
    list = json.loads(graphResponse.content)
    print(" List display name: ", list["displayName"])

References

Microsoft Graph SelectedOperations Permissions to SharePoint

Microsoft says “Initially, Sites.Selected existed to restrict an application’s access to a single site collection. Now, lists, list items, folders, and files are also supported, and all Selected scopes now support delegated and application modes.”. In other words, Microsoft started supporting even more granular access to SharePoint content from custom applications using Microsoft Graph API.

In the article below I will deep-dive into SelectedOperations.Selected permissions, including PowerShell scripts to provide granular permissions (if you want to know more about Sites.Selected Graph API permissions – it’s here).

Why do we need SelectedOperations.Selected Graph API permissions

Granular permissions available are:

Lists.SelectedOperations.SelectedProvides application access to a specific list
ListItems.SelectedOperations.SelectedProvides application access to one or more list items, files, or folders
Files.SelectedOperations.SelectedProvides application access to to one or more files or library folders

Set of SelectedOperations permissions is exactly what Microsoft promised a few years ago. And this is great, as we really need granular access to the content in SharePoint sites. I’ve been supporting enterprise SharePoint for more than 15 years now, and I know that it was always a concern if application in fact requires access to a specific list/library or a folder or even one file, but admins provide access to entire site collection.

Especially, I believe, this feature will become more in demand due to Copilot for Microsoft 365. Currently – it’s mostly developers and data analytics who needs unattended application access to SharePoint, but sooner or later regular users powered with m365 Copilot license will start creating autonomous agents…

Here is the screenshot of a Copilot agent authentication/access to SharePoint data using client id and secret:

So below is my research and lab setup, guide with screenshots and PowerShell scripts on how to provide granular (to library/list or folder, or even just one document or list item) Graph API permissions to SharePoint. This KBA is for Microsoft 365 SharePoint administrators. I’m planning to have a separate KBA for developers on how to use granular permissions.

Prerequisites

Admin App

First, we need an Admin App – an app we will be using to provide permissions.

The only requirement to the app is: the app should have Microsoft.Graph Sites.FullControl.All Graph API permissions consented:

Target Site, List. Item

Your client will probably provide you with the link to the SharePoint resource they need access to. But to do both – to provide granular permissions – or to access one specific list, folder or item – we need to know this site id, list id, item id. So it’ll be our admins job to decompose the link and get Ids to provide access, then share these Ids with the client with some instructions on how to call Graph API to get access.

For this lab/demo setup, I have created three sites under Microsoft Teams (group-based Teams-connected SharePoint sites), then test list and test library in each, like this:

Client Application

There must be an App Registration for client application – application that will have access to Test-List-01 and Test-Lib-01 only. This app registration should have Microsoft Graph “Lists.SelectedOperations.Selected” or “ListItems.SelectedOperations.Selected” or “Files.SelectedOperations.Selected” API permissions consented. Example below has Lists:

Providing selectedoperations permissions

PowerShell script to provide selectedoperations.selected access for an app to a specific list would be as below. Here we use plain calls to MS Graph API. Full script for your refence is available at GitHub, but here is the essential part:

$apiUrl = "https://graph.microsoft.com/beta/sites/$targetSiteId/lists/$targetSiteListId/permissions"
$apiUrl 
$params = @{
	roles = @(
	    "read"
    )
    grantedTo = @{
        application = @{
            id = $clientAppClientId
        }
    }
}
$body = $params | ConvertTo-Json
$response = Invoke-RestMethod -Headers $Headers -Uri $apiUrl -Method Post -Body $body -ContentType "application/json"

Notes

  1. (Update from 2025-10-28) SelectedOperations permissions are still in beta. E.g. I have to call “https://graph.microsoft.com/beta/sites/$targetSiteId/lists/$targetSiteListId/permissions”
    When trying to call “/v1.0/sites/$targetSiteId/lists/$targetSiteListId/permissions” it says
    "code": "BadRequest", "message": "Resource not found for the segment \u0027permissions\u0027."
  2. The “/beta/sites/$targetSiteId/lists/$targetSiteListId/permissions” API returns not only applications permissions, but also user’s permissions!
  3. TBD: I’m not sure if it’s a bug or my incorrect setup, but I noticed that if I provide access for the app to the list – app can read site.

TBC…

References

Calling Microsoft Graph API from Python

Below is how I authenticate and call Microsoft Graph API to work with SharePoint from Python application.

Plain

no MSAL or Azure libraries used:

import requests
import json
from secrets import clientSc 

clientId = "7e60c372-ec15-4069-a4df-0ab47912da46"
# clientSc = "<imported>" 
tenantId = "7ddc7314-9f01-45d5-b012-71665bb1c544"

apiUri = "https://login.microsoftonline.com/" + tenantId + "/oauth2/v2.0/token"

body = {
    "client_id"     : clientId,
    "client_secret" : clientSc,
    "scope"         : "https://graph.microsoft.com/.default",
    "grant_type"    : "client_credentials" 
}

response = requests.post(apiUri, data=body)
token = json.loads(response.content)["access_token"]

graph_url = 'https://graph.microsoft.com/v1.0/sites/root'
site = requests.get(
    graph_url,
    headers={'Authorization': 'Bearer {0}'.format(token)}
)

print(site.content)
print(json.loads(site.content)["webUrl"])

secrets is a Python file where I assign client secret to variable clientSc (so my secret is not shared on github). This is ok for demo purposes but generally, you should not hard-code secrets but keep secrets somewhere safe (Vault).

MSAL

Using MSAL library to get bearer token:
https://github.com/VladilenK/m365-with-Python/tree/main/Graph-API-MSAL

References

Providing ACS permissions for app to access SharePoint

SharePoint app-only service principals (also know as ACS-based apps) were introduced >10 years ago as an option to get access to SharePoint in unattended manner – using client id and client secret with no user present. Later, with MS Graph introduction, Microsoft was saying “SharePoint App-Only is the older, but still very relevant, model of setting up app-principals.”. Finally, this ACS era comes to an end.

Microsoft retires ACS authentication and App-Only principals

Let me quote Microsoft (Dec 18, 2023):

  • “… we will be retiring the use of Azure ACS (Access Control Services) for SharePoint Online auth needs and believe Microsoft 365 customers will be better served by modern auth…”
  • “Azure ACS will stop working for new tenants as of November 1st, 2024 and it will stop working for existing tenants and will be fully retired as of April 2nd, 2026…
    There will not be an option to extend using Azure ACS with SharePoint Online beyond April 2nd, 2026″
  • “… we recommend switching those applications to use Microsoft Entra ID for authorization and authentication needs…”

So, for new development it is strictly recommended using Azure Registered Apps and Graph API to access Microsoft 365 resources programmatically and avoid using SharePoint App-only principals.
Azure ACS retirement: How to prepare your tenant – Guide for SharePoint Admins
Azure ACS retirement: Track down App-Only (ACS) apps

Below is a legacy content:

You still need ACS in some cases

But, as always, it all is not so simple, as

  • there are still plenty of 3-rd party applications written and used widely that require ACS-based permissions. Moreover, there are still some 1-st party applications (Microsoft apps and services) that require ACS-based permissions
  • though Microsoft Graph API is good and provide a lot of functionality and is developing rapidly, it cannot cover all SharePoint dev’s needs, so using SharePoint REST API could be unavoidable… and that is where some complications are coming
  • permissions to specific SharePoint sites (not to all tenant sites, but to one or several SharePoint sites in tenant) for apps is done via Sites.Selected, but this works to entire site collection only. E.g. via Sites.Selected you cannot provide granular permissions (e.g. to specific list) for an app, which might be crucial in some cases, so you’d still have to use ACS-based permissions

Hopefully, Microsoft will resolve all the issues above before April 2026… But for now we have to live with both – Azure Registered applications and API permissions configured in Entra ID and with SharePoint app-only service principals and ACS-based permissions.

Feb 2025 update:
There are less and less apps that require ACS, as most vendors updated their applications. E.g.
– Azure Data factory v2 supports Sites.Selected permissions
– Alteryx SharePoint connection since v 2.2.supports Sites.Selected permissions

Azure Apps and Entra Id vs SharePoint app-only spn and ACS

Comparison between Azure Apps and Entra Id API permissions vs SharePoint app-only spn and ACS-based permissions

ACS-based SharePoint app/permissionsApps registered in Azure with Sites.Selected API permissions
support authentication with client secret only, secret is valid for 1 year exactlysupport authentication with client secret and/or certificate, custom expiration time
support granular access to SharePoint site content – e.g. to entire site collection or web (subsite) or a specific listsupport only access to entire site collection (but Microsoft is working on granular access)
Now (Sep 2024) Microsoft supports granular (list, library, Item, Document level) access for an app to a site.
support only classic SharePoint REST API and CSOMsupport both – classic SharePoint REST API and CSOM and Microsoft Graph API
app id (client id) is created via appregnew.aspx at a specific SharePoint site by site collection administratorapp id (client id) is created in Azure portal, API Sites.Selected permissions are configured via Azure portal and require tenant admin consent
permissions for the app to a site are provided at the site by site collection administrator via appinv.aspx pagepermissions for the App to to a specific SharePoint site/list/item are provided by SharePoint admin with PowerShell script or Graph API calls
logging audit log

SharePoint app-only service principal and ACS-based permissions

Since SharePoint app-only service principals and ACS-based permissions were introduced for SharePoint 2013 as part of Add-Ins feature – there are plenty of articles from Microsoft and MVPs and SharePoint gurus on this. But I would like to highlight one thing:

  • AppRegNew page creates service principal and allows authentication
  • AppInv page provides permissions and allows authorization to SharePoint

Check SharePoint AppRegNew.aspx and AppInv.aspx for details

Both – using AppRegNew.aspx and AppInv.aspx should be stopped now.

Recommended transition tactics

Feb 2025 Update: there is a newer article: “Azure ACS retirement: How to prepare your tenant

For developers

  • Request a new App Registrations and Sites.Selected permissions
  • Prioritizing using Microsoft Graph API. In cases Graph API does not provide required functionality – it’s ok to use SharePoint API, but please ensure certificate is used (not secret)
  • Stop using ACS

For SharePoint admins

  • Encourage users register applications in Azure (not in SharePoint)
  • Disable ability for site owners register service principals in SharePoint via appregnew.aspx
    Your users will start seeing “Your SharePoint tenant admin doesn’t allow site collection admins…” message (see details), but that’s ok.
  • Create a process so users can request application permissions to SharePoint. Provide Sites.Selected permissions by default. Consider automation.
    In rare cases when 3-rd party apps require legacy ACS-based permissions, it would be you (SharePoint service admin) who will provide ACS-based access to sites.
    Track this activity (so you know for whom this ACS-based permissions were provided).
    Inform every developer that ACS will be gone.
  • Keep audit logs
    Starting today and until it’s over you’d get audit logs from Microsoft 365 purview center – consider selecting all events anyone visited appinv.aspx page.
  • In March-April 2025 (1 year before) ACS EOL, start notifying developers who use ACS.
    You can get list of developers combining
    – audit log data
    – report from Entra Id on apps owners
  • In advance ( let say, starting September 2025) you can try to temporary switch off ACS (“scream test”).

References