Microsoft Graph using MSAL with PowerShell and Certificate Authentication

Close to 3 years ago I authored this post on using PowerShell for Certificate based Azure AD Authentication using ADAL (Active Directory Authentication Library). More recently I’ve published posts on using MSAL (MicroSoft Authentication Library) with PowerShell for Application and Delegated integration with the Microsoft Graph. MSAL is well on the way to replacing ADAL. so we need to update our ADAL based integration automations to use MSAL with PowerShell and Certificate Authentication.

NOTE: Microsoft recently updated their documentation. The FAQ here states that ADAL will not be receiving any new functionality as of 30 June 2020. It’s time to ditch ADAL and use MSAL.

In this post I update my post from 3 years ago to show how to use MSAL with PowerShell and Certificate Authentication to an Azure AD Registered Application to leverage Application based API permissions. The certificate replaces the client secret during authentication in a client credentials authentication flow.

The process is very similar to the ADAL method;

  • generate a self signed certificate
  • add the certificate to the Azure AD Registered Application
  • leverage the MSAL.NET library (via the MSAL.PS PowerShell Module) to authenticate to Microsoft Graph
  • use the Access Token for Microsoft Graph queries using PowerShell

You will need the MSAL.PS PowerShell Module installed. You can do that quickly from an Administrative PowerShell 5.1+ session using the following command:

Install-Module -name MSAL.PS -Force -AcceptLicense

This post also assumes you have an existing Azure AD Registered App that you will add a certificate to. If not register one in Azure AD Blade of the Azure Portal.

Generate a Self Signed Certificate

From an Administrative PowerShell session use the following script to generate a Self Signed 10 year certificate. Update the $certPassword, $certName,  $certPath and $certFileName variables for your Certificate Name, the password that will be associated with the Certificate when we export it from the Certificate Store, and where and what the certificate file will be named. Don’t use an extension for the file name.

$certPassword = "P@SSW0rd1"
$certName = "pwsh.darrenjrobinson.com"
$certPath = "C:\Users\darrenjrobinson\Certificates\SelfSigned\"
$certFileName = "AADAppCert"

$Params = @{
   "DnsName" = @($certName)
   "Subject" = "CN=$($certName)"
   "CertStoreLocation" = "Cert:\LocalMachine\My"
   "NotAfter" = (Get-Date).AddYears(10)
   "KeyAlgorithm" = "RSA"
   "KeyLength" = "2048"
}

$myCert = $null 
$myCert = New-SelfSignedCertificate @Params
$thumbPrint = $myCert.Thumbprint

The Certificate will be created in the Local Computer Certificate Store. Using the MMC Certificates snap-in connected to the Local Computer we can locate our new Self Signed Certificate under Certificates => Personal => Certificates.

Certificate Manager Self Signed Certificate

Export the Certificate

Using PowerShell we will now export our self signed certificate from the local computer certificate store and password protect it.

# Password to be set on the exported Certificate
$pwd = ConvertTo-SecureString -String $certPassword -Force -AsPlainText

# Export the Certificate
Export-PfxCertificate -cert "cert:\LocalMachine\My\$($thumbPrint)" -FilePath "$($certPath)$($certFileName).pfx" -Password $pwd

Obtain the Raw Certificate Data

From the Exported Certificate will will extract the certificate data and output it to another file in a format for upload to an Azure AD Registered App (with the .cer extension).

$x509cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate("$($certPath)$($certFileName).pfx", (ConvertTo-SecureString -String $certPassword -Force -AsPlainText))
$keyValue = [System.Convert]::ToBase64String($x509cert.GetRawCertData())
$keyValue | out-file "$($certPath)$($certFileName).cer"

Inspect the Certificate

Using my x509Details PowerShell Module we can easily inspect the Raw Certificate details to verify we have the correct certificate.

Get-X509Details $keyValue

x509 Certificate Details

Upload the Certificate to the Azure Registered App

From the ‘Certificates and secrets‘ menu item of your Azure AD Registered App, select ‘Upload Certificate‘, locate the exported certification on your local computer and select Add.

Upload Certificate to the Azure AD Registered Application

The Certificate will then be present on the registered Azure AD Application and valid for as an authentication secret.

Certificate Configured on the Azure AD Registered Application

Use MSAL.PS and Get-MsalToken with the Self Signed Certificate to get an Access Token

We can now use the MSAL.PS PowerShell Module with our certificate to authenticate to Microsoft Graph using our registered Azure AD Application.

Import-Module MSAL.PS
$clientID = 'yourAADRegisteredAppClientID'
$tenantID = 'yourAADTenantID'
$ClientCertificate = Get-Item "Cert:\LocalMachine\My\$($thumbPrint)"
$myAccessToken = Get-MsalToken -ClientId $clientID -TenantId $tenantID -ClientCertificate $ClientCertificate
# Inspect the Access Token using JWTDetails PowerShell Module
$myAccessToken.AccessToken | Get-JWTDetails

JWT Access Token from Client Credentials Certificate Based Authentication

MSAL with PowerShell and Certificate Authentication – Using the Access Token

We can simply use our Access Token in the header of an Invoke-RestMethod request to the Microsoft Graph API as shown below to return a page of results for Azure AD Users and find those that contain ‘darren’ in the displayName attribute.

$users = (Invoke-RestMethod -Headers @{Authorization = "Bearer $($myAccessToken.AccessToken)" } `
  -Uri "https://graph.microsoft.com/beta/users" `
  -Method Get).value
$users | Select-Object | Where-Object {$_.displayName -like "*darren*"}

MSAL with PowerShell and Certificate Authentication - Microsoft Graph Users Query using Certificate based Authentication

Refreshing the Access Token

To obtain a new Access Token we simply use Get-MsalToken with the same options as previously and if our existing token is close to expiring the refresh token will be used to obtain a new Access and Refresh Token.

$myAccessToken = Get-MsalToken -ClientId $clientID -TenantId $tenantID -ClientCertificate $ClientCertificate

To force a refresh of our Access and Refresh Tokens we simply add the -ForceRefresh switch to the Get-MsalToken cmdlet.

$myAccessToken = Get-MsalToken -ClientId $clientID -TenantId $tenantID -ClientCertificate $ClientCertificate -ForceRefresh

Summary

Using the MSAL.PS PowerShell Module we can quickly obtain an Azure AD Access Token with Application Permissions using a Self Signed Certificate and the Client Credentials flow, and then silently refresh our Access Token leveraging the MSAL.PS Module, the MSAL.NET library and the token cache.