Configuring Scopes in Azure Active Directory (Part 1)
Tags: azure, oauth, scopes, aad
Published: 2020-04-20 17:12:44

##**Intro**##

When I started learning Azure Active Directory, I was pretty overwhelmed by the Microsoft documentation, which in my opinion was pretty complicated (especially for people who don’t have much experience at configuring any OAuth Authorization Servers — like me). Since I’ve spent a lot of time reading Microsoft documents, blogs, and have experimented by myself and with my teammates (thanks [Tim](https://www.linkedin.com/in/tpadams89/)!), I’d like to share with you what I’ve learned about AAD during this time.

I’ve always been a big fan of the “Example first” way. Using this idea, I’m going to walk you through some examples and, hopefully, after reading this tutorial, you can approach Microsoft Documentation fearlessly.

This article assumes that you already have a good understanding of OAuth 2.0, especially Authorization Code flow. If you don’t, I highly recommend you watch this great video: {% youtube 996OiexHze0 %}

[Here](https://oauth2.thephpleague.com/terminology/) is the OAuth terminology that will be used throughout this tutorial.

I also assume that you have your own tenant in Azure and it’s associated with the valid subscription. If not, you can find tutorials how to do it [here](https://docs.microsoft.com/bs-latn-ba/azure/active-directory/fundamentals/active-directory-access-create-new-tenant) and [here](https://docs.microsoft.com/pl-pl/azure/active-directory/fundamentals/active-directory-how-subscriptions-associated-directory).

Throughout this tutorial I’ll be using only Azure AD Endpoint V2, however, in one section I’ll add in some basic differences between V1 and V2.



##**Creating applications**##
First, we’ll create two applications in AAD, that will represent both Client and Resource Applications. You can do this by following these steps:

*Your Azure Active Directory instance -> App registration -> New registration:*

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/jek8t0is4wfuly1r75gd.png)

You need to fill in the Name textbox and the Redirect URI as shown on the screenshot below (you can provide different names, of course, but I suggest using the same ones, because I’ll use these during this tutorial):

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/g3flvfvb1c509un7a2hr.png)

The same requirement needs to be completed for the Resource Application. Here is the screenshot below:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/h7ixn3b4fiohy4b9s3so.png)

We’ve created two applications in AAD (these are neither WebService nor Azure Function applications and so on — both are only representations of physical applications in AAD):

* **ClientApp** — this application represents the application that communicates with the user directly (e.g. ASP.NET application) and acts on behalf of the User when communicating with the BooksCollectionApp.
* **BooksCollectionApp** — this application represents the Resource Application that may have sensitive information belonging to the User (e.g. WEB API contains User’s data).

##**Creating scopes**##

Now, let’s create scopes. To do this, let’s first go to the details of the *BooksCollectionApp* in App Registrations. Next, select Expose an API, then click the *“Add a scope”* button.

When you click this button for the first time, you should see a new window stating that you need to add an *“Application ID URI”* before proceeding. Let’s assign a URI that will represent your application (like Domain Name). I’ve used `https://bookscollectionapp.com`. You can pick any valid URI, that’s unique across your tenant; you don’t need to buy a domain.

Next fill in the panel like below:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/9ggqxav1rh4o7wqwbxrr.png)

It means that this scope is meant for everybody, not only for administrators. These administrators won’t have to give consent to enable this scope for users (I’ll explain it better later on). Both the Display name and the Description input above, will be shown on the consent page while getting JWT.

##**Getting access token**##

Now let’s use Postman to get a token which will contain the scope created above. Normally, a web app or mobile app would redirect you to log-in to the Microsoft website to receive an Authorization Code. Then the app would exchange the Authorization Code for a JWT. Exchange for a JWT only occurs during Authorization Code flow. To keep things simple and to better understand, we won’t build an app, we’ll just use Postman.

In order to get a JWT, there will be two stages:
###1. Getting an Authorization Code###

During this stage, we actually don’t need Postman at all, only a browser. However, I use Postman to generate URLs that are copied and pasted to a browser later.

To create a GET request like in the screen below, you need to paste your Tenant ID in the black box. I’ve changed my Tenant ID to black in the hope that you won’t hack me :D. Second, change the *client_id* value to **your** *ClientApp* Client ID. Then, change the scope value in a way that replicates the following:
`{{your BooksCollectionApp Application ID URI}}/Books.Read`

