Getting Access Token for Microsoft Graph Using OAuth REST API

Microsoft Graph is here to unite Azure & Office 365 data under a single roof. It is a simple REST API and Microsoft provided many examples on how to use it including an interactive Graph Explorer which allows us to discover the different methods.

Using the API is as simple as sending HTTP request – for example calling this method will return the details about the users in the directory:

https://graph.microsoft.com/v1.0/users

In the Graph Explorer it all works fine, but as soon as we try to use the Graph API from outside the page, like from another program or test application like Postman, we receive “401 Unauthorized” exception.

What’s now?

The Access Tokens

As it turns out, in order to use any of the Microsoft Graph API, we need to let it know who we are – who is making the request.
Microsoft Graph API uses Bearer Authentication in order to validate the request, which means it expects to receive an authorization token (sometimes called a bearer token) together with the request. This token will contain, in a secured way, all the details about the requester.

Sending authorization token with the request is a simple matter, all we need to do is to add an Authorization header to the request containing the word Bearer and our authorization token:

Authorization: Bearer <access_token>

There are several kinds of authorization tokens – Graph API requires an access token. The token itself is a looks like a random base 64 string, something like:

eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI3Rlcy5SZWFkLkFsbCBOb3..
QXBwIG9mZmxpbmVfYWNjZXNzIG9wZW5pZCBQZW9wbGUuUmVhZCBQZW9wbGUuUmVh..
ZC5BbGwgcHJvZmlsZSBSZXBvcnRzLlJlYWQuQWxsIFNpdGVzLkZ1bGxDb250cm9s..
LkFsbCBTaXRlcy5NYW5hZ2UuQWxsIFNpdGVzLlJlYWQuQWxsIFNpdGVzLlJlYWRX..
DMrCzi7JrvQ7_jclKHfPrv7jJAgsHfYkal5OalQAWRC6kaj6cgO0c7xyfGQUnhGA

It is not important to understand the token format for now, only that once we get a valid access token we can use it to access the information we need.

So how do we get the access token? That’s where things get little more complicated.

In order to get a valid token for the Graph API, we need to use another Microsoft API: the Azure Active Directory (AAD) Services.

Azure Active Directory Services

Azure Active Directory is where all of our organization users are stored. Since the data we want to retrieve from the Graph API is usually related to specific organization users, it only makes sense that we need to use Azure Active Directory Services in order to retrieve a valid access token.

Microsoft AAD Services are based on OAuth 2.0 protocol and act as an Identity Provider, which is an OAuth term for “where the users sit”. Using those services, we can issue access tokens for the Graph methods (as well as id tokens and refresh tokens which are not in the scope of this article).

Azure Active Directory Authorization endpoint has the following URL format:

https://login.microsoftonline.com/<organization-directory-name>.onmicrosoft.com/oauth2/token

Meaning every tenant directory has its own URL. The directory name can be found by hovering over our name in the Azure Portal.

Instead of a name, we can also use the directory ID:

https://login.microsoftonline.com/256d2b4c-b5c3-758f-55f3-cd8969f04e86/oauth2/token

Your Azure Active Directory ID can be found in Azure Portal > AAD Properties blade:

So now that we know what is the authorization endpoint URL, what message do we need to send in order to get an access token?

Well, the answer for that is – it depends. The service support several OAuth authentications flows, each suited for different scenario and the kind of information we have. Regardless of the kind of message we send, the response will always contain the Access Token.

But before we can look at the different options, we first need to understand another important part of the puzzle. All of the different flows in Graph API have something in common – they all require a Client ID with a Client Secret. In order to get those, we first need to create an OAuth App.

Understanding OAuth Apps (clients)

A very important concept in the OAuth world is the separation between users and clients. Users are the actual people who use our system. Clients are the applications they are using to do so. Why is this separation important? It’s all about regulating access to resources.

In the past, when applications wanted to access data in another system or database which required authentication it had two options:

  • Pass on the current logged on user authentication
  • Impersonate a strong user

In many times the first option was not used – sometimes because it was complicated to perform (SSO is hard to get right), or the current user did not have enough permissions to perform the operation the application required.

This leaves out the second option, of impersonating a strong user. But which user to impersonate? Since we don’t want to use an existing user (which could lead to many issues), a dedicated user for the application was needed to be created. Those users are often called System Accounts since they are used by the system and not actual human beings.

