Powershell code to implement Client Credential authentication using Self-Signed Certificate

Viewed 29

Can anyone help me out troubleshooting this script as to why doesn't return the $TokenResponse? I was able to use secret key and user auth but can't find a way to auth using the self-signed cert which is the most secure option of all. In Azure, permissions are assigned to Application and work fine with said secret key

#Define Client Variables Here
########################
$TenantName = "contoso.com"
$AppId = "11111-2222-3333-4444-5555"
$Certificate = Get-Item Cert:LocalMachine\My\1234567890987654321
$Scope = "https://graph.microsoft.com/.default"
 
#Create base64 hash of certificate
##################################
$CertificateBase64Hash = [System.Convert]::ToBase64String($Certificate.GetCertHash())
 
#Create JWT timestamp for expiration
####################################
$StartDate = (Get-Date "1970-01-01T00:00:00Z" ).ToUniversalTime()
$JWTExpirationTimeSpan = (New-TimeSpan -Start $StartDate -End (Get-Date).ToUniversalTime().AddMinutes(2)).TotalSeconds
$JWTExpiration = [math]::Round($JWTExpirationTimeSpan,0)
 
#Create JWT validity start timestamp
####################################
$NotBeforeExpirationTimeSpan = (New-TimeSpan -Start $StartDate -End ((Get-Date).ToUniversalTime())).TotalSeconds
$NotBefore = [math]::Round($NotBeforeExpirationTimeSpan,0)
 
#Create JWT header
#################
$JWTHeader = @{
    alg = "RS256"
    typ = "JWT"
    x5t = $CertificateBase64Hash -replace '\+','-' -replace '/','_' -replace '='
}
 
#Create JWT payload
#################
$JWTPayLoad = @{
       aud = "https://login.microsoftonline.com/$TenantName/oauth2/token"
       exp = $JWTExpiration
       iss = $AppId
       jti = [guid]::NewGuid()
       nbf = $NotBefore
       sub = $AppId
}
 
# Convert header and payload to base64
#################################
$JWTHeaderToByte = [System.Text.Encoding]::UTF8.GetBytes(($JWTHeader | ConvertTo-Json))
$EncodedHeader = [System.Convert]::ToBase64String($JWTHeaderToByte)
$JWTPayLoadToByte =  [System.Text.Encoding]::UTF8.GetBytes(($JWTPayload | ConvertTo-Json))
$EncodedPayload = [System.Convert]::ToBase64String($JWTPayLoadToByte)
 
# Join header and Payload with "." to create a valid (unsigned) JWT
######################################################
$JWT = $EncodedHeader + "." + $EncodedPayload
 
# Get the private key object of your certificate
######################################
$PrivateKey = $Certificate.PrivateKey
 
# Define RSA signature and hashing algorithm
#####################################
$RSAPadding = [Security.Cryptography.RSASignaturePadding]::Pkcs1
$HashAlgorithm = [Security.Cryptography.HashAlgorithmName]::SHA256
 
# Create a signature of the JWT
#########################
$Signature = [Convert]::ToBase64String(
    $PrivateKey.SignData([System.Text.Encoding]::UTF8.GetBytes($JWT),$HashAlgorithm,$RSAPadding)
) -replace '\+','-' -replace '/','_' -replace '='
 
# Join the signature to the JWT with "."
###############################
$JWT = $JWT + "." + $Signature
 
# Use the self-generated JWT as Authorization to get the Access Token
##########################################################
$Header = @{
    Authorization = "Bearer $JWT"
}
 
$Body = @{
    client_id = $AppId
    client_assertion = $JWT
    client_assertion_type = "urn:ietf:params:oauth:client-assertion-type:jwt-bearer"
    scope = $Scope
    grant_type = "client_credentials"
}
 
#$authUri = "https://login.microsoftonline.com/common/oauth2/token"
$authUri = "https://login.microsoftonline.com/common/oauth2/nativeclient"
 
$TokenResponse = Invoke-RestMethod -Header $Header -Uri $authUri -Method POST -Body $Body
$TokenResponse

When I run it as Admin, I simply get an empty $TokenResponse

Any help will be highly appreciated.

Thanks!

1 Answers

Sorted by replacing the AUD and payload token with this URL:

https://login.microsoftonline.com/$TenantName/oauth2/v2.0/token
Related