- Setup an Azure AD protected Web API
- Test your Azure AD protected Web API
- Debug your Azure AD protected Web API
While there are already a few blogs out there that are doing a very good job explaining how to setup an Azure AD protected Web API. I noticed that sometimes it’s just a hit and miss for some people that do the setup and run into an error or issue.
I just wanted to show some more detailed information on how you can debug an Azure AD protected Web API when it’s not working as expected.
Setup
1. Create an Azure AD app. So go to https://portal.azure.com and follow the steps in the screenshots given below.
2. Now get the Application ID URI from your newly created Azure AD app. Take a look at the screenshots below to see where you can find these.
For the Application ID URI you can click on ‘Add an Application ID URI’. This will bring you on the ‘Expose an API’ page of your Azure AD app. Here you should click on ‘Set’ on top of this page. And save the URI that is generated by default.
Save this URI somewhere in your notepad, we’ll need this in our configuration for the Web API.
3. Add a Startup.cs class to your Web API project if you don’t have this already. Add the following code to your Startup class.
using Microsoft.IdentityModel.Tokens; using Microsoft.Owin.Security.ActiveDirectory; using Owin; using System; using System.Collections.Generic; using System.Configuration; using System.Linq; using System.Web; namespace AzureAD.API { public class Startup { public void Configuration(IAppBuilder app) { ConfigureAuth(app); } private void ConfigureAuth(IAppBuilder app) { app.UseWindowsAzureActiveDirectoryBearerAuthentication( new WindowsAzureActiveDirectoryBearerAuthenticationOptions { Tenant = "{{TenantURI}}", //e.g. demotenant.onmicrosoft.com -> this can also be a normal domain name, depending on your Azure AD configuration e.g. wardwilmsen.com TokenValidationParameters = new TokenValidationParameters { ValidAudience = "{{Application ID URI}}" //e.g. api://1e65b3d8-6d84-4d3e-9ea2-70d2041c4fbg } }); } } }
Make sure you have the ‘Microsoft.Owin.Security.ActiveDirectory’ Nuget installed.
4. Set the ‘ValidAudience’ parameter to your Application ID URI value that we just got from our Azure AD app. We’ll also need our TenantURI. You can find this one in the Azure portal in the general overview of your Azure Active Directory.
5. Now you can also set the Tenant variable in your Startup.cs file. Once this is done, the hardest part is done. We just need 2 more simple steps to get our Azure AD protected API launched.
6. Protect your API Controller with the authorization filter. This will set the Azure AD authentication for an API Controller. So without this step there is no security on your web API.
7. Setup CORS in your web API. You can do this inside your Azure Web app if you are using this to host your web app. But I prefer to do this inside the web.config file of your web API. This way it’s always configured correctly and you know what you are doing without overhead of your Azure app settings. Use the code below in your web.config (httpProtocol => customHeaders) to make sure you allow cross origin calls.
<system.webServer> <handlers> <remove name="ExtensionlessUrlHandler-Integrated-4.0" /> <remove name="OPTIONSVerbHandler" /> <remove name="TRACEVerbHandler" /> <add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" /> </handlers> <modules> <remove name="TelemetryCorrelationHttpModule" /> <add name="TelemetryCorrelationHttpModule" type="Microsoft.AspNet.TelemetryCorrelation.TelemetryCorrelationHttpModule, Microsoft.AspNet.TelemetryCorrelation" preCondition="integratedMode,managedHandler" /> </modules> <httpProtocol> <customHeaders> <add name="Access-Control-Allow-Origin" value="{{DOMAIN YOU WILL BE CALLING THE API FROM}}" /> <!-- e.g. https://demotenant.sharepoint.com --> <add name="Access-Control-Allow-Credentials" value="true"/> <add name="Access-Control-Allow-Headers" value="Content-Type" /> <add name="Access-Control-Allow-Methods" value="GET, POST, PUT, DELETE, OPTIONS" /> </customHeaders> </httpProtocol> </system.webServer>
Make sure you fill in the correct value for the ‘Access-Control-Allow-Origin’ header. If you want to allow all websites to call your API, you can also use a * as a value for your origin header.
8. All done! Upload your web API to your hosting. I prefer to use Azure, it’s just a quick and easy to deploy your web app in there.
Test
Of course it is important to test your Azure AD protected API. And I think the most easy way to do this, is to use Microsoft Flow with the HTTP action.
1. Go to https://flow.microsoft.com and create a new flow. You can use any trigger of your choice, we will just test / trigger this one manually once the setup of this flow is done.
2. Select the HTTP action, we will now call one of our API controllers that we just deployed to our web app. For me it’s just the standard Values controller that I am calling with a basic GET call. This same steps apply to any type of call that you are doing (POST, PUT, PATCH, …)
3. Click ‘show advanced options’. Here we will select ‘Active Directory OAuth’ for Authentication. This option will ask for some params to be filled in before it will work though.
- Tenant: The same value as we have filled in for our Startup class in our web API.
- Audience: This is our Application ID URI from the Azure AD app. (same value as in the Startup class)
- Client ID: This is the id of the application we want to use to authenticate against the Audience, this can be any (other) Azure AD app. To make this easy, we will just use the same Azure AD app as we used to configure our Azure AD Authentication. So use the Client ID from your Azure AD app we created earlier in this post.
- Client secret: We want to authenticate with this app on the Audience, so we should provide a secret (=password for your app) to get a valid authentication flow. To create a client secret for our Azure AD app. Go to the Azure portal and go to the ‘Certficates & secrets’ tab and add a New client secret. You can set an expiry time if you want to. Give it a any name you want and press ‘Add’ to generate the new key. After adding this key, you should immediately copy this key from the overview of keys. Because it will be hidden afterwards. Use this key as the client secret for your HTTP call in Flow.
4. Trigger your flow manually with the ‘Test’ button in the upper right corner. If the HTTP call failed, it will say something like this.
5. If it did succeed it will show up green and should return the value you expect from your API call. If it failed, you should hop on to the next part of this post to debug potential issues in your Azure AD protected web API setup.
Debug
As a first step I always want to make sure that my Startup class is being triggered as it should. So what you could do is start the web API in Debug mode from your Visual Studio. It will run on your localhost, but this enables you to check whether the Startup function is triggered when the IIS service is firing up.
If this debug points are not being triggered during the startup of your web app in your local IIS. It means that you are still missing an Owin package. To fix this issue install the Microsoft.Owin.Host.SystemWeb Nuget package.
If the step above is still not the solution to your issue. You can go a little bit further into debugging your Azure AD authentication on the web API side.
Add the following piece of config to your web.config file. This will enable the logging for your Microsoft.Owin middleware.
<system.diagnostics> <switches> <add name="Microsoft.Owin" value="Verbose" /> </switches> </system.diagnostics>
Now hook your remote debugger from your Visual studio to your Azure web app. And open up the Output window to check the output of the debugger of your Azure web app. Run the flow again and if your Owin configuration was updated with the settings above it should show some more detailed information about why the authentication is failing.
I just changed the Audience (Application ID URI) here in the web API. So this will throw a ‘Microsoft.IdentityModel.Tokens.SecurityTokenInvalidAudienceException’ which is pretty clear about what is going wrong here. This is caused because the audience in the web API is not matching with the one of our Azure AD app and with the one defined in Flow.