However, this pattern of using system accounts had many problems, for example:

  • Password policy: System Account had a password just like any other users, which caused problems if the normal password policy were applied to them. For example, the accounts would have locked after a number of failed login attempts (usually by entering the wrong password). Also, the password would expire after some time. In all of those instances, the user was automatically locked meaning all applications which depended on them would have failed.
  • Managing: Users are usually managed by the organization IT department meaning developers were depended on them in order to create and manage System Accounts across different environments. This can cause operation delays and unexpected application shut-downs as a result of a random password change.
  • Security: Since the application runs its code as a power-user, any vulnerability in the code could allow users to gain access to data they were not supposed to. This can be used for to perform Privilege escalation attacks. Because of that, it is usually considered dangerous to use code impersonation.
  • No refined Access Control: Since the application is using a single System User for ALL users, it means that System Account has to have full access to ALL of the user’s data. This could lead to information leakage where users are accidentally exposed to other users data. Also, the users have no say in what kind of data a specific application can access – it is controlled by the system itself.

Because all of those issues OAuth protocol doesn’t use System Accounts in order to authenticate a client program. Instead of System Accounts, we now have OAuth Apps (clients).

In OAuth, when a client application wants to access a resource (for example our Graph API), the first thing it needs to do is to authenticate it self (meaning which client application is calling the service, not which user is using it). This is done by sending Client ID and it’s matching Client Secret.

So where do we get that Client ID and Secret? We can get it by registering an OAuth App.

Registering OAuth App

We can register OAuth App for the Graph API from the Azure Portal.

Full instructions on how to do so can be found in the official documentation here.

For our needs, this is the minimum which is required:

  1. Create new App in the target directory (Azure Portal > Azure Active Directory > App Registration > New Application Registration)

  2. In the Create screen enter the following information:

    • Name: Can be any name, for example “MicrosoftGraphClient”
    • Type: Must be “Web App/API” for our needs.
    • Sign-on URL: Not important (since we do not intent users to directly login to the app), for our case we should put it under the tenant directory, for example https://<directoryname>.onmicrosoft.com/MicrosoftGraphClient
  3. Get the Client ID
    Note the Application ID – It is the Client ID we need for the following steps.

  4. Create new Client Secret
    Navigate to App > Keys > Passwords and add a new key.

    • Name (description): enter a descriptive name for the key so you later know what client application is using it (you can have more than one key per app)
    • Expires: Choose “Never Expire”, unless you want to change your key every year or two.
    • Click Save – a new Client Secret will be generated for you. This will be the only time you will see the Client Secret, so you better copy it to a secured location otherwise you won’t be able to retrieve it again!

Configuring App Permission

Now that we have created an App we have to configure its permissions. In OAuth world, when Apps try to access the information they must have the appropriate permissions to do. Configuring those permissions is a two-step process – first, we need to declare what kind of permissions the App would like to have. Then we need to make sure the App is granted that permission.

  1. Setup app permission
    Navigate to App > Required Permission > Add > Select an API > “Microsoft Graph” > Select Permission.
    We see a list of Graph related permissions. Here we can select the permission our App should have, according to the type of information it needs. If we are not sure about which specific permission is required, we can use the API Documentation to find out. For example, since we wanted to access /Users method we can find in the Users documentation page that User.Read.All is one of the permissions which will allow us to call this method.
    There are two permission groups: Application Permission and Delegated Permissions, and many permissions exists in both groups. More on that later, for now select the required permission in both groups.
  2. Grant App permissions
    Now that we have declared what kind of permission our App requires, it’s time to grant them to the App. Usually this is done by the user itself when the App first attempt to access their information, but for now we can just “accept” on behalf of all of our tenant users by doing one of the following:
    a. Clicking the Grant Permissions button in the App > Required Permission blade

    b. Or using the admin consent endpoint: https://login.microsoftonline.com/{TenantDirectory}.onmicrosoft.com/adminconsent?client_id={ApplicationID}

In both cases, we end up granting our App the required permissions for all of the users in the directory.

Note: Consenting on behalf of all users is usually only done in specific scenarios, like a background service which requires full access to all tenant data. In most cases, users should consent themselves.

Getting the Access Token

After we registered our OAuth App, got its Client ID and Secret, and configured its permissions we can finally use AAD Services in order to get the Access Token.

In OAuth, there are several different ways to achieve access tokens, each suited for different a scenario. Those ways are called “grant flows”, and according to the desired flow different message need to be sent. Let’s review our different flows.

Flow 1: Get Access Token from Client Credentials (Client credentials Grant)

The most basic option is to use our Client ID and Secret in order to get an access token. for this we need to send POST message to our Azure Active Directory Authentication endpoint (which we talked about before) with following body parameters:

