Some of you may have been developing an application that integrates with Azure AD, and hit this screen:
In the tiny text at the bottom you can find this error code:
AADSTS90094: The grant requires admin permission.
Note that this article is based on Azure AD v1. For v2/converged apps, I recommend Marc LaFleur's article: http://massivescale.com/microsoft-v2-endpoint-admin-consent/.
Why this happens
TL;DR, your app requires a permission which requires an administrator to consent to it, but it has not been done and the currently signing in user is not an admin.
If you want to skip explanations, you can find guidance on how to fix it at the bottom of the article.
But it is also good to know the details on what Azure AD is expecting to exist so you can actually figure out why this error occurs.
So here we will go through how to find out manually if the permissions have been granted successfully.
Delegated permissions requiring admin consent
Let's say your application requires a delegated permission which requires an admin to consent, like Read all users' full profiles on the MS Graph API here:
Now when a user tries to authenticate, Azure AD is looking for an OAuth2PermissionGrant object on the service principal.
You can find this object by using the Azure AD Graph Explorer, and after logging in, enter a URL like this:
https://graph.windows.net/joonasapps.onmicrosoft.com/servicePrincipals/6cef654b-d2eb-4dd1-95c0-8032b29a963e/oauth2PermissionGrants
Obviously replace values to match your app and directory.
Replace joonasapps.onmicrosoft.com
with your Azure AD tenant's id, or any of its verified domains.
You can find the service principal id by finding your app registration in Azure Portal, then click the link that says Managed application in local directory above it. Then, go to Properties. There you can find the Object ID.
You can also find the service principal from the Enterprise applications tab.
Now in order for Azure AD to consider the permissions granted, this object must match the following requirements:
consentType
should be either AllPrincipals or Principal- If it is Principal, then
principalId
must equal the logging in user'sobjectId
- AllPrincipals means an administrator has granted consent and thus no user needs to be asked anymore
- If it is Principal, then
scope
should contain all of the delegated permissions required by the application
If this object is not found, consent will be asked from the user. And then if the user is not an admin, but the permission requires an admin to grant, you get the error.
A bit of a "funny" thing happens if the object is found, but the scopes do not match.
If you did not explicitly ask the user to consent with prompt=consent
, the user authenticates successfully.
However, the resulting access token will only contain the scopes that were previously consented.
Something to consider when designing your authentication flow.
If you do then redirect them to Azure AD again with prompt=consent
, you get the same consent check as before if the object was not found at all.
Application permissions
App permissions differ from delegated permissions in that they require an administrator to consent always.
They are essentially roles which can be applied to service principals. So if the role has not been granted, the permission is not granted.
Let's say our app now requires an app permission like Send mail as any user on the MS Graph API here:
This time, Azure AD is looking for an appRoleAssignment
on the service principal.
You can find your app's granted app permissions by using the Azure AD Graph Explorer, and after logging in, enter a URL like this:
https://graph.windows.net/joonasapps.onmicrosoft.com/servicePrincipals/6cef654b-d2eb-4dd1-95c0-8032b29a963e/appRoleAssignments
Like above, you must input your own tenant's and service principal's identifiers.
In order for Azure AD to consider the permission granted, the object must match these requirements:
principalId
matches the service principal'sobjectId
id
matches theid
of the app permission being requested- These are defined on the service principal identified by
resourceId
- These are defined on the service principal identified by
resourceId
matches the API's service principal'sobjectId
If this object is not found, and the current user is not admin, you get the error.
Note: if you add more app permissions after granting some, there will be no error, unless you explicitly ask for consent. Same as delegated permissions.
If you need to know which id
corresponds to what permission, you can get them once again with the Graph Explorer:
https://graph.windows.net/joonasapps.onmicrosoft.com/servicePrincipals/ea63ed19-7427-4282-b2a3-eb8f049eaed5
In my test directory that is the object id for Microsoft Graph API's service principal.
You can find all of the delegated permissions (oauth2Permissions) and app permissions (appRoles with allowedMemberTypes
including Application) there.
One small curiosity you can discover there is that MS Graph is also known as Microsoft.Azure.AgregatorService
.
The typo with "Agregator" aside, it reflects MS Graph's position as a gateway that aggregates other APIs.
I thought that was interesting :)
How to fix it
Now, how can we go about creating those required objects?
Firstly, each of these options requires the user to be an administrator.
One of the easiest ways in a single-tenant scenario is to click the Grant permissions button found here:
If you hit the error on authentication, there is also an option to log out and sign in as an administrator:
In the case where you added permissions later after consents were already granted,
you can force consent again by adding prompt=admin_consent
to the authorize URL.
Then an administrator can sign in and consent to the newly required permissions.
Why not prompt=consent
? Because then the admin would only grant required delegated permissions for themselves :) App permissions will be granted correctly with either prompt.
Example authorize URL:
https://login.microsoftonline.com/joonasapps.onmicrosoft.com/oauth2/authorize?response_type=code&client_id=0808d6a7-164c-41be-8930-8dad927e42ae&redirect_uri=https%3A%2F%2Flocalhost%3A44307%2F&prompt=admin_consent
If this goes successfully, you can use the methods described in the earlier section to confirm this has indeed happened with Azure AD Graph Explorer.
Multi-tenant scenarios
If this is the first time a user from another tenant is logging in, and your app requires permissions which need administor consent, the first user who signs in must be an admin.
You should have some kind of onboarding flow in which you include prompt=admin_consent
in the authorize URL as above. Though this time you would use common instead of a tenant id/domain:
https://login.microsoftonline.com/common/oauth2/authorize?response_type=code&client_id=0808d6a7-164c-41be-8930-8dad927e42ae&redirect_uri=https%3A%2F%2Flocalhost%3A44307%2F&prompt=admin_consent
If another tenant has previously consented to permissions, but you have now changed them, that is a little harder.
In this case you cannot just force consent for any user signing in. They will be unable to login at all to your app if they are not administrators.
What you need to do is:
- Check the access token you get from Azure AD. If it does not contain the scopes/roles expected, then new consent is needed
- Hopefully your app can still work without those new permissions :)
- When you ask for a token with the Authorization Code Grant flow, the response includes what scopes are in the token, e.g.
"scope": "Calendars.ReadWrite User.Read"
- Alternatively you can try calling the API with the access token. If it comes back with a 401/403, then we probably do not have the permissions granted yet.
- An email/notification might be needed that says something like An administrator from your organization needs to sign in through this link to enable the new features blah blah...
- The link would be the authorize URL containing
prompt=admin_consent
- The link would be the authorize URL containing