This post will demonstrate a couple of things:

  1. How to create a signed jwt token (aka Client Assertion) using Powershell.
  2. How to use this generated Client Assertion in Postman to get an Access Token Using Client Credentials Grant Flow.

To get an Access Token using Client-Credentials Flow, we can either use a Secret or a Certificate. This post will use a self-signed certificate to create the client assertion using both the nuget packages Microsoft.IdentityModel.Tokens and MIcrosoft.IdentityModel.JsonWebTokens. The idea in this blog is borrowed from the documentation Generating proof of possession tokens for rolling keys. Thanks to my team members Ray Held and Bac Hoang for assisting me with this sample. 🙂

Pre-requisites

To run the script in this blog you should have the following:

  1. Application created on the Azure portal under App registrations Blade.
  2. Assign Application level permissions under Microsoft Graph resource. Make sure you give the admin consent.
  3. Signing certificate

Here is the reference for Creating a self-signed Certificate. By running the Powershell script given in this reference, you will have the private key pfx and public key cer files created in the specified folder. Next you will need to upload that .cer file in the App registrations, like shown below.

Once you upload the certificate this is how it looks:

1. Create a signed jwt token (aka Client Assertion) using Powershell.

About few Objects used in this PowerShell script:

  • $x509cert = This will give the certificate from the .pfx file. You will need the full path to the .pfx file and the password for the pfx.
  • $signingCredentials = Represents an X.509 token used as the signing credential.
  • $securityTokenDescriptor = Place holder for all the attributes related to the issued token.

Note: This PowerShell script may not work in PS core environment.

$ClientID           = "XXXX" # Application/Client id used for the cert name 
$TenantID      = "XXXX" # tenant id is used for the cert name 
$CertPassWord       = "XXXX" # Password used for creating the certificate
$aud                = "https://login.microsoftonline.com/$TenantID/v2.0/"
$CertificatePath_Pfx           = "Get the path to the .pfx file" # Path where the certificate is saved

Function JsonWeb-Libraries{

    if ( ! (Get-ChildItem $HOME/IdentityModel/lib/Microsoft.IdentityModel.Logging.* -erroraction ignore) ) {
        install-package -Source nuget.org -ProviderName nuget -SkipDependencies Microsoft.IdentityModel.Logging -Destination $HOME/IdentityModel/lib -force -forcebootstrap | out-null
    }
    [System.Reflection.Assembly]::LoadFrom((Get-ChildItem $HOME/IdentityModel/lib/Microsoft.IdentityModel.Logging.*/lib/net45/Microsoft.IdentityModel.Logging.dll).fullname) | out-null
    if ( ! (Get-ChildItem $HOME/IdentityModel/lib/Microsoft.IdentityModel.Tokens.* -erroraction ignore) ) {
        install-package -Source nuget.org -ProviderName nuget -SkipDependencies Microsoft.IdentityModel.Tokens -Destination $HOME/IdentityModel/lib -force -forcebootstrap | out-null
    }
    [System.Reflection.Assembly]::LoadFrom((Get-ChildItem $HOME/IdentityModel/lib/Microsoft.IdentityModel.Tokens.*/lib/net45/Microsoft.IdentityModel.Tokens.dll).fullname) | out-null

    if ( ! (Get-ChildItem $HOME/IdentityModel/lib/Microsoft.IdentityModel.JsonWebTokens.* -erroraction ignore) ) {
        install-package -Source nuget.org -ProviderName nuget -SkipDependencies Microsoft.IdentityModel.JsonWebTokens -Destination $HOME/IdentityModel/lib -force -forcebootstrap | out-null
    }
    [System.Reflection.Assembly]::LoadFrom((Get-ChildItem $HOME/IdentityModel/lib/Microsoft.IdentityModel.JsonWebTokens.*/lib/net45/Microsoft.IdentityModel.JsonWebTokens.dll).fullname) | out-null
}
  
Function Get-ClientAssertion {

    JsonWeb-Libraries
    $x509cert = [System.Security.Cryptography.X509Certificates.X509Certificate2]::new($CertificatePath_Pfx, $CertPassWord)
    $claims = new-object 'System.Collections.Generic.Dictionary[String, Object]'
    $claims['aud'] = $aud
    $claims['iss' ] = $ClientId
    $claims['sub'] = $ClientId
    $claims['jti'] = [GUID]::NewGuid().ToString('D')
                   
    $signingCredentials = [Microsoft.IdentityModel.Tokens.X509SigningCredentials]::new($x509cert)
    $securityTokenDescriptor = [Microsoft.IdentityModel.Tokens.SecurityTokenDescriptor]::new()
    $securityTokenDescriptor.Claims = $claims
    $securityTokenDescriptor.SigningCredentials = $signingCredentials 

    $tokenHandler = [Microsoft.IdentityModel.JsonWebTokens.JsonWebTokenHandler]::new()
    $clientAssertion = $tokenHandler.createToken($securityTokenDescriptor)
    write-host $clientAssertion
}  
$myvar = Get-ClientAssertion

Once you get the Client-Assertion , you can decode it using jwt.ms . Here is how the decoded Client Assertion looks like.

The reference for Client – Assertion Format: https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-certificate-credentials

2. Use this generated Client Assertion in Postman to get an Access Token Using Client Credentials Grant Flow.

Reference: https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-client-creds-grant-flow#second-case-access-token-request-with-a-certificate

Use the TenantID and ClientID which are used while running the powershell script. And For client_assertion parameter, use the output from the PowerShell script.

POST: https://login.microsoftonline.com/{TenantID}/oauth2/v2.0/token

BODY:

scope=https://graph.microsoft.com/.default
&client_id={ClientID}
&client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer
&client_assertion= {assertion you received from the above script}
&grant_type=client_credentials

Here is the Postman screen shot:

You can decode the access token using jwt.ms.

5 1 vote
Article Rating
Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments