How to use Application Permission with Azure AD v2 endpoint and Microsoft Graph


The following scenario of OAuth flow is sometimes needed for the real applications, but this scenario is not supported in the first release of Azure AD v2.0 endpoint. (For v1 endpoint, it's supported.)

In this blog post, I introduce the recently supported new flow using application permissions with v2.0 endpoint, and explain how it works and how to use with new v2.0 endpoint and Microsoft Graph.

When to use this permission ?

As l wrote in the previous post "How to build backend server-side app (Deamon, etc) using Azure AD v1 endpoint" (sorry, which is written in Japanese), the client credential authentication using application permissions is very strong and powerful OAuth flow for applications.

Imagine that you want to synchronize the organization users in your app with Azure AD users periodically. This sync app would work with no login UI (as daemon or services) and access to the all Azure AD users (read/write).
Using the usual OAuth flow (code grant flow, etc), this is impossible.

The following flow uses the organization-level access privilege using the certificate or password (app secret) without login UI, and can access to the all users, all messages, all calendars, all files, all contacts, all registered devices, etc in the granted organization.
This flow exactly helps the previous scenarios.

Note : The usual user authentication using v2.0 endpoint and Microsoft Graph (which is the commonly used OAuth 2.0 code grant flow) is described in the previous post "OAuth flow by v2.0 endpoint using unified Microsoft identity (Azure AD Account and Microsoft Account)". (Sorry, it's also written in Japanese...)

Application Registration

Before using this flow, you must register your application.

You login to https://apps.dev.microsoft.com (the app registration portal), and please add your app pushing "add your app" button.
After the application is created, you can get the application id in the application page. (see the following screenshot)

In "Microsoft Graph Permissions" section on the application page, you can see the following "Application Permissions" area.
In this area, please push "add" button and select "Files.Read.All".

Note : In the usual v2.0 endpoint flow, you can use the scope (permission) values on the fly. (You don't need to pre-define this permission values.) But, as I explain later, this flow uses the pre-defined scope attached on your application.

In "Application Secrets" section, you can create the application password (application secret) or asymmetric certificate (key pair). This time, press "Generate New Password" button and create the application secret (password).

Lastly, in "Platforms" section, please press "Add Platform", add the Web platform, and set your application's url as redirect uri. (See the following screenshot)
In this example, we use "https://localhost/testapp02" as redirect uri.

Save all settings.

Admin Consent

We assume that this application (service) is multitenant application and share this service for user's organization. How to deliver this application for each organization (tenant) ?

In this case, you can use the delivery mechanism called "consent".
For example, if you use the Facebook integrated application, the application informs you operations ("read your friend list", "create posts", etc) the application is needing. If the user approve, the user can use this application. No extra setup is needed.
The application using Microsoft identity is having the same mechanism, and this is called "user consent" which grants the application to access your own data (your messages, your files, etc).

But as I wrote above, this application uses the all organization's data, not for the specific user. In such a case, the "administrator consent" (admin consent) is used in Azure AD, and this consent must be done by the administrator in the organization.

Note : The admin consent is not only for the application permission, but also used to grant delegated permissions (user permissions) to all users in your organization.

When you use the administrator consent, all you have to do is to go to https://login.microsoftonline.com/{tenant name}/adminconsent?client_id={application id}&state={some state data}&redirect_uri={redirect uri} using web browser.
In this example, we go to the following url. (We assume the user tenant is testdirectory.onmicrosoft.com.)

https://login.microsoftonline.com/testdirectory.onmicrosoft.com/adminconsent?client_id=6abf3364-0a60-4603-8276-e9abb0d843d6&state=12345&redirect_uri=https%3a%2f%2flocalhost%2ftestapp02

Note : The state parameter is returned as the same value after login. Your application can use this data (state), if you want to keep some state after login.

If you access to the above url, the login screen (the following screenshot) is shown. Before using this application, the tenant administrator must login and permit to use this application only once, and the login is needed no longer.
When you are using v2.0 endpoint, you can use both Azure AD Account (organizational account) and Microsoft Account (personal account). But, in this case, this application needs the organization-level permission, then you must login using Azure AD tenant administrator's account here. (If you use the other kind of account, the error occurs.)

After you login using the administrator account of Azure AD, the following consent screen is displayed. This screen says that if you grant to this application, this app takes the organization-level access privilege.

If you agree this consent, this application is granted to access your organization's data. After that, the web page is redirected to {redirect uri}?admin_consent=True&tenant={tenant name}&state={state data}. In this example, this is the following url.

https://localhost/testapp02?admin_consent=True&tenant=testdirectory.onmicrosoft.com&state=12345

Note : If the error occurs, the page is redirected to the following uri.
http://localhost/testapp02?error=permission_denied&error_description=The+admin+canceled+the+request

If you don't need this application anymore, you can revoke this application in Azure Portal. (Soon the new Azure Portal (Ibiza) will support this feature.)

Note : If you change your application permission in this application, the users (tenant administrators) must consent to this application again. (The user can consent many times.)

Get Access Token

Now it's ready ! Your application can get the access token including permissions (scope or roles) and call some proper operations by the given permissions.

Your application can get access token using the following HTTP request (OAuth).
Note that you cannot use https://login.microsoftonline.com/common/oauth2/v2.0/token (which is commonly used) for getting the token. Instead, you must use https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token, which identifies the specific tenant. (In this example, we use "testdirectory.onmicrosoft.com" as user's organization.)

The scope must be "https://graph.microsoft.com/.default".
In the usual v2.0 endpoint authentication flow, you can use the scope (permission) values on the fly like https://graph.microsoft.com/mail.read. But, this scope (.default) means that the application uses the pre-defined scope (which is "Files.Read.All" in this example).

POST https://login.microsoftonline.com/testdirectory.onmicrosoft.com/oauth2/v2.0/token
Content-Type: application/x-www-form-urlencoded

grant_type=client_credentials&client_id=6abf3364-0a60-4603-8276-e9abb0d843d6&client_secret=JfgrNM9CcW...&scope=https%3A%2F%2Fgraph.microsoft.com%2F.default

Your application can get the following HTTP response. The access token is used for calling APIs (services).

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8

{
  "token_type": "Bearer",
  "expires_in": 3599,
  "ext_expires_in": 0,
  "access_token": "eyJ0eXAiOi..."
}

Call Services (Microsoft Graph)

Lastly your application calls the service of Microsoft Graph using the provided access token.
In this example, the "Files.Read.All" permission is used for your application, then your application can read the all user's files using Microsoft Graph in the given organization (testdirectory.onmicrosoft.com).
The following is retrieving all files in the OneDrive root folder for the user "demouser01@testdirectory.onmicrosoft.com". The result is returning the two folder items.
Your application can get the files of arbitary users in the organization in the same way.

HTTP Request

GET https://graph.microsoft.com/v1.0/users/demouser01@testdirectory.onmicrosoft.com/drive/root/children
Accept: application/json
Authorization: Bearer eyJ0eXAiOi...

HTTP Response

HTTP/1.1 200 OK
Content-Type: application/json;odata.metadata=minimal;odata.streaming=true;IEEE754Compatible=false;charset=utf-8

{
  "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#users('demouser01%40testdirectory.onmicrosoft.com')/drive/root/children",
  "value": [
    {
      "createdBy": {
        "user": {
          "id": "cf258756-2623-47cb-be46-c85d436265bb",
          "displayName": "Demo Taro"
        }
      },
      "createdDateTime": "2015-12-22T07:24:46Z",
      "eTag": "\"{4C06E4DE-B89C-4A78-848A-FCC6118041F6},1\"",
      "id": "01B6UXIPO64QDEZHFYPBFIJCX4YYIYAQPW",
      "lastModifiedBy": {
        "user": {
          "id": "cf258756-2623-47cb-be46-c85d436265bb",
          "displayName": "Demo Taro"
        }
      },
      "lastModifiedDateTime": "2016-05-23T11:45:24Z",
      "name": "testfol",
      "webUrl": "https://o365directory-my.sharepoint.com/personal/demouser01_o365directory_onmicrosoft_com/Documents/testfol",
      "cTag": "\"c:{4C06E4DE-B89C-4A78-848A-FCC6118041F6},0\"",
      "folder": {
        "childCount": 2
      },
      "parentReference": {
        "driveId": "b!iXgvhy0l90q10oljtqrTKLCkDzZ204FFhgSqDnNXhktfZNOb9jHxQrzimH24Z67t",
        "id": "01B6UXIPN6Y2GOVW7725BZO354PWSELRRZ",
        "path": "/drive/root:"
      },
      "size": 0
    },
    {
      "createdBy": {
        "user": {
          "id": "cf258756-2623-47cb-be46-c85d436265bb",
          "displayName": "Demo Taro"
        }
      },
      "createdDateTime": "2016-05-20T12:57:32Z",
      "eTag": "\"{2135DDF2-5458-49E6-8C8E-2AB08501A7E4},1\"",
      "id": "01B6UXIPPS3U2SCWCU4ZEYZDRKWCCQDJ7E",
      "lastModifiedBy": {
        "user": {
          "id": "cf258756-2623-47cb-be46-c85d436265bb",
          "displayName": "Demo Taro"
        }
      },
      "lastModifiedDateTime": "2016-05-20T13:04:11Z",
      "name": "expense",
      "webUrl": "https://o365directory-my.sharepoint.com/personal/demouser01_o365directory_onmicrosoft_com/Documents/expense",
      "cTag": "\"c:{2135DDF2-5458-49E6-8C8E-2AB08501A7E4},0\"",
      "folder": {
        "childCount": 2
      },
      "parentReference": {
        "driveId": "b!iXgvhy0l90q10oljtqrTKLCkDzZ204FFhgSqDnNXhktfZNOb9jHxQrzimH24Z67t",
        "id": "01B6UXIPN6Y2GOVW7725BZO354PWSELRRZ",
        "path": "/drive/root:"
      },
      "size": 0
    }
  ]
}

Using Microsoft Authentication Library (MSAL)

Microsoft Authentication Library (MSAL) is the library that helps you to develop applications that work with v2.0 endpoint. Now this also supports this scenarios.

The following is the C# MSAL sample code, which gets the access token for this application permission's scenario.
You must install the NuGet package "Microsoft.Identity.Client" (Microsoft Authentication Library, MSAL) in your project.

using Microsoft.Identity.Client;

static void Main(string[] args)
{
  // get access token including application permissions
  ConfidentialClientApplication cl = new ConfidentialClientApplication(
    "https://login.microsoftonline.com/testdirectory.onmicrosoft.com/v2.0",
    "6abf3364-0a60-4603-8276-e9abb0d843d6",
    "https://localhost/testapp02",
    new ClientCredential("JfgrNM9CcW..."),
    new TokenCache());
  AuthenticationResult authResult = cl.AcquireTokenForClient(
    new string[] { "https://graph.microsoft.com/.default" },
    null).Result;

  Console.WriteLine(authResult.Token);
  Console.ReadLine();
}
Comments (28)

  1. SS says:

    Hi , I am getting
    {StatusCode: 403, ReasonPhrase: 'Forbidden', Version: 1.1, Content: System.Net.Http.StreamContent, Headers:
    {
    Transfer-Encoding: chunked
    request-id: 83d0d501-2290-44cc-aa84-cb49975a4404
    client-request-id: 83d0d501-2290-44cc-aa84-cb49975a4404
    x-ms-ags-diagnostic: {"ServerInfo":{"DataCenter":"East US","Slice":"SliceA","ScaleUnit":"003","Host":"SDFS","ADSiteName":"CST"}}
    Duration: 61.6349
    Cache-Control: private
    Date: Thu, 13 Oct 2016 22:38:57 GMT
    Server: Microsoft-IIS/8.5
    X-Powered-By: ASP.NET
    Content-Type: application/json
    }}

  2. SS says:

    I have me sure that all the permission are given correctly

    1. Hi, If you have already consented (agreed with the permission request UI), please revoke this permission and approve again (by admin user).
      If you still cannot solve this, could you please attach your HTTP request raw ? (including authorization token)

  3. The app works on selecting entry and exit point is programmed

  4. Application content fingers heads do I

  5. Applications do not relieve programs road intersection and earn a pattern you carry responsibility for the delay of the signature.

  6. dejan says:

    Hi
    I'm using MSAL PublicClientApplication to connect to outlook.com.

    Works great, very easy to use, but in "confirming permissions" screen, in addition to "read contacts" permission I defined in "scopes" ({https://outlook.office.com/contacts.read"}) and in the app registration, there are four others permissions including "[APP] will be able to see and update your info, even when you're not using this app.". Am I missing something here? How can I remove these?
    Regards
    Dejan

    1. Are you using delegated permissions on the fly ? (i.e, are you setting "https://outlook.office.com/contacts.read" as "scope" value for the login ?)
      In this case, you don't need to select any permissions in the app registration portal.
      I'm sorry it's written by Japanese, but I'm writing this usual case (using the delegated permissions on the fly) in the following post.
      https://blogs.msdn.microsoft.com/tsmatsuz/2016/03/08/azure-ad-msa-v2-endpoint-validate-id_token/
      In this post, the following url is used for user's login (the confirming permission's screen is displayed), and you don't need to select any permissions in the app registration portal beforehand.

      https://login.microsoftonline.com/common/oauth2/v2.0/authorize?response_type=id_token+code&response_mode=form_post&client_id=7822587c-fed4-4dd3-8e68-165334eb7c92&scope=openid+https%3a%2f%2fgraph.microsoft.com%2fmail.read&redirect_uri=https%3A%2F%2Flocalhost%2Ftest.php&nonce=abcdef

      1. dejann says:

        Hi,
        Thanks for your reply.
        Not sure if I explained well. I'm using MSAL public client from a UWP application. In the user authentication screen there are 4 additional permissions user has to agree with (in addition to the permission I defined in scope). So this is in the user authentication screen, not in the application configuration portal. One of these permissions sound scary, telling user that the app can change their data even when they are not using the app! I'm sure many users will be put off by this and may think the app is some sort of malware. Is there any way to change this?

        1. Can you please tell the following 2 questions ?
          1 ) Did you clear all "Microsoft Graph Permissions" in the app settings in app reg portal (https://apps.dev.microsoft.com) ?
          2 ) What kind of scopes did you set in MSAL code ?

  7. dejan says:

    1) Originally I had MS Graph delegated permissions in the app portal match what's in my application code (scopes). I have now removed it. No change.
    2) In the app, scopes are defined as: {"https://outlook.office.com/contacts.readwrite"}

    1. Thank you for your reply again. I checked using my sample app, and I also found "access your data any time" when I've add "offline_access" in the scopes. (If I erase this offline_access, this permission message has disappeared.)
      If you set offline_access, not only access token but the refresh token is also returned. Using this refresh token, the app can always get access token inside the app, and can proceed some granted operations. This means "access your data any time".
      For example, if the app is mobile app, this refresh token enables some operations without logging in again. Most mobile apps with OAuth 2 are using this refresh token, because the users must input their user name and password again and again. But, if your app is web app, you may erase this offline_access and prompt to the user to login again when the access token is expired. (The access token lifetime is 1 hour by default.)

      1. dejan says:

        Hi,
        Thanks for that! How do I erase offline_access permission? I don't have that in scopes.

        1. Are you using some sort of library like MSAL ? If so, the libarary would add the "offline_access" automatically because of enabling cache.
          As you know, you can see the http raw using Fiddler and you can check what kind of scopes is added for the http request. If the issue exists, could you please tell me your http raw request ? I will try the same request.

          1. dejan says:

            Yes, using MSAL. I guess there is no way to switch it off with msal. Thanks for all the help!

  8. Lei Xu says:

    Hi,

    After getting the access token I was trying to get the following resource (https://graph.microsoft.com/v1.0/me/message) which resulted in the following error:

    HTTP/1.1 400 Bad Request
    Cache-Control: private
    Transfer-Encoding: chunked
    Content-Type: application/json
    Server: Microsoft-IIS/8.5
    request-id: 95878061-9c50-4d67-934f-c50a4b98edc6
    client-request-id: 95878061-9c50-4d67-934f-c50a4b98edc6
    x-ms-ags-diagnostic: {"ServerInfo":{"DataCenter":"Japan East","Slice":"SliceB","ScaleUnit":"000","Host":"AGSFE_IN_2","ADSiteName":"KAW"}}
    Duration: 3.5795
    X-Powered-By: ASP.NET
    Date: Wed, 25 Jan 2017 06:01:48 GMT

    {
    "error": {
    "code": "BadRequest",
    "message": "Current authenticated context is not valid for this request",
    "innerError": {
    "request-id": "95878061-9c50-4d67-934f-c50a4b98edc6",
    "date": "2017-01-25T06:01:49"
    }
    }
    }

    However if I change the resource uri to (https://graph.microsoft.com/v1.0/users/@.onmicrosoft.com/messages) I will be able to retrieve the mail messages.

    Why is this?

    Thanks in advance,
    Lei Xu

    1. Hi Lei san,
      You cannot get from /me uri using this token. The token which is written in this post is the token for "application" scope, not "user" scope. As you know, no login UI is displayed when retrieving the token, and the token is not including who logged-in. (First we're logging-in using administrator user, but this is not for getting token, but for subscribing (consenting) this application in this tenant.)
      If you want to use /me using the user token, please use the usual OAuth flow as follows. (Don't use this application permission.)
      Sorry, but the following post is written in Japanese, and please translate ...
      https://blogs.msdn.microsoft.com/tsmatsuz/2016/02/24/v2-endpoint-oauth2-client-using-azure-active-directory-and-microsoft-account/

      1. Lei Xu says:

        Thanks a lot for the clarification.

  9. Lei Xu says:

    Hi,

    Thanks to your post and answers I have managed to access office 365 resources with oauth2 client credentials flow through azure ad 2.0.

    I am looking for a little bit different two legged tutorial which use openid connect.

    I believe that openid connect for azure ad 2.0 login is in this format:

    https://login.microsoftonline.com/{tenant}/v2.0/.well-known/openid-configuration

    Wondering how to use this to achieve two legged authentication just like the oauth2 client credentials flow.

    Thanks in advance.

  10. PA says:

    Hi,

    thank you for your post.

    I followed the steps using crome rest client. I was able to give admin consent successfully. I also got the token created. But when i use that token to query graph apis, I am getting error
    "code": "InvalidAuthenticationToken",
    "message": "CompactToken parsing failed with error code: -2147184105",

    Please help me fix this issue

    1. Are you using Microsoft Account to consent ? Even if it has the admin permissions, you cannot use Microsoft Account in this scenario. You need to use organization admin account (usually it has the format of *****@*****.onmicrosoft.com or provisioned custom domain) for consent.

  11. aladac says:

    Hi,

    What is the correct way to specify both the ".default" and the "offline_access" scopes? When I try to add "offline_access" to the scope parameter while calling the token endpoint I get a incorrect scope error.

    1. Hi, This (app permissions) is used for the backend (daemon app, backend services, etc) which uses certificates in the backend, so I think you cannot specify "offline" which is usually used for the mobile app that is preserving token as refresh token in the devices.

  12. KevinA says:

    H I am getting:
    Authorization_IdentityNotFound
    when I try to:
    var graphClient = new GraphServiceClient("https://graph.microsoft.com/v1.0", new DelegateAuthenticationProvider(
    async requestMessage => { string token =authResult.Token;
    requestMessage.Headers.Authorization = new AuthenticationHeaderValue("bearer", token);
    })
    var user = await this.graphClient.Users[emailAddress].Request().GetAsync()

    I have User.Read.All under both Delegated Permissions and Application Permissions

    1. In your case, you don't need application permissions, and also don't need the delegated permissions previously set in App Reg Portal. (In v2 endpoint, you can set your required scopes on the fly like "https://login.microsoftonline.com/common/oauth2/v2.0/authorize?response_type=id_token+code&response_mode=form_post&client_id={your app id}&scope=openid+https%3a%2f%2fgraph.microsoft.com%2fmail.read&redirect_uri={your app url}&nonce=abcdef".)

      I'm sorry but I'm not familiar with "Authorization_IdentityNotFound" error, and can you please give me the error status and messages on HTTP response by using Fiddler or something ?

  13. Evan says:

    Please help! I keep getting error "The identity of the calling application could not be established"

    1. Are you following the step by step procedure written in this post ? (Or you're using SDK or some sort of things ?)
      If so, can you please give me the entire HTTP request and HTTP response of the error request ? (You can use Fiddler for tracking HTTPS if you're using SDK.)
      I think this error seems to be the same as "Authorization_IdentityNotFound" which is mentioned in another comment.

Skip to main content