DEV Community

Cover image for Configuring Scopes in Azure Active Directory (Part 1)
ockamey
ockamey

Posted on • Edited on

Configuring Scopes in Azure Active Directory (Part 1)

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!), 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:

Here 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 and here.

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

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

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

Alt Text

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

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

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

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:

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

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

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

Alt Text

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

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

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

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

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

Alt Text

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

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

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

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

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

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.

Top comments (7)

Collapse
 
darklite1 profile image
DarkLite1

I singed up specifically to thank you. This write-up really helped me understand the concept behind scopes a lot better than reading 20 articles on the Microsoft websites. Thank you very much for taking the time for writing this. #stayhomestaysafe

Collapse
 
ockamey profile image
ockamey

Thank you! I'm really happy that I could help.

Collapse
 
jamiegpo profile image
jamiegpo

Similarily to DarkLite1... a massive thank you. Really great to have all that info in one place & in such a concise clear manner. Really appreciated.
Not to take away from the above... but if anyone else is looking at this I would also recommend the following which I attended @ignite a couple of years ago:
myignite.techcommunity.microsoft.c...
myignite.techcommunity.microsoft.c...
They're beginning to look at little dated but, combined with the article above, should give a very good understanding of the topic.
Thanks again...

Collapse
 
ockamey profile image
ockamey

Thanks!
BTW the links that you've attached return "Page not found", do you know if we can find the ones that work?

Collapse
 
jamiegpo profile image
jamiegpo

Sorry not sure what happened to them!

jeftek.com/learnazuread/ has them (sessions BRK3234 & BRK4022). You can also find them direct on YouTube here:
youtube.com/watch?time_continue=7&...
youtube.com/watch?time_continue=25...

thanks again...

Collapse
 
pinkesh6834 profile image
Pinkesh Patel

@czmiel24 Thanks for the great article, just wanted to check what If I have multiple scopes supported but token needs to be generated with specific scope. I can see here in your scope even if you are not request .all scope still it is being returned and same issue I am facing with my project.

I have "a", "b", "c" scopes in host app and added them in permissions under client app but when I request the token for "a" scope still it is returning all 3 scopes in token.

Collapse
 
ockamey profile image
ockamey • Edited

Cover Photo by Bradley Dunn on Unsplash