You can find your Client ID and Tenant ID in the Overview of your application in the Application Registration panel.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/yvxspwoaieiqa3zmzvaf.png)

Following on, copy the link from the Postman (the one that is underlined in red above) to your browser. After sending the request, you should be able to see the following screen:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/8t44t7z7k3kfzhi650rw.png)

The consent view shows the user what the *ClientApp* will be able to do on behalf of them.

Next, press accept and you should be redirected to *https://localhost:44327/signin-oidc*, and in the query parameter code your Authorization Code will be contained:

<code>https://localhost:44237/signin-oidc?code=<b>AQABAAIAAAAm-06blBE1TpVMil8KPQ41DmEwZFwyAM06SoXS0-M0oC2oKw85100IlRqALFCc8x3_XbOql3smPubHvda7WBiUNRIAxYnnCRWj7WBXD0QGK-1xDtuBxzCauxKd5N9ZEEKv_vn4I4x4tC6EsU7SlH81Abmlxw8njiHtuVszhQiRonSP7xLubXYvJ0MEvit_YExvfx0EkPPQh1nZlN8KQwde05zIvXvpgHFqyw1QxH52bs7bgFGauhFBnd8tn1iwmE74BKLufGOwCtBT4dqhpTK17s49s-FzRckDeKGQJouD758Pf0qsyTzdIUNst2rWNk2A7IL_Hv_BX1vb9T_KCpyzphPre7iVCLJdm3cGNrYUzVVUFwMrOPBWyHtsdei8Zf4BJqFYSFvVBs5Xa6CSuDWfVy0HVWJlTYnSRUBz6hFYsgP4sAlrMpT4IAA</b>&session_state=c67ad588-c6b5-4419</code>

Copy the code, but be careful not to press Shift + End because there is one more query parameter at the very end.

###2. Exchanging Authorization code for an access token (JWT)###

The next step is to exchange the access code for JWT. We must send a POST request from Postman as shown in the screenshot below. The value of `{{client_id}}` is the *ClientApp’s* Client ID. You can generate the `{{client_secret}}` by going to “Certificates & secrets” in the *ClientApp* view, and `{{tenant_id}}` is your Tenant ID. You can take advantage of the Environments feature in Postman, the same as what I’ve used. During the last step you must insert your Authorization code in place of “HERE PASTE YOUR AUTHORIZATION CODE”.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/mgonmfxogpglkyi38tyl.png)

After clicking the Send button, your response should resemble below:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/2z5j6ougu275ztzccyso.png)

After generating your Client Secret, it may take some time to propagate the changes, thus, if you receive the error that states the Client Secret value is invalid, you should consider sending the request again in about 1–2min.

Copy the value of the access_token property (it’s a JWT), ensuring not to include the quotation marks, and paste the token at https://jwt.ms. Now you should be able to see that your token has been decoded. The parts you should focus on are the *“aud”*, *“appid”* and *“scp”* properties. Below, you can see some parts of my token (I’ve removed parts of it for security reasons):

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/048s7nbos37lcuknnhws.png)

The *“aud”* claim is the abbreviation for *Audience* and its value is the *BooksCollectionApp’s* Application ID URI — AAD takes the Application ID URI from the scopes parameter from the first request. The *“appid”* is the *ClientApp’s* Client ID. The *“scp”* above is the space separated scope (there can be more than one value). As you can see, the scope is exactly the same value that we had previously used in our *BooksCollectionApp*.

This means that when the *ClientApp* (e.g. ASP.NET application) passes our token to the *BooksCollectionApp* (e.g. WEB API), the *BooksCollectionApp* knows that it shouldn’t allow the *ClientApp* to perform any other action than reading the user’s books. The *ClientApp*, on behalf of us, won’t be able to write anything to the *BooksCollectionApp*. This can only happen if we trust the *BooksCollectionApp* to follow the scopes provided in your token.

##**An alternative way to get an access token from Postman**##

In the previous section, I showed you how to get an access token using Postman, however, there is actually an easier and quicker way to receive it from there. If you select any request in Postman, you’ll be able to see an *Authorization* tab, like in the screen below:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/3uza3no83cisutiqwm98.png)

In order to get JWT from AAD, you should select *OAuth 2.0* from the type menu and click the *“Get New Access Token”* button. After the pop-up window has appeared, you’ll need to fill in the inputs as shown in the screen below:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/4pm17stjxegdwl2wu6gf.png)

