In the last article you learned how to authenticate an ASP.NET Core web application to Azure Active Directory using the preview version of the
And ideally (hopefully!) you were able to make sense of the concepts and how the fit together because the first article in this series helped you out!
This post takes it one step further. You'll create a Web API application, protect it behind Azure AD, and then have the web app from the previous article access it.
(And remember, refer back to that first article in this series to help explain any concepts.)
Let's get to it.
The Authentication Process
Here's the high-level flow of what's going to happen.
- The web application authenticates with Azure AD - as outlined in the previous post.
- As part of that authentication, Azure AD will return the ID and Access tokens.
- Something cool though has been setup behind the scenes. A new Azure AD application was created. And in that application a scope or permission was defined.
- The Azure AD web application was granted permission to that scope.
- And then during the authentication process, that scope is put into a claim inside the access token. I hope all of the concepts we talked about in the first articles are coming together now. Tokens, claims, scopes - there's a method to all the madness!
- The web application will then send a request to a Web API that's also registered with Azure AD. It will pass that access token ... and as mentioned, the scope is a part of that.
- The API will check that token and scope against Azure AD and then will process to process the request, or reject it.
It should be noted that no user-level authorization is going on here.
The scope and the token in general are being checked as being valid. But no checks as to whether the user has access to the particular resource is happening. That's up to your code.
You can think of a scope as a permission at the Azure AD application level. One Azure AD application says I have these scopes and they'll do certain things. (Like read and write products from a database.) Then those scopes are granted to other Azure AD applications. And a user (or admin of Azure AD) consents that the calling application can use the scope(s).
So even though an API may have a scope that says if you can access me, you can read & write products from a database. It's really saying, your request will be able to access the code that reads & writes the products, but the code will still do some authorization to make sure you're reading & writing the correct products.
And in step number 4 above, I'll use the
Microsoft.Identity.Web library to make development easier on myself.
That's the general overview. Time to set things up.
Setting Up the Web API
There are 2 parts to get a web API ready to do authentication with Azure Active Directory.
The first is to setup the Azure AD application to model the real-world web API.
The second is the code the web API and make sure it communicates with Azure AD appropriately to check the token and scope.
(And using the
Microsoft.Identity.Web library here will really help us out. But you could use the MSAL library if you want. Or the built-in ASP.NET middleware. Or even roll your own (don't do that) ... but I wanna talk about
Microsoft.Identity.Web so that's what I'm going to do).
Azure AD Application
First thing first - we're going to need an Azure AD application to model the web API application within Azure AD.
Why a distinct Azure AD application for the web API?
Why do you want a separate Azure AD application for your web API from your web application?
Because Azure AD applications model real-world applications and hold application specific attributes such as reply URLs, secrets, and API dependencies. A web application will have different info in those values.
And from a (non Azure AD) application perspective, a web API serves a different purpose than a web application, or mobile app for that matter. So because they are different, it makes sense for them to reside in their own Azure AD applications too.
Setting Up the Web API Azure AD Application
The initial setup of the Azure AD application that models the Web API is exactly the same as the one for a web application.
Once done with that you'll have the stuff needed for the ASP.NET Core Web API to call Azure AD, namely the Application ID.
You need to take a couple of additional steps though.
The first is that you need to register a scope, or expose an API on the Azure AD application.
This is one of those areas again where multiple terms are used interchangably.
When you hear somebody talk about exposing an API for an Azure AD application, they're also talking about creating a scope for that application. And vice-versa.
Follow the steps here to expose an API for the Azure AD web API application.
Once you have that done, you'll have both an Application ID URI and the scope name - which when combined with be unique, and how the calling application will refer to them.
The ASP.NET Core Web API
The next step in all of this is to create the ASP.NET Core Web API. And here I'm going to use the
Microsoft.Identity.Web library. (But it should be noted this library is in preview as I'm writing this.
If you installed the
dotnet new templates from the
Microsoft.Identity.Web library, then you can run a single line and get most of everything setup for you.
dotnet new webapi -n ForecastAPI -au SingleOrg
You'll need to modify the
appsettings.json file, just like you did when setting up the web app. (Change the
Let's walk through some of the code that
Microsoft.Identity.Web uses to integrate with Azure AD and help perform the authentication.
Microsoft.Identity.Web and Azure AD
The first thing I want to call out is in the
Startup.cs file under
This is adding the
Microsoft.Identity.Web functionality to the middleware - so it can be used later. Notice it's using the
AzureAd section from the
Now open up the controller the template created for you:
First, it has the
[Authorize] attribute at the class level.
Then in the
Get function, this line:
scopeRequiredByApi is an array of strings that is any scope name (only the name, not prefixed by the Application ID URI) that you created while defining the Azure AD Web API application.
So this in effect is saying - if the token coming in doesn't a claim with any of those scopes as a value - reject the request.
That's it. The Web API is setup to handle incoming requests. The
VerifyuserHasAnyAcceptedScope will verify the user is authenticated and has the proper scope in the access token.
Get at the user's identity
Now the user has authenticated - how do you authorize them?
Well, that's up to your application - but you can find out information about the user.
That will be done through various Claims which are returned in an ID token.
But you don't need to inspect the ID token yourself. The
User object, which is a part of the
ControllerBase class the Web API controller inherits from, does it for you.
So this line:
var userId = User.FindFirst(System.Security.Claims.ClaimTypes.NameIdentifier)?.Value;
Will grab you an identifier for the user - which you can use to perform some more authorization against.
Changes to the Web Application
Of course you can't just call the web API and have everything work. There's some more configuration for the Azure AD application and the code for the web application.
Changes to the Azure AD Application
There are 2 additions you need to make to the Azure AD application that represents your web application.
- Create a client secret.
- Add the exposed API/scope as a configured permission.
So there's something I've haven't told you yet. In order to get an access token, the web application actually has to send a client secret over to Azure AD first.
And the only time you want to send a client secret is when your web application is 100% server-side. (This is known as a confidential client).
If you really want to get down into the weeds, the whole signing-in process is known as a flow in the identity world. And the flow a server-side web app takes when a user logs in to get an access token to call a web API is called an Authorization Code Flow. (And check it out, there are 2 calls happening to Azure AD in order to get an access token! But our the
Microsoft.Identity.Web library is abstracting all that away from us!)
High level, the client secret that's getting passed in this flow is telling Azure AD it can trust communicating with this app.
There are other flows available to applications which have different setups, such as SPA but those are beyond the scope of this article.
Long story short, you need to create a Client ID for the web application's Azure AD application, and this tutorial will show you how.
The second thing you need to do is tell the web application's Azure AD application that you want to grant it permission to the web API's scope.
You do this because the access token generated after authentication needs to include a claim with the scope as a value. And the only way the Azure AD application can know about the scope is to have it configured somewhere.
That somewhere is here.
This tutorial will walk you through how to add the permission to access the exposed API of the web API to the web application.
Changes to the ASP.NET Core Web Application's Code
We're almost there now.
There's a couple of changes you'll need to make to the ASP.NET Core web application in order to authenticate to Azure AD to get that access token and then to exchange it for whatever functionality the web API has!
- Add a
ClientSecret: XXXXXvalue to the
AzureAdportion of the
appsettings.jsonfile. This will slot in right below the
SignedOutCallbackPath- although ordering does not matter.
Then in the
Startup.cs file. The
services.AddMicrosoftWebAuthentication turns into:
This again is all
Microsoft.Identity.Web and it's priming the pump to have the web application use in-memory tokens and have the ability to call web api's.
The only thing that's left is to call the web api - and that's done through this class.
All the fun happens down in the
And by fun, this line:
_tokenAcquisition.GetAccessTokenForUserAsync(scopes) pulls out the access token that was received during the authentication with Azure AD.
Then further down that token is put into the
Authorization header of the HTTP request.
Then one last thing. You'll notice the code above is making mention of a section from the
WeatherAPI. This is what that looks like:
The thing to note here is the scope is the fully formed one. Application ID URI + scope name.
Get the code!
You can download all the code for this sample here. You will have to do the Azure AD setup yourself and pop those values in, but everything else should work.
Web App Meet Web API
There it is.
By virtue of creating a new Azure AD application that exposes some scopes, and granting access to those scopes to the existing Azure AD web application, an access token can be generated that includes a claim holding the scope's value.
Once that access token is retrieved by the web application, it can send that over to the web API, which validates both the scope and the token against Azure AD - and then lets the request go through.
But remember - this isn't doing authorization in the sense that it is verifying the user can access particular data. Only that the authenticated user of the web application has access to the web API. It's up to your code to verify the rest of the authorization.