A cloud-based identity and access management service for securing user authentication and resource access
Bug Report: prompt=consent blocks non-admin users even when tenant-wide admin consent has been granted
Summary
When an OAuth 2.0 authorization request includes prompt=consent, non-admin users are blocked with the "Need admin approval" (AADSTS90094) screen, even when a Global Administrator has already granted tenant-wide admin consent for the application. Replacing prompt=consent with prompt=select_account (or omitting the parameter) allows the same users to authenticate successfully without any issues.
This behavior is inconsistent and effectively makes prompt=consent unusable for applications that require admin-level permissions in tenants where user consent is restricted.
Environment
- Identity Provider: Microsoft Entra ID (formerly Azure AD)
- OAuth endpoint:
https://login.microsoftonline.com/common/oauth2/v2.0/authorize - Permissions requested (delegated):
Mail.ReadWrite,Mail.Send,User.Read,offline_access - App type: Multi-tenant
- Issue started: Around March 2026
- No changes were made to the application or Entra ID configuration on our side
Steps to Reproduce
Prerequisites
- Register a multi-tenant application in Microsoft Entra ID
- Configure delegated permissions:
Mail.ReadWrite,Mail.Send,User.Read,offline_access - A Global Administrator grants tenant-wide admin consent (via Enterprise Applications > Permissions > "Grant admin consent for [Organization]", or by signing in with "Consent on behalf of your organization" checked)
- The tenant's user consent setting is set to "Allow user consent for apps from verified publishers, for selected permissions" or more restrictive
Reproduce with prompt=consent (FAILS)
A non-admin user accesses the following authorization URL:
https://login.microsoftonline.com/common/oauth2/v2.0/authorize
?client_id={app_id}
&response_type=code
&redirect_uri={redirect_uri}
&scope=Mail.ReadWrite Mail.Send User.Read offline_access
&prompt=consent
Result: The user is shown the "Need admin approval" / "AADSTS90094" screen and is completely blocked. The admin has already consented, but the user cannot proceed.
Reproduce with prompt=select_account (WORKS)
The same non-admin user accesses:
https://login.microsoftonline.com/common/oauth2/v2.0/authorize
?client_id={app_id}
&response_type=code
&redirect_uri={redirect_uri}
&scope=Mail.ReadWrite Mail.Send User.Read offline_access
&prompt=select_account
Result: The user successfully authenticates without any consent error.
Expected Behavior
When tenant-wide admin consent has been granted, prompt=consent should display the consent screen (showing the scopes the application will access) and allow the non-admin user to proceed, since the required permissions have already been approved by an administrator.
The OpenID Connect Core 1.0 specification (Section 3.1.2.1) defines prompt=consent as:
The Authorization Server SHOULD prompt the End-User for consent before returning information to the Client.
The specification says the server should prompt for consent -- it does not say the server should block the user when admin consent is already in place.
Actual Behavior
prompt=consent causes Microsoft Entra ID to require the user to grant consent, ignoring the existing tenant-wide admin consent. Since the tenant policy restricts user consent for these permission scopes, the user is blocked with no way to proceed.
Why This Is a Bug, Not Expected Behavior
1. Inconsistent policy evaluation
The same tenant-wide admin consent is respected when prompt=select_account is used but ignored when prompt=consent is used. The consent policy (whether the app is authorized) should not depend on the prompt parameter, which is a UX hint for whether to show the consent screen.
2. Contradictory behavior with admin consent
If an administrator has explicitly approved the application on behalf of the entire organization, prompt=consent should honor that approval and show the consent screen informatively (or skip it). Instead, it treats the request as if no admin consent exists, and blocks the user.
3. Impossible state: admin consent = granted, yet user is blocked
When prompt=consent is used:
- Admin consent is granted -> OK
- Non-admin user signs in -> Blocked ("Need admin approval")
- Admin re-approves -> User is still blocked on next attempt
This creates a loop where the application is effectively unusable for non-admin users when prompt=consent is specified, regardless of admin consent status.
4. Loss of legitimate functionality: displaying scopes to users
There is a legitimate use case for prompt=consent: showing users the permission scopes the application will access, for transparency. This was previously possible even when user consent was restricted at the tenant level, because admin consent was honored.
Now, applications face an impossible choice:
- Use
prompt=consentto show scopes to users -> users are blocked - Use
prompt=select_accountto avoid blocking -> users never see what scopes the app accesses
There is no way to achieve both "display scopes to the user" and "respect admin consent" simultaneously. This is a regression in functionality.
5. Microsoft's own troubleshooting guidance is impractical
Microsoft's official troubleshooting documentation (Step 7: Verify if the prompt parameter is passed) suggests:
Sometimes, signing in to the application requires passing the
promptparameter ofconsentoradmin_consent. Once the application obtains consent, make sure thepromptparameter isn't specified.
This implies that applications should use prompt=consent for initial consent, then dynamically remove it afterward. However, this is not practically implementable:
- At the time the authorization URL is constructed (before any token is obtained), the application has no way to query whether admin consent has already been granted for the current tenant
- For multi-tenant applications, consent status varies per tenant, making tenant-by-tenant tracking unreliable (consent can be revoked at any time on the Entra side without notifying the application)
- There is no Microsoft Graph API or Entra endpoint that allows an application to check its own consent status before initiating the OAuth flow
The guidance effectively asks developers to solve an impossible problem: conditionally include prompt=consent based on information that is not available at the time the decision must be made. This further supports the argument that prompt=consent should simply work correctly when admin consent is already in place, rather than requiring applications to avoid it.
6. Admin consent TRUE alone works, but admin consent TRUE + user consent TRUE fails
This is the most clearly inconsistent aspect:
- When the user flow does not trigger a consent prompt (e.g.,
prompt=select_account), admin consent alone is sufficient -> Works - When the user flow does trigger a consent prompt (via
prompt=consent), the system requires the user to also have consent authority, even though admin consent is already granted -> Fails
In other words: admin_consent=granted is sufficient when the user is not asked to consent, but admin_consent=granted AND user_consent=required fails when the user is asked to consent. The addition of user consent into the flow invalidates the existing admin consent, which is logically inconsistent.
Impact
- This issue affects any multi-tenant application that uses
prompt=consentin its OAuth flow - Multiple independent reports have appeared on Microsoft Q&A since early 2026:
- Standard Users Cannot Access Microsoft Graph Mail.Read Despite Admin Consent (2026-03-03)
- Getting error for enterprise application - AADSTS90094 (2026-03-04)
- Users stuck in "Requires admin approval" even after admin approves - Outlook/Graph (2025-09-19)
- Normal users cannot complete OAuth consent for Microsoft Graph Outlook integration (2026-02-14)
- The current workaround (replacing
prompt=consentwithprompt=select_account) sacrifices transparency, as users can no longer see which scopes the application is requesting
Workaround
Replace prompt=consent with prompt=select_account in the authorization URL. This respects existing admin consent and allows non-admin users to authenticate. However, users will not see the permission scopes the application is accessing.
Request
Please fix the consent evaluation logic so that prompt=consent honors existing tenant-wide admin consent. When admin consent has been granted, prompt=consent should:
- Display the consent screen to the user (showing the scopes, for transparency)
- Allow the user to proceed, since the required permissions have already been approved by an administrator
This would be consistent with the OIDC specification's intent for the prompt=consent parameter and would restore the ability to combine scope transparency with admin consent.