After accomplishing the above, click the *“Request Token”* button. A new window will pop up, containing the consent view. If it’s accepted, the *Access Token* input in the request will be filled in with the desired JWT. Please be advised, if you ask for the same scope as in the previous section, you won’t get the consent view, because you’ll have already given consent for this scope.
The result of the above will be the same as the method described in the previous section, but if something goes wrong (e.g. if you pass a wrong parameter), this method won’t provide you with many details about the error. Taking everything into consideration, this is why I prefer the explicit (previous) flow.
One important thing that should be remembered is the following difference between *Auth URL* and *Access Token URL*. These are both similar, but not the same. The first endpoint points to the function of actually getting the Authorization Code, and the second one points to the function of exchanging the Authorization Code for JWT.

##**Scopes that only Administrators can consent to**##

Now, let’s create a different scope that requires administrator’s consent. Return to the *BooksCollectionApp* and to the “Expose an API” panel and add a new scope (make sure to select *“Admins only”* in the *“Who can consent option”*).

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/mma9fchemgqj5ul89izn.png)

Let’s now try to get a new token using the new scope:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/8h1ubini2fitrf068f1v.png)

You should probably get the token without any problems; you might ask yourself why? Since it’s been changed so that only administrators can consent! The answer to your possible question is that you are probably a Global Administrator. In order to get a better idea of this option, you should be a regular User. If you don’t have any regular users in your tenant, you can add them in by creating a new one by going to:

*Your tenant -> Users -> All users -> New user.*

After completing the above, let’s now try getting an Authorization Code by logging in as a regular user. After logging in as a regular user you should see the view below:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/jwwdtct6q495meyyt6b6.png)

This means that the user wants to use the admin only scope. We’ve created the scope *Book.Read*.***All*** for this reason, it means that everybody who uses the scope, will also be able to read all users books (not just the logged-in user’s books), of course, only if the physical application is implemented in this way. In a real scenario, the Owner of the *ClientApp* (the Owner is the person who has created the application — you can see the list of owners in the *Owners* panel contained within your application) should ask a Global Administrator to give consent, if the *ClientApp* needs to use this scope. In order to allow an administrator to give consent, the Owner must go to the *ClientApp* and add the scope to the *API Permissions* panel. Let’s go ahead and do this.

Go to:
*ClientApp in App Registrations -> ClientApp -> API permissions -> Add a permission -> My API’s -> BooksCollectionApp -> Delegated permissions -> Check “Books.Read.All”*. As below:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/j3gwhpzlkmfvpvc5qnpc.png)

As you may have noticed, there are two different types of permissions: *Delegated* and *Application*. When thinking about scopes, these are *Delegated* permissions. *Application* permissions are *App Roles* which contains an *Application* allowed member type (I’ll write a similar tutorial for *App Roles* in the future). After the scope has been added, you should see the table below:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/se21z5qefkj39pcbtc8o.png)

In a real case scenario, the Owner of the *ClientApp* should ask the Global Administrator to grant permission to the *ClientApp* application, this can be done by clicking the button *“Grant admin consent for…”*. The Global Administrator, after validating the request, should click *“Grant admin consent for…”* button, giving consent for the application. In our case the button *“Grant admin consent for…”* only needs to be clicked. After completing this, the following should be visible below:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/r2bfjq4j5z9wohanq9xw.png)

Once again, let’s try to get a token as a regular user. If you have copied everything provided in this tutorial correctly, you will be presented with the JWT which contains the **Books.Read.All** scope.

Hold on, how was I able to get the token with the proper scope without the consent view showing up?! This has happened because you’re not an administrator and as it has been shown previously, this specific scope requires Administrator Consent to obtain. At first, it may be a little bit confusing, especially because the purpose of scopes is to show the user what the Client can do on behalf of them and now there is no consent view, however, there is logic behind this concept. The scope requires administrator consent and it must be remembered that you don’t have administrator privileges. It took me some time to clarify this and I recommend you to go over this section a few times to ensure it’s clear :D.

Now in your JWT there should be more than one scope, as can be seen below:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/o049i2guxfqfpsr8topy.png)

##**Summary**##

I hope that this tutorial has helped you better understand Scopes in Azure Active Directory. It hasn’t comprehensively covered everything required to understand all details related to scopes. In the next coming weeks, I plan to release the second part of this tutorial, which includes more features that you can perform with Scopes. In the meantime, if you have any questions feel free to get in touch with messages and comments below.