Microsoft Graph permissions explained: delegated vs application, consent and least privilege
Every app that reads mail, lists users or manages devices through Microsoft Graph does it with a permission someone granted. Here's how those permissions work, how to read them, who can grant them, and how to find the apps in your tenant that have far more than they need.
On this page
Microsoft Graph is the API behind most of Microsoft 365 and Entra ID. Apps don't get access to it by default. They're granted permissions, and those permissions decide exactly what an app can read or change. Getting them wrong is one of the most common ways tenants end up with an app that can read every mailbox or reset any password.
How to read a permission name
Every permission follows the same pattern: {resource}.{operation}.{constraint}.
- Resource is what it applies to:
User,Mail,Group,Sitesand so on. - Operation is what the app can do:
Read,ReadWrite,Create,Send. - Constraint limits the scope.
.Allmeans everything in the tenant..OwnedBymeans only things the app owns. With no constraint, Microsoft says "the default constraint is limited to data that's owned by the signed-in user."
So User.Read reads the signed-in user's own profile, User.Read.All reads every user, and Application.ReadWrite.OwnedBy manages only the apps that this app owns. Comparing similar names side by side helps: see User.Read vs User.Read.All or Group.Read.All vs GroupMember.Read.All.
Delegated vs application permissions
The same permission name can exist in two forms, and the difference matters more than the name.
| Delegated | Application | |
|---|---|---|
| Also called | Scopes | App roles |
| Signed-in user? | Yes, the app acts on their behalf | No, the app acts as itself |
| What it can reach | Only what the permission and the user both allow | Everything the permission covers, across the tenant |
| Who can consent | Users, for their own data, if your consent policy allows. Admins for everyone | Admins only |
The key line from Microsoft: with delegated permissions, "the application can't access anything the signed-in user couldn't access". With application permissions, "the application can access any data that the permission is associated with."
That's why Mail.Read as a delegated permission reads the signed-in user's mail, but Mail.Read as an application permission reads every mailbox in the tenant, unless you limit it with Exchange Online's RBAC for Applications. Microsoft's guidance is that "the delegated permission model is the recommended approach whenever it meets the application's requirements."
Who can grant them
For application permissions, Microsoft is specific: "Only Privileged Role Administrator and Global Administrator can consent to application permissions." That makes those two roles a high-value target, and it's one reason to make them eligible in PIM rather than permanently assigned. See activating PIM roles.
For delegated permissions, users can consent for their own data unless your consent settings stop them. Many tenants restrict user consent to low-risk permissions from verified publishers, and send everything else through an admin consent workflow.
Least privilege in practice
Microsoft's rule is simple: "Grant the app the minimum permission it needs for the operation." In practice:
- Prefer Read over ReadWrite. If the app only reports, it doesn't need to write. Every resource with both is compared on the comparisons page.
- Prefer a narrow resource over
Directory.*.Directory.Read.Allreads almost everything in Entra ID. If the app only needs users,User.Read.Allis smaller. See Directory.Read.All vs User.Read.All. - Use the selected-scope permissions where they exist.
Sites.Selectedgives access only to the SharePoint sites you grant explicitly, instead of every site withSites.Read.All. - Prefer delegated where a user is present. It can never exceed what that user can do.
Every Graph permission has its own page on this site, with what it allows, its risk and smaller alternatives: Microsoft Graph permissions explained.
Find the apps with the most access
Application permissions to Microsoft Graph are stored as app role assignments on the Microsoft Graph service principal. This lists every app that holds one, and which permission it holds:
Connect-MgGraph -Scopes "Application.Read.All"
$graph = Get-MgServicePrincipal -Filter "appId eq '00000003-0000-0000-c000-000000000000'"
$names = @{}; $graph.AppRoles | ForEach-Object { $names[$_.Id] = $_.Value }
Get-MgServicePrincipalAppRoleAssignedTo -ServicePrincipalId $graph.Id -All |
Select-Object PrincipalDisplayName, @{ n = "Permission"; e = { $names[$_.AppRoleId] } } |
Sort-Object Permission, PrincipalDisplayNameLook first for anything ending in ReadWrite.All, plus Mail.Read, Mail.Send, Files.Read.All, Sites.Read.All, RoleManagement.ReadWrite.Directory and AppRoleAssignment.ReadWrite.All. Then ask the owner of each app whether it still needs that permission, and whether a smaller one would do. The app permission risk checker can rate an export for you in the browser.
Checklist
- Restrict user consent, and turn on the admin consent workflow.
- Keep Global Administrator and Privileged Role Administrator in PIM, because they're the roles that can grant application permissions.
- Review application permissions to Microsoft Graph at least every quarter.
- For every
.Allpermission, ask whether a narrower one exists. - Prefer delegated permissions whenever a user is signed in.