POST https://login.microsoftonline.com/<AAD_name>/oauth2/token

  • grant_type: The flow we want to use, client_credentials in this case
  • client_id: The Client ID (Application ID) of the application we created in the previous step
  • client_secret: The Client Secret we created in the previous step
  • resource: The name of the resource we would like to get access, https://graph.microsoft.com in this case

We will receive a response with a JSON object containing the following properties:

  • token_type: The value Bearer
  • expires_on: The token expire timestamp in Unix epoch time
  • access_token: The access token we needed to access the Graph API

This option is called Client Credentials Grant Flow and is suitable for machine-to-machine authentication where a specific user’s permission to access data is not required.

To learn more about this flow: Service to service calls using client credentials (shared secret or certificate)

Flow 2 – Get Access Token From Client & User Credentials (Resource Owner Credentials Grant)

The first option, while is the simplest of all (since it only requires the Application ID and Secret), doesn’t always work for all cases. There are several Graph Methods for which just using the client credentials is not enough – they require user authorization as well.
For example, in order to retrieve Group Events, we can see under permissions that Application is Not supported, meaning getting access to that resource with just Client Credentials will not work.
However, the first line is Delegated (work or school account): Group.Read.All, meaning if we can get a “delegated permission” we can make this work.

So what does “delegated permission” mean, you ask? Well in simple terms, we need to show the API that not only we come with an approved Client, we also have to carry a valid User authorization as well. Meaning that our access token needs to contain both valid Client and User claims as well.

So how can we do that? There are a couple of ways to achieve that, in this option, we will look at the most simple way – the Resource owner credentials Grant.

For this flow we need to send the following POST message:

POST https://login.microsoftonline.com/{{AAD_name}}/oauth2/token

  • grant_type: The grant flow we want to use, password in this case
  • client_id: The Client ID (Application ID) of the application we created in the previous step
  • client_secret: The Client Secret we created in the previous step
  • resource: The name of the resource we would like to get access, https://graph.microsoft.com in this case
  • username: Full username of the user including the domain, for example, john@contoso.onmicrosoft.com
  • password: User’s plain-text password

We will receive a response with a JSON object containing the following properties:

  • token_type: The value Bearer
  • expires_on: The token expire timestamp in Unix epoch time
  • access_token: The access token we needed to access the Graph API
  • refresh_token: A refresh token that can be used to acquire a new access token when the original expires

To learn more about this flow: Resource Owner Password Credentials Grant in Azure AD OAuth

Besides the access token, we received two additional tokens – Refresh Token and ID Token. They were are not necessary for this flow, but they can be used in other grant flows and this is an example of how to get them. We automatically get the Refresh Token in this flow, and we can get an ID Token by adding to the request scope parameter with the value openid, as seen in the above Postman screenshot.

Flow 3 – Get Access Token From Refresh Token (Refresh Token Grant)

Access tokens eventually expire; however, some grants respond with a refresh token which enables the client to get a new access token without requiring the user to be redirected.

Getting Access Token from Refresh Token is a simple process, all we need to do is to send the following request:

  • grant_type: The grant flow we want to use, refresh_token in this case
  • client_id: The Client ID (Application ID) of the application we created in the previous step
  • client_secret: The Client Secret we created in the previous step
  • resource: The name of the resource we would like to get access, https://graph.microsoft.com in this case
  • refresh_token: The Refresh Token

We will receive a response with a JSON object containing the following properties:

  • token_type: The value Bearer
  • expires_on: The token expire timestamp in Unix epoch time
  • access_token: The access token we needed to access the Graph API
  • refresh_token: Refresh Tokens can also expire (although it may take weeks or months). When that happens, a new Refresh Token will be returned here so it can be used as a replacement for the old one.

Those with sharp eyes will notice another small change in the request compared to the others – the URL of authorization endpoint is different.
This is to show that we have another option – instead of using the tenant directory we can just use the common endpoint:

POST https://login.microsoftonline.com/common/oauth2/token

This, in turn, will redirect the request to the original tenant directory, so this endpoint can be used interchangeably with the direct endpoint.

The common endpoint will not work for all grant flows like Option 1 (Client credentials Grant). It requires a User context in order to know which directory the App sits in.

Option 2, Resource Owner Credentials Grant, allowed us to get a “delegated token” (token with both Client and User) using the User credentials. But in many cases we wouldn’t have access to the user password – this flow is more designed for System Accounts, where we have full control of the user.

