Share via

Bug Report: prompt=consent blocks non-admin users even when tenant-wide admin consent has been granted

Daisuke 0 Reputation points
2026-04-02T01:40:41.56+00:00

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

  1. Register a multi-tenant application in Microsoft Entra ID
  2. Configure delegated permissions: Mail.ReadWrite, Mail.Send, User.Read, offline_access
  3. 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)
  4. 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.

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.

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=consent to show scopes to users -> users are blocked
  • Use prompt=select_account to 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 prompt parameter of consent or admin_consent. Once the application obtains consent, make sure the prompt parameter 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.

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

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:

  1. Display the consent screen to the user (showing the scopes, for transparency)
  2. 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.

Microsoft Security | Microsoft Entra | Microsoft Entra ID

Your answer

Answers can be marked as 'Accepted' by the question author and 'Recommended' by moderators, which helps users know the answer solved the author's problem.