๐งฉ๐งโ๐ผAC - User Consent for Applications
Only admins grant applications access to company data, with a request workflow so that stays workable.
What this policy is about ๐งฉโ
Every user in a default tenant can hand a third-party application standing access to their mailbox, their files, and the directory they can read. One consent screen, one click on Accept, and an app nobody vetted holds a refresh token in someone else's cloud.
This page turns that off, and then does the part everyone skips: builds the request path that keeps people working, so the block does not get reversed the first week a director cannot connect their new AI note-taker. ๐ค
Consent is not a login. It is a delegation. The user does not hand over a password, they hand over an ongoing, token-based right to act as them. Changing that password later does nothing about it.
Why this matters ๐ฃโ
The illicit consent grant is the attack that walks straight past everything you spent last year deploying.
An attacker registers an app, gives it a name like "Invoice Viewer" or "M365 Security Scanner", and mails a link that lands on a genuine login.microsoftonline.com consent screen. Real Microsoft domain, real certificate, real sign-in the user already completed today. The prompt asks to read mail and files, and it looks exactly like the fifteen other prompts that turned out fine.
Now count what did not save you:
- MFA did not fire. The user authenticated legitimately. The attacker never wanted the password. ๐
- The password reset did not help. A refresh token is not a password. Reset it, rotate it, expire it, the token keeps working until someone explicitly revokes the grant.
- Conditional Access mostly did not see it. The app acts through Graph with the permissions it was given, from the attacker's infrastructure, with a token it already holds.
- Nobody got an alert. There is no notification when a user consents. The record sits in the audit log with a generic app name, waiting for someone to go looking. ๐
What the attacker gets depends on what was asked for and granted: the mailbox contents, the OneDrive, everything shared with that person in SharePoint, and the directory they can read. Quiet, persistent, and it survives the incident response that focuses on credentials.
The second problem is duller and far more common. Nobody attacks anything, and a hundred SaaS trials, browser extensions, and AI assistants accumulate standing access to company data, one enthusiastic click at a time. There is no inventory, no owner, and no expiry. That is not an incident. That is Tuesday. ๐
๐ ๏ธ Configurationโ
Three settings on two pages. The block is the easy part. The workflow is what makes it stick.
Role: the portal path wants Global Administrator. Doing the same thing through Graph needs only Privileged Role Administrator, which is the least-privileged route and the one to prefer.
1. User consent settingsโ
Where: Entra admin center โ Identity โ Applications โ Enterprise apps โ Consent and permissions โ User consent settings.
| Setting | Value | Why |
|---|---|---|
| User consent for applications | Do not allow user consent | Consent becomes an admin decision. Users can still use every app you approved, they just cannot approve new ones on your behalf. |
| Group owner consent for apps accessing data | Do not allow group owner consent | Otherwise the door reopens one storey up: a group owner consenting on behalf of a Team hands an app access to that Team's files and conversations. |
The middle option, allow user consent for apps from verified publishers, for selected permissions, is the fallback for a tenant that genuinely cannot staff a review queue. It is a real improvement over the default, and it is still a stranger's app reading a mailbox because a user clicked yes. Treat it as a stepping stone, not the destination. If you use it, go to Permission classifications on the same page and confirm what counts as low impact, because that list is doing all the work. ๐ช
2. Admin consent requestsโ
Where: same section โ Admin consent settings.
| Setting | Value | Why |
|---|---|---|
| Users can request admin consent to apps they are unable to consent to | Yes | Without this, the block is a dead end with no next step, and a dead end is what sends someone to their personal account with a company document attached. |
| Who can review admin consent requests | Named reviewers | Pick people, not a role blanket. Reviewers need a role that can actually grant consent, so Cloud Application Administrator or Application Administrator. Requests touching directory roles need a higher role to approve. |
| Selected users will receive email notifications for requests | Yes | A queue nobody is told about is a queue nobody empties. |
| Consent request expires after | 30 days | Requests should die of old age rather than sit forever. If it took a month, the answer was no. |
3. Do the same thing in Graphโ
The portal is fine for one tenant. For twenty, and for proving what a tenant is set to:
Connect-MgGraph -Scopes "Policy.ReadWrite.Authorization"
# What is assigned right now
(Get-MgPolicyAuthorizationPolicy).DefaultUserRolePermissions |
Select-Object -ExpandProperty PermissionGrantPoliciesAssigned
Read that output before you write anything. You are looking for managePermissionGrantsForSelf.microsoft-user-default-legacy (users can consent to anything not requiring admin consent, the wide-open default) or ...microsoft-user-default-low (the verified-publisher middle tier).
# Block user consent, keeping any owned-resource policies that were already there
Update-MgPolicyAuthorizationPolicy -BodyParameter @{
defaultUserRolePermissions = @{
permissionGrantPoliciesAssigned = @()
}
}
That empty array is the whole setting. One caveat with teeth: if the tenant had a managePermissionGrantsForOwnedResource.* entry, an empty array wipes that too. Copy the existing entries you want to keep back into the array instead of blanking it, or the developers who manage consent for their own apps will find out the hard way. ๐งจ
4. Find out what was already granted ๐งนโ
Turning consent off changes nothing about what users consented to last year. Existing grants keep working. That inventory is the actual security work, and it is the finding that makes the report worth reading:
Connect-MgGraph -Scopes "Directory.Read.All"
Get-MgOauth2PermissionGrant -All |
Where-Object { $_.ConsentType -eq 'Principal' } |
Select-Object ClientId, PrincipalId, Scope
ConsentType = Principal means an individual user consented for themselves. That is the pile worth reading. Sort by Scope and look for Mail.Read, Files.Read.All, Mail.Send, and anything ending in .ReadWrite.All. Resolve ClientId with Get-MgServicePrincipal to turn a GUID into a name you can ask the business about. ๐
Removing a grant is not the end of it either. Revoke the grant, then revoke the sessions, or an issued refresh token keeps working until it expires on its own schedule:
Remove-MgOauth2PermissionGrant -OAuth2PermissionGrantId <id>
Revoke-MgUserSignInSession -UserId <upn>
Caveats โ ๏ธโ
Day one produces tickets. Plan for it. Users will hit "Approval required" on things that used to just work. That is the policy functioning correctly, and it is also the moment it gets reversed if nobody warned anyone. Announce it, staff the queue, and answer fast for the first fortnight. An approval that takes three days teaches people to route around IT. ๐ซ
The reviewer queue is the whole policy. This setting converts a security problem into a service-desk commitment. If nobody owns the queue, the tenant ends up back on the default within a quarter, usually with an irritated director attached. Decide who reviews and what "yes" requires (publisher verified, permissions proportionate, a business owner named) before you flip the switch.
Admin consent is tenant-wide, so it is a bigger yes. When an admin approves, the app is approved for everyone, not just the requester. That is stronger governance and a heavier decision. Scope with Assignment required on the enterprise app plus a group, so an approved app reaches the team that asked and nobody else.
Apps that require assignment already need an admin. If an enterprise app has user assignment required, its permissions need admin consent regardless of the user consent setting. Some of your "this broke" reports will predate the change. Check before assuming.
License fit (MKB lens). User consent settings and the admin consent workflow are Entra ID Free. Business Premium includes them. There is no upsell here. Automatic detection of risky consent grants and app anomaly policies is a different, paid conversation, which is precisely why the manual inventory above matters at this tier. ๐ถ
Reversibility. Clean-revert. Setting it back to the legacy policy restores the old behaviour instantly, and it leaves no trace beyond an audit log entry. Alert on changes to the authorization policy: an attacker who lands a privileged account will re-enable user consent, because that is how they make their access outlive the password reset. ๐จ
๐ฅ Assignment scopeโ
Tenant-wide, applied to the default user role. There is no per-user exception, and that is by design: the answer to "this team needs that app" is an admin consenting to that app once, then assigning it to that team, not a carve-out that lets one department consent to anything.
Check it monthly. An authorization policy with a non-empty permissionGrantPoliciesAssigned containing a managePermissionGrantsForSelf.* entry is the signal that someone opened it back up. Re-run the grant inventory in the same pass, because new Principal grants appearing after the block is in place means it was off for a while and nobody said so. ๐
On tenant takeover, inventory before you change anything. Which policy is assigned, which apps hold user-consented grants, which of those nobody can name. Blocking consent takes a minute. The list of apps already reading the customer's mailboxes is the deliverable.
๐ Relatedโ
- ๐ข๐งโ๐ผTS - Default User Permissions: the neighbouring tenant defaults, including who may register applications in the first place.
- ๐จ๐งโ๐ผIP - User Risk Policy: the account-compromise path this one deliberately sidesteps, which is exactly why you need both.
Consent phishing does not steal a password. It asks the user for a key, politely, and the user hands it over. Take the key off the ring. ๐งฉ