In most of the cases instead of having the user credentials we will have another Access Token, only issued to our Application instead of the desired API. To better understand that we have to dive a little deeper into the Access tokens themselves.

Understanding the Access Token

So what is that access token is made of anyways? Well, the token is actually a JSON Web Token – a signed JSON document, passed as base 64 format (so it can be sent in the request header). From a practical standpoint, we can think about it as a text string which contains information.
The token is signed (but not encrypted) which means while we can read it (for example by using jwt.io parser) we can’t modify it. The first thing Graph API do is to validate the signature, so if the token wasn’t generated in a place it trust (like Azure Active Directory Authorization Services) it will not accept the request.

You can learn about JWT format at jwt.io.

So what does the token contains? Let’s use jwt.io parser and look at a basic access token, the one returns from Client Credentials Grant Flow (option 1):

{
  "aud": "https://graph.microsoft.com",
  "iss": "https://sts.windows.net/f62479de-8353-4507-aaf3-6a52320f641c/",
  "iat": 1521555934,
  "nbf": 1521555934,
  "exp": 1521559834,
  "app_displayname": "MicrosoftGraphClient",
  "appid": "2024c60c-fe49-4ca0-80e8-94132f56d7c4",
  "roles": [
    "Directory.Read.All",
    "User.Read.All",
    ...
    "Mail.ReadWrite",
  ],
  "tid": "f62471de-8358-4907-aaf3-6a52320f741c",
}

As we can see, the token contains many pieces of information which are called “claims” in OAuth terms. JWT can contain any number of claims – but there are a couple of common ones. Let’s review some of those claims:

  • app_displayname: The Application Name
  • appid: The Application ID (Client ID)
  • Several dates related claims like iat (Issued At), exp (Expire) and nbf (Not Before) which are used during token validation
  • iss: The Issuer of the token, which is a combination of constant https://sts.windows.net/ and our Tenant ID
  • tid: Our tenant active directory ID

There are also ‘aud’ and ‘roles’ claims which are very important, but before explaining those let’s see what is different between this token and an access token which also contains a user.

Delegated Access Token

As we discussed on Option 2 (Resource Owner Credentials Grant), access tokens can also contain a user inside. Let’s look at delegated access token:

{
  "aud": "https://graph.microsoft.com",
  "iss": "https://sts.windows.net/f62479de-8353-4507-aaf3-6a52320f641c/",
  "iat": 1521565239,
  "nbf": 1521565239,
  "exp": 1521569139,
  "app_displayname": "MicrosoftGraphClient",
  "appid": "2024c60c-fe49-4ca0-80e8-94132f56d7c4",
  "family_name": "Kent",
  "given_name": "Clark",
  "name": Clark Kent",
  "unique_name": "clark.kent@supercorp.onmicrosoft.com",
  ...
  "tid": "f62472de-8358-4507-aaf3-6a52320f641c",
}

As we can see most of the claims are the same of the previous example, except a couple of additional claims regarding the user:

  • given_name, family_name, name: The username
  • unique_name: The user full unique moniker, including the company AAD name

This is how a delegated token looks like – it contains both Client & User claims.

It’s time now to look at the aud and roles claims.

Token Audience

The aud claims contain the token audience – who is this access token is issued for.

