Originally posted on: http://geekswithblogs.net/PaulNichols/archive/2015/12/18/azure-ad-securing-your-api-with-zero-code.aspx.
See how Microsoft now enables you to add authentication to your Azure APIs with zero code. Also, save yourself time by seeing how to call those API securely.
Firstly, I'd like to say that Microsoft has made securing an API extremely simple with, as they describe it, a turn-key solution. Saying that I still managed to make hard work of this myself so I wanted to document the steps to help others save time.
For all the articles I read on the subject, I still had one piece of the puzzle wrong in my client application. I’d like to show you in this article how I diagnosed and avoid such problems…
I’m going to take you through step by step how to deploy an API app, secure it and then write a console application to access the API.
If you’re like me, you probably find security one of the hard parts of development? Writing the features is fun but deploying that code into production in a secure way is less fun.
Download code here.
Deploying and Securing a WebAPI Application
Let's start by allowing Visual Studio 2015 to generate an API with no security, and then upload that to Azure. We'll then have an API that technically the whole world has access to, hence the need for security.
Choose File > New > Project
Ensure you select the Azure Subscription you’d like to publish to:
Next select the WebAPI
option (notice that I’ve chosen no Authentication on the right hand side of the dialog).
Then, you’ll be prompted to create a new App service plan.
Once you’ve set up the App service plan (details of which I won’t go into here) and you click create, you will be presented with your new WebApi
application
You’ll notice if you expand the solution in the Solution Explorer that there is a values controller by default. This is what we will secure and then query via our test client.
You’ll notice when you look at the code, there’s no Authorize
attribute so when we deploy this API app, it will be sans Authentication/Authorization!
Right, let’s deploy this plain API app to Azure. Right click on the API project in the Solution Explorer and select Publish. When this dialog appears, select “Microsoft Azure App service” as the target of your deployment.
In the next dialog, drill down until you find the App service plan you created earlier and then select and publish. Check Visual Studio’s output window and with any luck, your API app will now be deployed to Azure.
Your site should pop up in your web browser. If you then append /api/values to the address, you should see the following, an XML list of values …
So far so good. Now for the interesting bit.
Navigate to the new Azure portal https://portal.azure.com and select the Resource Group you published your API to.
In the resources blade, select your API site and click on Settings so that the following blade appears. Choose “Authentication / Authorization”.
Turn on Authentication and select Azure AD as the provider.
Now comes the magic, select Express mode and go with the AD App name selected for you. Click OK and click Save
Two weeks before writing this tip, this dialog didn't work and caused me all sorts of problems! The express mode didn't seem to create an application in any AD as far as I could see and there was no ability to search for an existing app. This is the beauty of Azure, it's always changing.improving :)
What this will now do for you is to create an Application in your Azure AD directory. In our case, this is the app the client will Authenticate with as well as the app the API is secured with. The doesn’t have to be the case. You can have an application for each of your APIs and then one for your client which has permissions to access all the API applications. This will be in a later article.
Now if you swap to https://manage.windowsazure.com and navigate to our AD directory, select applications and you should see your newly created and correctly configured Application. If not, then select “Applications my company owns” from the search filter and click the tick to search.
If you go back to your web browser now and try to GET the values from the API, you should find the API is secured and you never had to write a line of code or config!!
Excellent, but this is where I got a little confused and made hard work of the few lines of code required to create a client Console app and access the API securely.
Creating a Client Application to Call the Secured WebApi Application
What the console app will do is first get a bearer token from the AD application to access the secured resource, then add that token to an authorization header before calling the API for the values.
I won’t tell you how to create a Console App, but when you have then copy and paste the Program.cs and app.config code from here into your app.
First, let's look at the config:
="1.0"="utf-8"
<configuration>
<startup>
<supportedRuntime version="v4.0"
sku=".NETFramework,Version=v4.5.2" />
</startup>
<appSettings>
<add key="AADInstance"
value="https://login.windows.net/{0}" />
<add key="Tenant"
value="" />
<add key="ClientId"
value="" />
<add key="ApiResourceId"
value="" />
<add key="ApiBaseAddress"
value="<a href="https://paultestauthapi.azurewebsites.net/"">"</a> />
<add key="AppKey" value=""/>
</appSettings>
</configuration>
To get these configuration values, navigate to the Configure tab of your AD app.
The Client Id is on the configure tab and if you then select the drop down in the Keys section and click save, you will see an app key/secret generated. This will disappear once you leave this page (but you can generate it again) so copy it now.
Finally, you need the name of your AD tenant. This will be your domain/email address or you can use the GUID found in the Endpoints
option at the bottom of the AD Application page you’re currently on.
Now let's look at the code.
We’ll first see that we construct some credentials to call the app we are getting the bearer token from. In some cases, this can be an AD app that has access to many APIs, in this case we will just use the same app to authenticate with as the app the API is secured against.
ClientCredential clientCredential = ClientCredential(clientId, appKey);
We then ask AD for the token:
AuthenticationResult authResult = authContext.AcquireToken(apiResourceId, clientCredential);
And that single line of code was my big problem I had the wrong resource identifier. When I finally had everything working, I was using the Client ID of the resource I was trying to access so there was no ambiguity of which resource I was talking about. Further down the article, I will explain how I fixed my 401 Unauthorized issue that lead me to start using the resources Client Id.
One we have the token, we need to add that to the header before we make the call to the service.
httpClient.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue(authResult.AccessTokenType, authResult.AccessToken);
The final lines of code call the values controller and when you run the Console App, all being well, you should receive a 200 which shows you’ve successfully called the secured API with the correct authentication.
Well done!
How to Diagnose Authentication Issues
I just wanted to take a moment to explain how I worked out why, for a very long period of time, I was getting a status code of 401.71 Unauthorized.
Turn On Site Logging
Firstly turn on logging.
In the new portal, navigate back to the settings blade of your Web API app. Select “Diagnostics logs” and then configure that blade like this and save. Your site should now log all incoming HTTP calls.
Kudo to the Rescue
If you navigate to https://WhereEverYouPublishedTheAPITo.scm.azurewebsites.net/DebugConsole (notice the scm part of the URL), then you will find yourself in the Kudo Debug console. Then, select the CMD option up in the Debug Console menu, this will take to a site explorer.
Go to the LogFile folder, then Application. In there, you will find log file that show you the HTTP calls coming in and if they were successful, and if not the code and reason why not. Invaluable!
With this help, I was able to work out how to fix my client app.
Hope this will save someone else a little time. :)