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. 🙂


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                = "$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 -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 -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 -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 {

    $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 . Here is how the decoded Client Assertion looks like.

The reference for Client – Assertion Format:

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


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.



&client_assertion= {assertion you received from the above script}

Here is the Postman screen shot:

You can decode the access token using

5 2 votes
Article Rating
Notify of
Newest Most Voted
Inline Feedbacks
View all comments
April 14, 2021 5:45 am

Hi Bhavya.. nice article..
Can this be written in c using CURL library
can you please paste the code generated in POSTMAN for c-CURL here.

June 11, 2021 7:04 am

Hi, wondering why powershell has to be used, and also why script needs pfx, when someone can use pem? Why cant it be proved just using postman

Bac Hoang [MSFT]
June 15, 2021 4:43 am
Reply to  kris

This is just one way to show how this can be achieved. If you know of a different way to accomplish this, please share your input.