Authenticating to Azure AD non-interactively
Solution ·I want to use Azure AD as a user directory but I do not want to use its native web authentication mechanism which requires users to go via an Active Directory page to login (which can be branded and customized to look like my own).
I just want to give a user name & password to an authentication API.
The reason is I want to migrate an existing application currently using an LDAP server and want to change the code as little as possible.
You might have other reasons why you want to do that.
Let’s just say that the online literature HEAVILY leans towards using the web workflow. Even the documentation on native apps recommends that your native app pops the Azure AD web page to authenticate the user. There are good reasons for that as this way your app never touches user credentials and is therefore more secure and your app more trustworthy. So in general, I totally agree with those recommendations.
But in my case, my legacy app is touching the user credentials all over the place. Also this approach is a stepping stone before considering moving to the web workflow.
After a weekend spent scanning the entire web (as a side note I’ve learned there was little Scandinavian men living inside the Earth, which is flat by the way), I finally found Danny Strockis’ article about Authenticating to Azure AD non-interactively using a username & password.
That is it basically. Except the article is a little quick on setup, so I’m gona elaborate here.
I’m going to give a sample in C# using ADAL, but since at the end of the day, the authentication is one HTTP POST, I’ll also give a more bare bone sample using HTTP post if you don’t want or cannot to integrate with ADAL. The samples are quite trivial so you should be able to convert them in the language / platform of your choice.
Conceptually
We basically want our users to interact with our application only, punch in their credentials and have the application check with Azure AD if the credentials are good.
Here the application is represented as a Web app here but it could also be a native application.
In order to do this in the world of Azure AD is to use two Azure AD apps:
- A client app, representing the agent authenticating the user
- A service app, representing the actual application the user is authenticating against
I know it looks weird, it makes your brain hurts and is in great part the reason I’m writing this article, because it isn’t straightforward.
In the authentication parlance, the client App is the client (so far so good) while the service app is the resource, i.e. the thing the user is trying to access: the client is accessing the resource on the user’s behalf.
Setting up Azure AD
Yes, there will be some steps to setup Azure AD.
First we need a tenant. We can use the tenant used by our subscription but typically for those types of scenario we’ll want to have a separate tenant for our end users. This article shows how to create an Azure AD tenant.
We then need a user to test. We can create a user like this:
where, of course, ldapvpldemo is my Azure AD tenant.
We’ll also need to give that user a “permanent” password, so let’s show the password on creation (or reset it afterwards) then let’s go to an InPrivate browsing window and navigate to https://login.microsoftonline.com/. We can then login as the user (e.g. test@ldapvpldemo.onmicrosoft.com) with the said password. We’ll be prompted to change it (e.g. My$uperComplexPassw0rd).
Let’s create the client app. In App Registrations, let’s add an app with:
It would probably work as a Web app / API but a Native App seems more fitting.
We’ll need the Application ID of that application which we can find by looking at the properties of the application.
We then need to create the service app:
We’ll need the App ID URI of the service:
That URI can be changed, either way we need the final value.
We will need to give permission to the client app to access the service app. For that we need to go back to the client app, go to the Required Permissions menu and add a permission. From there, in the search box we can just start to write the name of the service app (e.g. MyLegacyService) and it should appear where we can select it. We then click the Access MyLegacyService box.
Finally, we need to grant the permissions to users.
With all that, we’re ready to authenticate.
ADAL
This sample is in C# / .NET but since ADAL is available on multiple platform (e.g. Java), this should be easy to port.
We’ll create a Console Application project. We need the full .NET Framework as the .NET core version of ADAL doesn’t have the UserPasswordCredential class we’re gona be using.
We need to install the NuGet package Microsoft.IdentityModel.Clients.ActiveDirectory in the project.
private static async Task AdalAuthenticationAsync()
{
// Constants
var tenant = "LdapVplDemo.onmicrosoft.com";
var serviceUri = "https://LdapVplDemo.onmicrosoft.com/d0f883f6-1c32-4a14-a436-0a995a19c39b";
var clientID = "b9faf13d-9258-4142-9a5a-bb9f2f335c2d";
var userName = $"test@{tenant}";
var password = "My$uperComplexPassw0rd1";
// Ceremony
var authority = "https://login.microsoftonline.com/" + tenant;
var authContext = new AuthenticationContext(authority);
var credentials = new UserPasswordCredential(userName, password);
var authResult = await authContext.AcquireTokenAsync(serviceUri, clientID, credentials);
}
We have to make sure we’ve copied the constants in the constant section.
UPDATE (06-09-2017): The name of the constants match the ADAL SDK but doesn’t always match what we see on the portal screens. Here are the constants mapping. The tenant is the name of your AAD tenant appended by .onmicrosoft.com. The serviceUri is the App ID URI we collected above (red box). The clientID is the Application ID we’ve collected above (red box). Finally, user name and password belong to the actual user we want to authenticate.
This should work and authResult should contain a valid access token that we could use as a bearer token in different scenarios.
If we pass a wrong password or wrong user name, we should obtain an error as expected.
HTTP POST
We can use Fiddler or other HTTP sniffing tool to see what ADAL did for us. It is easy enough to replicate.
private static async Task HttpAuthenticationAsync()
{
// Constants
var tenant = "LdapVplDemo.onmicrosoft.com";
var serviceUri = "https://LdapVplDemo.onmicrosoft.com/d0f883f6-1c32-4a14-a436-0a995a19c39b";
var clientID = "b9faf13d-9258-4142-9a5a-bb9f2f335c2d";
var userName = $"test@{tenant}";
var password = "My$uperComplexPassw0rd";
using (var webClient = new WebClient())
{
var requestParameters = new NameValueCollection();
requestParameters.Add("resource", serviceUri);
requestParameters.Add("client_id", clientID);
requestParameters.Add("grant_type", "password");
requestParameters.Add("username", userName);
requestParameters.Add("password", password);
requestParameters.Add("scope", "openid");
var url = $"https://login.microsoftonline.com/{tenant}/oauth2/token";
var responsebytes = await webClient.UploadValuesTaskAsync(url, "POST", requestParameters);
var responsebody = Encoding.UTF8.GetString(responsebytes);
}
}
Basically, we have an HTTP post where all the previous argument are passed in the POST body.
If we pass a wrong password or wrong user name, we should obtain an error as expected. Interestingly, the HTTP code is 400 (i.e. bad request) instead of some unauthorized variant.
Summary
Authenticating on an Azure AD tenant isn’t the most recommended method as it means your application is handling credentials whereas the preferred method delegate to an Azure AD hosted page the handling of those credential so your application only see an access token.
But for a legacy migration for instance, it makes sense. Azure AD definitely is more secure than an LDAP server sitting on a VM.
We’ve seen two ways to perform the authentication. Under the hood they end up being the same. One is using the ADAL library while the other uses bare bone HTTP POST.
Keep in mind, ADAL does perform token caching. If you plan to use it in production, you’ll want to configure the cache properly not to get strange behaviours.
41 responses