August 26, 2025

Blog

API Permissions, Entra Roles, and Consent in Microsoft Graph — Everything You Need to Know

API Permissions, Entra Roles, and Consent in Microsoft Graph — Everything You Need to Know

Why Delegated Permissions in Microsoft Graph Don’t Mean What You Think

I’ve lost count of how many times I’ve heard this exact line:

“If I grant an app delegated Graph permissions with admin consent, the app can do everything in the tenant.”

When I began working with Microsoft Graph and Entra ID, I wondered the same. If an app got a delegated scope like PrivilegedAccess.ReadWrite.AzureADGroup, did that mean anyone in my tenant could use it? I decided to write this post because I wish someone had clarified it for me—plainly and honestly, not as a vendor pitch but as a fellow practitioner who once felt the same confusion I’m guessing you might.


The Reality Behind Delegated Permissions

One crucial insight changed everything for me: delegated permissions always run in the context of the signed-in user. That means the user’s existing Entra role remains a gatekeeper—just as much as the app’s permissions.


TLDR

  • Delegated permissions = App + User together. The app can only do what the user’s Entra role already allows.

  • Application permissions = App alone. Runs under the service principal and bypasses user role checks.

  • Admin consent doesn’t grant new powers to users, it only removes consent prompts.

  • Conditional Access applies to delegated (user sign-in) but not application permissions.

Delegated vs. Application Permissions — What’s the Difference?

Delegated Permissions (user + app)

In this scenario:

  • The app acts on behalf of a signed-in user.

  • Both the app must have the correct delegated scope, and the user must hold the necessary Entra role to perform the action.

  • If either is missing—the app’s scope or the user’s role—the call fails due to insufficient privileges.

Example (real test I ran):

I tried Graph CLI with PrivilegedAccess.ReadWrite.AzureADGroup.

  • As a normal user: every call failed.

  • As Global Admin: suddenly it all worked.

That was my “aha” moment: delegated permissions don’t elevate users—they simply let the app execute what the user could already do manually.

Application Permissions (app alone)

Here there’s no user involved—the app runs under its service principal identity.

The action succeeds solely based on the permissions granted to the app, independent of any user role.


Example:

An app with Group.ReadWrite.All (application permission) can create or delete groups even with no user signed in.

This is why application permissions must be treated as privileged service accounts. They bypass user checks entirely.

Entra Directory Roles: The Overlooked Link

It’s tempting to think API permissions alone determine what gets done—but that’s just half the story. The user’s Entra role matters equally in the delegated model.

Delegated Graph Permission → Required Entra Role(s)

Group.ReadWrite.All → Global Admin, Groups Admin, Directory Writers

RoleManagement.ReadWrite.Directory → Global Admin, Privileged Role Administrator

PrivilegedAccess.ReadWrite.AzureADGroup → Global Admin, Privileged Role Administrator

Directory.ReadWrite.All → Global Admin only

Even with app permission in place, you’re stuck if your user identity doesn’t match the roles above. It’s simple: delegated is meaningful only when app + user role align.


Consent Models: User vs. Admin

User Consent:

  • Each user must individually approve delegated scopes when they sign in.

  • Can be disabled or restricted by tenant admins.

Admin Consent:

  • A Global Admin can pre-approve app permissions for the entire tenant.

  • That means no more prompts for users.

  • But here’s the catch: it does not bypass the user’s required role. A non-admin user will still be blocked from privileged actions—even though the permissions are “consented.”


Security Risks and Misuse:

From experience, here are the most common mistakes I’ve seen:

  • Over-using application permissions just because they seem simpler.

  • Believing that admin consent = tenant-wide superpowers.

  • Neglecting to audit apps that hold broad scopes like Directory.ReadWrite.All.


The danger isn’t in delegated permissions—it’s in misunderstanding how they work, or over-provisioning application permissions without oversight.

Best Practices I Now Follow:

  • Prefer delegated permissions where possible: they’re naturally safer because they inherit the user’s least privilege.

  • Be cautious with application permissions: treat them like privileged service accounts.

  • Monitor and audit which apps have high-impact Graph scopes.

  • Use admin consent sparingly and only for trusted apps.

  • Regularly review service principals with app-only permissions—know what they can really do.


Dynamic vs. Static Consent:

One detail I learned from Microsoft docs: delegated permissions can be granted dynamically at runtime (incremental consent). That means an app can request additional scopes as needed, and the user decides in the moment.

Application permissions don’t have this flexibility—they’re static and must be pre-approved by admins.

This flexibility is another reason delegated permissions are often the better choice.


Conditional Access: Another Difference:

Conditional Access (CA) rules apply to delegated permissions, because a user is signing in. If CA requires MFA, device compliance, or specific networks, those policies will apply.

With application permissions, there’s no user sign-in—so CA policies don’t apply. This is an often-overlooked governance gap.


Final Word:

Delegated = User + App, constrained by Entra role

Application = App alone, governed by its service principal

Admin consent removes prompts, not role checks

If you take one thing away from this blog, let it be this: delegated permissions don’t elevate users; they respect their boundaries.

for more info: https://learn.microsoft.com/en-us/entra/identity-platform/permissions-consent-overview