The audience of the token is a very important security principle in OAuth: access tokens are issued for a specific purpose, which means there is only one place they can be used. In our example, the token audience is the Graph API URL, which means this token is only valid for that service (the Graph service checks that the audience is https://graph.microsoft.com otherwise an exception will be thrown).
This makes sure that access tokens can only be used for the purpose they were issued for.

So how do we issue access tokens for different audiences? This is controlled by the resource parameter which we stated in our request. Since we passed https://graph.microsoft.com we received token with https://graph.microsoft.com as the audience. If we wanted to use the token for another Microsoft service like Azure Graph (the previous version of Microsoft Graph) we would have used https://graph.windows.net as the resource value. Most of Microsoft REST APIs can be accessible if we issue a correct access token for them, for example in order to use SharePoint REST API we need to pass the SP Site URL in the resource (https://<tenant-name>.sharepoint.com).

Besides issuing access token for Microsoft services, at any time we issue token against our own Apps.

In all previous examples, we issued tokens for a specific target – the Microsoft Graph API. But if we wanted a delegated token (so we can perform operations on behalf of a user) we needed the user credentials. In many cases we won’t have those – instead, we will have another access token – an access token which was issued for our Application:

{
   "aud": "2024c60c-fe49-4ca0-80e8-94132f56d7c4",
    ..

Tokens which were issued for a specific App have their aud claim contains that App Id (Client Id). They are used to prove for that application that the user contained in the token is authenticated for it.

App-specific token usually come from two additional OAuth flows (which are out of the scope of this post) – the Authorization Code Grant and Implicit Grant. When we have those tokens, and we want to use another Microsoft service on behalf of the authenticated user, we can’t just use them because their aud claim is “wrong” (it contain our App Id instead of the desired API, for example, https://graph.microsoft.com).

In those cases we need to exchange our Application token in a similar one, only addressed for the service we want (and still contain the authenticated user claims). And exactly for this purpose, we have the 4th flow – the “On-Behalf-Of Grant”.

For more information about tokens and claims visit here:
Azure AD token reference

Scopes

Besides audience, we got another important claim – the scopes of the resource.

Scopes allow us to have a greater resolution regarding access to resources, for example, to separate between a read/write access or to specify which methods inside the service are allowed.

Every resource can define its own scopes – it’s entirely up to the service creators. Inside the access token, scopes are just array of strings so they can contain anything. When using a resource, it is important to know what are the valid scopes so we can issue them accordingly.

Note: Some API may refer to scopes as roles (like in the above example). In OAuth context, they are the same thing.

Microsoft Graph scopes are directly related to the permissions we setup while creating the app – every permission equals to a specific scope.
For example:

  • Calendars.Read: Read user calendars
  • User.Read.All: Read all users’ full profiles

This means that our access token scopes are the direct result of the permissions we set up in the first place – the more permissions we asked the more scopes we have. When using any Graph method, it is important to make sure the access token contains the required scopes.

More information:
Scopes, permissions, and consent in the Azure Active Directory v2.0 endpoint
Microsoft Graph permissions reference
Microsoft Graph API documentation

Now that we have our basics covered up, it’s time to finally discuss the last grant flow, the On-Behalf-Of flow.

Flow 4 – Get Access Token From Another Access Token (On-Behalf-Of Grant)

Flow 4 allows us to convert an access token which was issued for our App into another access token which carries the user claims but is addressed to another App/Service (delegated access token).

For this flow we need to send the following POST message:

POST https://login.microsoftonline.com/{{AAD_name}}/oauth2/token

  • grant_type: The grant flow we want to use, urn:ietf:params:oauth:grant-type:jwt-bearer in this case
  • client_id: The Client ID (Application ID) of the application we created in the previous step
  • client_secret: The Client Secret we created in the previous step
  • resource: The name of the resource we would like to get access, https://graph.microsoft.com in this case
  • requested_token_use: on_behalf_of
  • assertion: Our current access_token

The new access token we get in the response will contain almost the exact information the current one has, only the audience will be different (https://graph.microsoft.com in this case) which will allow us to perform delegated operations against the Graph API.

To learn more about this flow: Azure Active Directory v2.0 and OAuth 2.0 On-Behalf-Of flow

Conclusion

Getting an access token wasn’t easy and required some preparation, but once we have it all we need to do is to send it in the request Authorization header in order to gain access to the Graph API. There are a couple of points we need to keep in mind though:

  • The access token can do only what it can do. Meaning it is up to us to make sure our access token contains the correct audience, scopes, client & user claims necessary for the specific method we want to use.
  • Access tokens can only be addressed to one audience (resource), which means that in many cases we will need to use several access tokens – each one addressed for a different service.
  • Access token usually meant for short-term use (access tokens issued from AAD will expire in 1 hour). Make sure your application can handle the token expiry and utilize the refresh token to get a new access token.
  • Refresh token can also expire, always plan for that scenario.
  • Client Secret and Refresh Token are sensitive information and should only be kept in trusted client applications (for example server side or a phone secured storage). They are never to be kept in browsers, this is what the other two flows are for (Authorization Code Grant for websites and Implicit Grant for SPAs).

You can learn more about AAD authentication scenarios or Authorization Grant Flows.

One Comment

  1. Pretty comprehensive article which probably took you a while to write 🙂

    But since you mentioned ROPC, I have to comment on it a little. I have a pretty strong opinion that you really should not use the ROPC flow unless your case is the service account + automation. Since it won’t work if the user has MFA, password is expired etc. Normally an app should use authorization code/hybrid flow or implicit flow or device code flow to get delegated access tokens. You can even use one of them to get a refresh token to bootstrap a background service.

    Sorry for the small rant, your article is pretty nice actually. I like the structure and the way stuff is presented. The content is also very good.

    ROPC is just one of those things that people abuse in apps which should not use it, and make users pass their passwords through their app.

What do you think?