When you grant API permissions to a client app in Microsoft Entra ID, the permission grants are recorded as objects that can be accessed, updated, or deleted like your data. Using Microsoft Graph to directly create permission grants is a programmatic alternative to interactive consent and can be useful for automation scenarios, bulk management, or other custom operations in your organization.
In this guide, you'll learn how to grant and revoke app roles for an app using Microsoft Graph. App roles, also called application permissions, or direct access permissions, allow an app to call an API with its own identity.
Caution
Be careful! Permissions granted programmatically are not subject to review or confirmation. They take effect immediately.
Prerequisites
To complete these instructions, you need the following resources and privileges:
A working Microsoft Entra tenant.
You'll run the requests in this article as a user. You must complete the following steps:
Sign in to an app such as Graph Explorer or Postman as a user with privileges to create applications in the tenant.
In the app you've signed in to, consent to the Application.Read.All and AppRoleAssignment.ReadWrite.All delegated permissions on behalf of the signed-in user. You don't need to consent on behalf of your organization.
Get the object ID of the client service principal to which you'll grant app roles. In this article, the client service principal is identified by ID b0d9b9e3-0ecf-4bfd-8dab-9273dd055a94.
Caution
Apps that have been granted the AppRoleAssignment.ReadWrite.All permission should only be accessed by appropriate users.
Step 1: Get the appRoles of the resource service principal
Before you can grant app roles, you must first identify the app roles to grant and the resource service principal that exposes the app roles. App roles are defined in the appRoles object of a service principal. In this article, you'll use the Microsoft Graph service principal in the tenant as your resource service principal.
Request
The following request retrieves the app roles defined by the Microsoft Graph service principal in the tenant.
GET https://graph.microsoft.com/v1.0/servicePrincipals?$filter=displayName eq 'Microsoft Graph'&$select=id,displayName,appId,appRoles
// Code snippets are only available for the latest version. Current version is 5.x
// To initialize your graphClient, see https://learn.microsoft.com/en-us/graph/sdks/create-client?from=snippets&tabs=csharp
var result = await graphClient.ServicePrincipals.GetAsync((requestConfiguration) =>
{
requestConfiguration.QueryParameters.Filter = "displayName eq 'Microsoft Graph'";
requestConfiguration.QueryParameters.Select = new string []{ "id","displayName","appId","appRoles" };
});
// THE CLI IS IN PREVIEW. NON-PRODUCTION USE ONLY
mgc service-principals list --filter "displayName eq 'Microsoft Graph'" --select "id,displayName,appId,appRoles"
<?php
// THIS SNIPPET IS A PREVIEW VERSION OF THE SDK. NON-PRODUCTION USE ONLY
$graphServiceClient = new GraphServiceClient($tokenRequestContext, $scopes);
$requestConfiguration = new ServicePrincipalsRequestBuilderGetRequestConfiguration();
$queryParameters = ServicePrincipalsRequestBuilderGetRequestConfiguration::createQueryParameters();
$queryParameters->filter = "displayName eq 'Microsoft Graph'";
$queryParameters->select = ["id","displayName","appId","appRoles"];
$requestConfiguration->queryParameters = $queryParameters;
$result = $graphServiceClient->servicePrincipals()->get($requestConfiguration)->wait();
The following object is an example of the response.
Note: The response object shown here might be shortened for readability.
HTTP/1.1 201 Created
Content-type: application/json
{
"@odata.context": "https://graph.microsoft.com/v1.0/$metadata#servicePrincipals(id,displayName,appId,appRoles)",
"value": [
{
"id": "7ea9e944-71ce-443d-811c-71e8047b557a",
"displayName": "Microsoft Graph",
"appId": "00000003-0000-0000-c000-000000000000",
"appRoles": [
{
"allowedMemberTypes": [
"Application"
],
"description": "Allows the app to read user profiles without a signed in user.",
"displayName": "Read all users' full profiles",
"id": "df021288-bdef-4463-88db-98f22de89214",
"isEnabled": true,
"origin": "Application",
"value": "User.Read.All"
}
]
}
]
}
Step 2: Grant an app role to a client service principal
In this step, you'll grant your app an app role that's exposed by Microsoft Graph, thereby creating an app role assignment. From Step 1, the object ID of Microsoft Graph is 7ea9e944-71ce-443d-811c-71e8047b557a and the app role User.Read.All is identified by ID df021288-bdef-4463-88db-98f22de89214.
Request
The following request grants your client app (the principal of ID b0d9b9e3-0ecf-4bfd-8dab-9273dd055a94) an app role of ID df021288-bdef-4463-88db-98f22de89214 that's exposed by a resource service principal of ID 7ea9e944-71ce-443d-811c-71e8047b557a.
// Code snippets are only available for the latest version. Current version is 5.x
// Dependencies
using Microsoft.Graph.Models;
var requestBody = new AppRoleAssignment
{
PrincipalId = Guid.Parse("b0d9b9e3-0ecf-4bfd-8dab-9273dd055a94"),
ResourceId = Guid.Parse("7ea9e944-71ce-443d-811c-71e8047b557a"),
AppRoleId = Guid.Parse("df021288-bdef-4463-88db-98f22de89214"),
};
// To initialize your graphClient, see https://learn.microsoft.com/en-us/graph/sdks/create-client?from=snippets&tabs=csharp
var result = await graphClient.ServicePrincipals["{servicePrincipal-id}"].AppRoleAssignedTo.PostAsync(requestBody);
// THE CLI IS IN PREVIEW. NON-PRODUCTION USE ONLY
mgc service-principals app-role-assigned-to create --service-principal-id {servicePrincipal-id} --body '{\
"principalId": "b0d9b9e3-0ecf-4bfd-8dab-9273dd055a94",\
"resourceId": "7ea9e944-71ce-443d-811c-71e8047b557a",\
"appRoleId": "df021288-bdef-4463-88db-98f22de89214"\
}\
'
<?php
// THIS SNIPPET IS A PREVIEW VERSION OF THE SDK. NON-PRODUCTION USE ONLY
$graphServiceClient = new GraphServiceClient($tokenRequestContext, $scopes);
$requestBody = new AppRoleAssignment();
$requestBody->setPrincipalId('b0d9b9e3-0ecf-4bfd-8dab-9273dd055a94');
$requestBody->setResourceId('7ea9e944-71ce-443d-811c-71e8047b557a');
$requestBody->setAppRoleId('df021288-bdef-4463-88db-98f22de89214');
$result = $graphServiceClient->servicePrincipals()->byServicePrincipalId('servicePrincipal-id')->appRoleAssignedTo()->post($requestBody)->wait();
# THE PYTHON SDK IS IN PREVIEW. FOR NON-PRODUCTION USE ONLY
graph_client = GraphServiceClient(credentials, scopes)
request_body = AppRoleAssignment(
principal_id = UUID("b0d9b9e3-0ecf-4bfd-8dab-9273dd055a94"),
resource_id = UUID("7ea9e944-71ce-443d-811c-71e8047b557a"),
app_role_id = UUID("df021288-bdef-4463-88db-98f22de89214"),
)
result = await graph_client.service_principals.by_service_principal_id('servicePrincipal-id').app_role_assigned_to.post(request_body)
GET https://graph.microsoft.com/v1.0/servicePrincipals/7ea9e944-71ce-443d-811c-71e8047b557a/appRoleAssignedTo
// Code snippets are only available for the latest version. Current version is 5.x
// To initialize your graphClient, see https://learn.microsoft.com/en-us/graph/sdks/create-client?from=snippets&tabs=csharp
var result = await graphClient.ServicePrincipals["{servicePrincipal-id}"].AppRoleAssignedTo.GetAsync();
<?php
// THIS SNIPPET IS A PREVIEW VERSION OF THE SDK. NON-PRODUCTION USE ONLY
$graphServiceClient = new GraphServiceClient($tokenRequestContext, $scopes);
$result = $graphServiceClient->servicePrincipals()->byServicePrincipalId('servicePrincipal-id')->appRoleAssignedTo()->get()->wait();
# THE PYTHON SDK IS IN PREVIEW. FOR NON-PRODUCTION USE ONLY
graph_client = GraphServiceClient(credentials, scopes)
result = await graph_client.service_principals.by_service_principal_id('servicePrincipal-id').app_role_assigned_to.get()
The response object includes a collection of app role assignments to your resource service principal and includes the app role assignment you created using the POST request.
// Code snippets are only available for the latest version. Current version is 5.x
// To initialize your graphClient, see https://learn.microsoft.com/en-us/graph/sdks/create-client?from=snippets&tabs=csharp
await graphClient.ServicePrincipals["{servicePrincipal-id}"].AppRoleAssignedTo["{appRoleAssignment-id}"].DeleteAsync();
// THE CLI IS IN PREVIEW. NON-PRODUCTION USE ONLY
mgc service-principals app-role-assigned-to delete --service-principal-id {servicePrincipal-id} --app-role-assignment-id {appRoleAssignment-id}
<?php
// THIS SNIPPET IS A PREVIEW VERSION OF THE SDK. NON-PRODUCTION USE ONLY
$graphServiceClient = new GraphServiceClient($tokenRequestContext, $scopes);
$graphServiceClient->servicePrincipals()->byServicePrincipalId('servicePrincipal-id')->appRoleAssignedTo()->byAppRoleAssignmentId('appRoleAssignment-id')->delete()->wait();
# THE PYTHON SDK IS IN PREVIEW. FOR NON-PRODUCTION USE ONLY
graph_client = GraphServiceClient(credentials, scopes)
await graph_client.service_principals.by_service_principal_id('servicePrincipal-id').app_role_assigned_to.by_app_role_assignment_id('appRoleAssignment-id').delete()
You've learned how to manage app role grants for a service principal. This method of granting permissions using Microsoft Graph is an alternative interactive consent and should be used with caution.
In this guide, you'll learn how to grant and revoke delegated permissions for an app using Microsoft Graph. Delegated permissions, also called scopes or OAuth2 permissions, allow an app to call an API on behalf of a signed-in user.
Caution
Be careful! Permissions granted programmatically are not subject to review or confirmation. They take effect immediately.
Prerequisites
To complete these instructions, you need the following resources and privileges:
A working Microsoft Entra tenant.
You'll run the requests in this article as a user. You must complete the following steps:
Sign in to an app such as Graph Explorer or Postman as a user with privileges to create applications in the tenant.
In the app you've signed in to, consent to the Application.Read.All, DelegatedPermissionGrant.ReadWrite.All delegated permissions on behalf of the signed-in user. You don't need to consent on behalf of your organization.
Get the object ID of the client service principal to which you'll grant delegated permissions on behalf of a user. In this article, the client service principal is identified by ID b0d9b9e3-0ecf-4bfd-8dab-9273dd055a94.
Caution
Apps that have been granted the DelegatedPermissionGrant.ReadWrite.All permission should only be accessed by appropriate users.
Step 1: Get the delegated permissions of the resource service principal
Before you can grant delegated permissions, you must first identify the delegated permissions to grant and the resource service principal that exposes the delegated permissions. Delegated permissions are defined in the oauth2PermissionScopes object of a service principal. In this article, you'll use the Microsoft Graph service principal in the tenant as your resource service principal.
Request
The following request retrieves the delegated permissions defined by the Microsoft Graph service principal in the tenant.
GET https://graph.microsoft.com/v1.0/servicePrincipals?$filter=displayName eq 'Microsoft Graph'&$select=id,displayName,appId,oauth2PermissionScopes
// Code snippets are only available for the latest version. Current version is 5.x
// To initialize your graphClient, see https://learn.microsoft.com/en-us/graph/sdks/create-client?from=snippets&tabs=csharp
var result = await graphClient.ServicePrincipals.GetAsync((requestConfiguration) =>
{
requestConfiguration.QueryParameters.Filter = "displayName eq 'Microsoft Graph'";
requestConfiguration.QueryParameters.Select = new string []{ "id","displayName","appId","oauth2PermissionScopes" };
});
// THE CLI IS IN PREVIEW. NON-PRODUCTION USE ONLY
mgc service-principals list --filter "displayName eq 'Microsoft Graph'" --select "id,displayName,appId,oauth2PermissionScopes"
<?php
// THIS SNIPPET IS A PREVIEW VERSION OF THE SDK. NON-PRODUCTION USE ONLY
$graphServiceClient = new GraphServiceClient($tokenRequestContext, $scopes);
$requestConfiguration = new ServicePrincipalsRequestBuilderGetRequestConfiguration();
$queryParameters = ServicePrincipalsRequestBuilderGetRequestConfiguration::createQueryParameters();
$queryParameters->filter = "displayName eq 'Microsoft Graph'";
$queryParameters->select = ["id","displayName","appId","oauth2PermissionScopes"];
$requestConfiguration->queryParameters = $queryParameters;
$result = $graphServiceClient->servicePrincipals()->get($requestConfiguration)->wait();
The following object is an example of the response.
Note: The response object shown here might be shortened for readability.
HTTP/1.1 201 Created
Content-type: application/json
{
"@odata.context": "https://graph.microsoft.com/v1.0/$metadata#servicePrincipals(id,displayName,appId,oauth2PermissionScopes)",
"value": [
{
"id": "7ea9e944-71ce-443d-811c-71e8047b557a",
"displayName": "Microsoft Graph",
"appId": "00000003-0000-0000-c000-000000000000",
"oauth2PermissionScopes": [
{
"adminConsentDescription": "Allows the app to read the full set of profile properties, reports, and managers of other users in your organization, on behalf of the signed-in user.",
"adminConsentDisplayName": "Read all users' full profiles",
"id": "a154be20-db9c-4678-8ab7-66f6cc099a59",
"isEnabled": true,
"type": "Admin",
"userConsentDescription": "Allows the app to read the full set of profile properties, reports, and managers of other users in your organization, on your behalf.",
"userConsentDisplayName": "Read all users' full profiles",
"value": "User.Read.All"
},
{
"adminConsentDescription": "Allows the app to list groups, and to read their properties and all group memberships on behalf of the signed-in user. Also allows the app to read calendar, conversations, files, and other group content for all groups the signed-in user can access. ",
"adminConsentDisplayName": "Read all groups",
"id": "5f8c59db-677d-491f-a6b8-5f174b11ec1d",
"isEnabled": true,
"type": "Admin",
"userConsentDescription": "Allows the app to list groups, and to read their properties and all group memberships on your behalf. Also allows the app to read calendar, conversations, files, and other group content for all groups you can access. ",
"userConsentDisplayName": "Read all groups",
"value": "Group.Read.All"
}
]
}
]
}
Step 2: Grant a delegated permission to the client service principal on behalf of a user
Request
In this step, you'll grant your app, on behalf of a user, a delegated permission that's exposed by Microsoft Graph, thereby creating an delegated permission grant.
From Step 1, the object ID of Microsoft Graph in the tenant is 7ea9e944-71ce-443d-811c-71e8047b557a
The delegated permissions User.Read.All and Group.Read.All are identified by the globally unique IDs a154be20-db9c-4678-8ab7-66f6cc099a59 and 5f8c59db-677d-491f-a6b8-5f174b11ec1d respectively.
The principal is a user identified by ID 3fbd929d-8c56-4462-851e-0eb9a7b3a2a5.
The client service principal is identified by ID b0d9b9e3-0ecf-4bfd-8dab-9273dd055a94. This is the object id of the service principal and not its appId.
// Code snippets are only available for the latest version. Current version is 5.x
// Dependencies
using Microsoft.Graph.Models;
var requestBody = new OAuth2PermissionGrant
{
ClientId = "b0d9b9e3-0ecf-4bfd-8dab-9273dd055a94",
ConsentType = "Principal",
ResourceId = "7ea9e944-71ce-443d-811c-71e8047b557a",
PrincipalId = "3fbd929d-8c56-4462-851e-0eb9a7b3a2a5",
Scope = "User.Read.All Group.Read.All",
};
// To initialize your graphClient, see https://learn.microsoft.com/en-us/graph/sdks/create-client?from=snippets&tabs=csharp
var result = await graphClient.Oauth2PermissionGrants.PostAsync(requestBody);
<?php
// THIS SNIPPET IS A PREVIEW VERSION OF THE SDK. NON-PRODUCTION USE ONLY
$graphServiceClient = new GraphServiceClient($tokenRequestContext, $scopes);
$requestBody = new OAuth2PermissionGrant();
$requestBody->setClientId('b0d9b9e3-0ecf-4bfd-8dab-9273dd055a94');
$requestBody->setConsentType('Principal');
$requestBody->setResourceId('7ea9e944-71ce-443d-811c-71e8047b557a');
$requestBody->setPrincipalId('3fbd929d-8c56-4462-851e-0eb9a7b3a2a5');
$requestBody->setScope('User.Read.All Group.Read.All');
$result = $graphServiceClient->oauth2PermissionGrants()->post($requestBody)->wait();
While the preceding request grants consent on behalf of a single user, you can choose to grant consent on behalf of all users in the tenant. The request body is similar to the previous request body except with the following changes:
The consentType is AllPrincipals, indicating that you're consenting on behalf of all users in the tenant.
The principalId property isn't supplied or can be null.
An example request body for granting consent on behalf all users is as follows:
GET https://graph.microsoft.com/v1.0/oauth2PermissionGrants?$filter=clientId eq 'b0d9b9e3-0ecf-4bfd-8dab-9273dd055a94' and principalId eq '3fbd929d-8c56-4462-851e-0eb9a7b3a2a5' and consentType eq 'Principal'
// Code snippets are only available for the latest version. Current version is 5.x
// To initialize your graphClient, see https://learn.microsoft.com/en-us/graph/sdks/create-client?from=snippets&tabs=csharp
var result = await graphClient.Oauth2PermissionGrants.GetAsync((requestConfiguration) =>
{
requestConfiguration.QueryParameters.Filter = "clientId eq 'b0d9b9e3-0ecf-4bfd-8dab-9273dd055a94' and principalId eq '3fbd929d-8c56-4462-851e-0eb9a7b3a2a5' and consentType eq 'Principal'";
});
// THE CLI IS IN PREVIEW. NON-PRODUCTION USE ONLY
mgc oauth2-permission-grants list --filter "clientId eq 'b0d9b9e3-0ecf-4bfd-8dab-9273dd055a94' and principalId eq '3fbd929d-8c56-4462-851e-0eb9a7b3a2a5' and consentType eq 'Principal'"
<?php
// THIS SNIPPET IS A PREVIEW VERSION OF THE SDK. NON-PRODUCTION USE ONLY
$graphServiceClient = new GraphServiceClient($tokenRequestContext, $scopes);
$requestConfiguration = new Oauth2PermissionGrantsRequestBuilderGetRequestConfiguration();
$queryParameters = Oauth2PermissionGrantsRequestBuilderGetRequestConfiguration::createQueryParameters();
$queryParameters->filter = "clientId eq 'b0d9b9e3-0ecf-4bfd-8dab-9273dd055a94' and principalId eq '3fbd929d-8c56-4462-851e-0eb9a7b3a2a5' and consentType eq 'Principal'";
$requestConfiguration->queryParameters = $queryParameters;
$result = $graphServiceClient->oauth2PermissionGrants()->get($requestConfiguration)->wait();
# THE PYTHON SDK IS IN PREVIEW. FOR NON-PRODUCTION USE ONLY
graph_client = GraphServiceClient(credentials, scopes)
query_params = Oauth2PermissionGrantsRequestBuilder.Oauth2PermissionGrantsRequestBuilderGetQueryParameters(
filter = "clientId eq 'b0d9b9e3-0ecf-4bfd-8dab-9273dd055a94' and principalId eq '3fbd929d-8c56-4462-851e-0eb9a7b3a2a5' and consentType eq 'Principal'",
)
request_configuration = Oauth2PermissionGrantsRequestBuilder.Oauth2PermissionGrantsRequestBuilderGetRequestConfiguration(
query_parameters = query_params,
)
result = await graph_client.oauth2_permission_grants.get(request_configuration = request_configuration)
Step 3: Revoke delegated permissions granted to a service principal on behalf of a user [Optional]
If a service principal has been granted multiple delegated permission grants on behalf of a user, you can choose to revoke either specific grants or all grants. Use this method to remove and revoke consent for the delegated permissions that you've assigned to Graph Explorer.
To revoke one or some grants, run a PATCH request on the oauth2PermissionGrant object and specify only the delegated permissions to retain in the scope parameter.
To revoke all grants, run a DELETE request on the oauth2PermissionGrant object.
Request
The following request revokes all permission grants and retains only the User.Read.All permission grant. The permissions are removed and the consent that was previously granted is revoked.
// Code snippets are only available for the latest version. Current version is 5.x
// Dependencies
using Microsoft.Graph.Models;
var requestBody = new OAuth2PermissionGrant
{
Scope = "User.Read.All",
};
// To initialize your graphClient, see https://learn.microsoft.com/en-us/graph/sdks/create-client?from=snippets&tabs=csharp
var result = await graphClient.Oauth2PermissionGrants["{oAuth2PermissionGrant-id}"].PatchAsync(requestBody);
// THE CLI IS IN PREVIEW. NON-PRODUCTION USE ONLY
mgc oauth2-permission-grants patch --o-auth2-permission-grant-id {oAuth2PermissionGrant-id} --body '{\
"scope": "User.Read.All"\
}\
'
<?php
// THIS SNIPPET IS A PREVIEW VERSION OF THE SDK. NON-PRODUCTION USE ONLY
$graphServiceClient = new GraphServiceClient($tokenRequestContext, $scopes);
$requestBody = new OAuth2PermissionGrant();
$requestBody->setScope('User.Read.All');
$result = $graphServiceClient->oauth2PermissionGrants()->byOAuth2PermissionGrantId('oAuth2PermissionGrant-id')->patch($requestBody)->wait();
# THE PYTHON SDK IS IN PREVIEW. FOR NON-PRODUCTION USE ONLY
graph_client = GraphServiceClient(credentials, scopes)
request_body = OAuth2PermissionGrant(
scope = "User.Read.All",
)
result = await graph_client.oauth2_permission_grants.by_o_auth2_permission_grant_id('oAuth2PermissionGrant-id').patch(request_body)
// Code snippets are only available for the latest version. Current version is 5.x
// To initialize your graphClient, see https://learn.microsoft.com/en-us/graph/sdks/create-client?from=snippets&tabs=csharp
await graphClient.Oauth2PermissionGrants["{oAuth2PermissionGrant-id}"].DeleteAsync();
<?php
// THIS SNIPPET IS A PREVIEW VERSION OF THE SDK. NON-PRODUCTION USE ONLY
$graphServiceClient = new GraphServiceClient($tokenRequestContext, $scopes);
$graphServiceClient->oauth2PermissionGrants()->byOAuth2PermissionGrantId('oAuth2PermissionGrant-id')->delete()->wait();
# THE PYTHON SDK IS IN PREVIEW. FOR NON-PRODUCTION USE ONLY
graph_client = GraphServiceClient(credentials, scopes)
await graph_client.oauth2_permission_grants.by_o_auth2_permission_grant_id('oAuth2PermissionGrant-id').delete()
You've granted delegated permissions (or scopes) to a service principal. This method of granting permissions using Microsoft Graph is an alternative to interactive consent and should be used with caution.