2 min read

Client Credentials - Client Certificate

Client Credentials - Client Certificate
Photo by Two Paddles Axe and Leatherwork / Unsplash

Introduction

The process is quite similar to the client secret flow described here, so be sure to take a look! The challenge here lies in generating a JWT (JSON Web Token) based on a certificate. I will provide a detailed explanation of how the JWT is generated and exchanged for an access token below.

Flow Details

For more information about this flow be sure to check the previous article. However in short:

  • 1 step to exchange JWT for an access token
  • Non-interactive
  • App Registration has to be created (can't use first-party MS apps)

Certificate Generation

First, we need a certificate public and private key pair for this to work. Below you can see a small script that generates a self-signed certificate pair inside your user certificate store. Next, we can export the public and private keys using the commands below:

		
Code loading...
      	
    
Show on Github

The private key will be stored in a secrets management solution like Azure Key Vault and the public key will be uploaded in your app registration.

Code Explanation

A new script has been developed to demonstrate the complex process of generating a JWT token using a private key and then exchanging that token for an access token. Although there are only 2 functions, one of them involves several sequential steps, which are detailed below:

  • PowershellClientCredentialsLogin: This function expects the private key formatted as the following type:
    "System.Security.Cryptography.X509Certificates.X509Certificate2"
    You can see in the lower portion how to retrieve that certificate from Azure Key Vault or your local certificate store using Powershell. Next, we have to generate that JWT, because the private key should NEVER be uploaded to Entra ID! When calling the /token endpoint, don't forget to set these properties:
    • client_assertion_type = "urn:ietf:params:oauth:client-assertion-type:jwt-bearer"
    • client_assertion = "{JWT TOKEN}"
    • grant_type = "client_credentials"
  • JWTTokenRetrieval: This function generates the JWT based on the X509 formatted private key provided by the previous function. Then we take the following steps:
    1. Generate the headers defining the key hash and hashing algorithm.
    2. Generate a payload containing client id, start and end date, audience and a random GUID for good measure
    3. Then we Base64 both the headers and payload and join them together with the "." as a separator, this will be the raw package
    4. Then we sign the raw package with the private key using SHA512 which gives us the signed package
    5. Last we join the headers and payload from step c and the signed package from step d using again the "." as a separator which gives us the JWT!

Code

		
Code loading...
      	
    
Show on Github

Conclusion

I'm happy to be able to understand this authentication flow and it might prove useful as the Connect-MgGraph doesn't support certificates being fed via a variable. The private key always has to be in your less secure certificate store.