Authenticating With Azure AD Graph API Using A Client Certificate

Lately I have been looking at authenticating to Azure AD without having to rely an a 'shared' secret. That is, for the most part, how the code samples about Azure AD are crafted, there is usually a step to generate an application secret and then paste it in a configuration file. This secret key is needed to get a token from Azure AD at runtime and authenticate, to something like a Web API, and retrieve data securely.
This approach is perfectly fine to get things working, but if you want a higher level of security in your production applications then obtaining an Azure AD token using a certificate would be a better option. In order to accomplish this task, we will need to setup a few things before Azure AD can utilize our certificate for authentication. Let's look at the high level steps we need to make in order to accomplish this.

Authenticate To Azure

In order to accomplish some of the setup steps, you will have to first authenticate with Azure using PowerShell. Simply launch a new PowerShell Administrator session and issue this command:

Login-AzureRmAccount

You will be prompted for your Azure account credentials with a new authentication window, once you are logged into your Azure account you should be able to utilize the Azure Cmdlets. Also, I am using the Azure AD PowerShell V2 Cmdlets, so make sure you have that version installed before getting started: https://azure.microsoft.com/en-us/updates/azure-ad-new-powershell-cmdlets-preview/

Login Azure Resource Manager Account

Obtain A Test Certificate

For our sample testing purposes we will use a self-signed certificate. In Windows, you have a couple native options to generate a self-signed certificate for testing purposes. The first option is by using the makecert.exe tool to generate a certificate and then import it into the certificate store. You essentially just launch a new command line console and issue a command like this:

makecert -r -pe -n "CN=myauthapp.oncloudapp.net" -ss My -len 2048 myAuthCertificate.cer -sv myAuthCertificatePrivateKey.pvk

The previous command creates a self-signed certificate with an exportable private key, a subject name of 'myauthapp.oncloudapp.net', a length of 2048 bits and it also creates a file to hold the private key (myAuthCertificatePrivateKey.pvk).

Once that is done, you need to generate the .pfx file, which will contain the public/private key pair, and then import it into your machine's certificate store. This is the command to generate the Personal Information Exchange (.pfx) file:
pvk2pfx.exe -pvk myAuthCertificatePrivateKey.pvk -spc myAuthCertificate.cer -pfx MyPFXAuthCertificate.pfx

However, while this works fine, there are a few caveats with this approach. If you are using Windows 10 then the makecert.exe tool is not installed by default. So you will have to download the Windows 10 SDK and select the Windows Software Development Kit option. And then follow the previous steps to generate the certificate and after that import it into the certificate store. What is more, the makecert.exe is considered deprecated according to its documentation. In any case, that's quite a bit of setup before you can get started with generating a self-signed certificate. The other option is to use PowerShell, version 4 and above, to generate a self-signed certificate for testing. Note that this only works in Windows 8.1 and above, so if you are still using Windows 7 then you have to use the makecert.exe approach. Here is the PowerShell command to generate a test certificate:

$certificate = New-SelfSignedCertificate -CertStoreLocation Cert:\LocalMachine\My -DnsName myauthapp.oncloudapp.net

This snippet will create a self-signed certificate in the Personal folder of the Local Computer certificate store. Also, the certificate will already contain the public/private key pair with it, so you won't need to do any import/exports in order to have a local web application authenticate against Azure AD using a certificate. It is important to note that the certificate you use in your store should contain the private key since the Azure AD authentication mechanism will be expecting it. Your new self-signed certificate should look similar to this:

self-signed certificate

Associate The Certificate With An Azure AD Application Registration

The next step is to associate the newly minted self-signed certificate in your local machine store, with a new Azure AD application registration. We can accomplish this via PowerShell also:

$azureAccount = Login-AzureRmAccount $certificate = New-SelfSignedCertificate -CertStoreLocation Cert:\LocalMachine\My -DnsName myauthapp.oncloudapp.net -FriendlyName MyTestAuthCertificate $base64Value = [System.Convert]::ToBase64String($certificate.GetRawCertData()) $adapp = New-AzureRmADApplication -DisplayName "MyAzureAuthApp" -HomePage "https://localhost:44361/" -IdentifierUris "https://YOURDOMAIN.onmicrosoft.com/MyAuthApp" -ReplyUrls "https://localhost:44361/" -CertValue $base64Value -StartDate $certificate.NotBefore -EndDate $certificate.NotAfter

The snippet first gets the certificate data from the previously created certificate, then creates a new Azure AD Application Registration and appends the certificate data to it.

Configuring Application Settings

If you are calling the Azure AD Graph from a web application, then you can simply add the configuration settings to the web.config as such:

<appSettings> <add key="ida:ClientId" value="{The Application Id e.g. 2e32a2b1-6612-4fad-abd0-35ce9d60e216" /> <add key="ida:AADInstance" value="https://login.microsoftonline.com/" /> <add key="ida:Domain" value="YOURDOMAIN.onmicrosoft.com" /> <add key="ida:TenantId" value="53a2cb9b-cbe5-4584-9f8e-08bf171ac0e2" /> <add key="ida:PostLogoutRedirectUri" value="https://localhost:44361/" /> <add key="AuthCertThumbprint" value="24BC6CE52319E969339094C4B200FA5F05451A12" /> </appSettings> You can find all of these settings in the Azure portal, under your new application registration. The last setting, the certificate thumbprint, can be retrieved by accessing the $certificate.Thumbprint property from the earlier PowerShell script. Or you can simply access the mmc.exe application and locate the certificate in the store and copy the thumbprint from the settings. Although, just a word of caution, there is a bug in the MMC console that causes a hidden character to be copied, rendering the certificate thumbprint invalid; the easy fix is just to make sure that you delete any leading characters from the thumbprint. See this StackOverflow answer for more details: http://stackoverflow.com/questions/11115511/how-to-find-certificate-by-its-thumbprint-in-c-sharp

The Authentication Code

You need to first pull the settings from the configuration file, this is pretty straight forward as they are simply a set of variables:

private string clientId = ConfigurationManager.AppSettings["ida:ClientId"]; private string aadInstance = ConfigurationManager.AppSettings["ida:AADInstance"]; private string graphResourceID = "https://graph.windows.net"; private string certificateThumbprint = ConfigurationManager.AppSettings["AuthCertThumbprint"];

Next, you will have to extract the certificate from the local certificate store. You can use something as simple as this code just as an example, in the real world you will want to have better error handling and certificate chaining security checks:

public static class AuthHelper { public static X509Certificate2 FindCertificateByThumbprint(string certificateThumbprint) { X509Certificate2 certificate = null; X509Store store = new X509Store(StoreLocation.LocalMachine); try { store.Open(OpenFlags.ReadOnly); X509Certificate2Collection certificateCollection = store.Certificates; X509Certificate2Collection currentCertificates = certificateCollection.Find(X509FindType.FindByTimeValid, DateTime.Now, false); X509Certificate2Collection signingCert = currentCertificates.Find(X509FindType.FindByThumbprint, certificateThumbprint, false); if (signingCert.Count == 0) { // No matching certificate found. return certificate; } // Return the first certificate in the collection, has the right name and is current. return certificate = signingCert[0]; } finally { store.Close(); } } }

Now, here is the sample code that you will need to get an Azure AD token, while authenticating using the self-signed certificate:

public async Task<string> GetTokenForApplication() { string signedInUserID = ClaimsPrincipal.Current.FindFirst(ClaimTypes.NameIdentifier).Value; string tenantID = ClaimsPrincipal.Current.FindFirst("http://schemas.microsoft.com/identity/claims/tenantid").Value; string userObjectID = ClaimsPrincipal.Current.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier").Value;
X509Certificate2 certificate = AuthHelper.FindCertificateByThumbprint(certificateThumbprint); ClientAssertionCertificate assertionCertificate = new ClientAssertionCertificate(clientId, certificate); AuthenticationContext authenticationContext = new AuthenticationContext(aadInstance + tenantID, new ADALTokenCache(signedInUserID)); AuthenticationResult authenticationResult = await authenticationContext.AcquireTokenSilentAsync(graphResourceID, assertionCertificate, new UserIdentifier(userObjectID, UserIdentifierType.UniqueId)); return authenticationResult.AccessToken; }

Once you have your token acquisition logic, you can use that to call the Azure AD graph like this sample snippet:

public async Task<ActionResult> Index() { string tenantID = ClaimsPrincipal.Current.FindFirst("http://schemas.microsoft.com/identity/claims/tenantid").Value; string userObjectID = ClaimsPrincipal.Current.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier").Value; try { Uri servicePointUri = new Uri(graphResourceID); Uri serviceRoot = new Uri(servicePointUri, tenantID); ActiveDirectoryClient activeDirectoryClient = new ActiveDirectoryClient(serviceRoot, async () => await GetTokenForApplication());
var result = await activeDirectoryClient.Users .Where(u => u.ObjectId.Equals(userObjectID)) .ExecuteAsync(); IUser user = result.CurrentPage.ToList().First(); return View(user); } catch (AdalException) { // Return to error page. return View("Error"); } // if the above failed, the user needs to explicitly re-authenticate for the app to obtain the required token catch (Exception) { return View("Relogin"); } }

In the code snippet above you create an instance of the ActiveDirectoryClient and then use the GetTokenForApplication() method to obtain a token authenticated with the certificate associated with the Azure AD Application.

Get Token

This renders the following result:

Azure AD Graph Results

The result is the same as if you were using the shared secret key, however, the certificate route provides a higher level of segregation of duties by allowing an admin to install and manage the certificate.

Conclusion

This post was a bit packed with a lot of information, and it is really meant to be high level. On this post, I briefly touched on how to authenticate using certificates with the Azure AD Graph, which is not completely the same as the Microsoft Graph. That is because, as far as I have seen, there is no clear path for doing certificate authentication with the Microsoft Graph. I may be wrong, and if I find out otherwise I will be sure to blog about it, but that's just what I know at this time. In any case, the ability to authenticate to Azure AD with certificates is very useful and it offers a higher level of security for building secure applications. Especially when connecting to a secure service like Key Vault, but that is a topic for another day. Thus, stay tuned for future posts where I will dive deeper into the topic of certificate authentication.

Until next time, happy coding. :)