Skip to content

Instantly share code, notes, and snippets.

@KentNordstrom
Created February 20, 2025 13:15
Show Gist options
  • Save KentNordstrom/9302175ddb4359fbd63f5824797eaf9c to your computer and use it in GitHub Desktop.
Save KentNordstrom/9302175ddb4359fbd63f5824797eaf9c to your computer and use it in GitHub Desktop.
Get a Jwt Access Token using a ServicePrincipal with Certificate
<#
.SYNOPSIS
Functions to get a token using a serviceprincipal and certificate
Please note the tokenScope is "hardcoded" to the Graph $tokenScope = "https://graph.microsoft.com/.default"
if you want to use it for other scopes please adjust.
#>
function Get-JwtAccessToken {
param ($ServicePrincipalId, $TenantId, $ClientCertificate)
$jwt = Get-RS256jwt -ServicePrincipalId $ServicePrincipalId -TenantId $TenantId -ClientCertificate $ClientCertificate
$tokenScope = "https://graph.microsoft.com/.default"
$tokenUrl = "https://login.microsoftonline.com/$TenantId/oauth2/v2.0/token"
$tokenBody = @{
'grant_type' = 'client_credentials'
'client_id' = $ServicePrincipalId
'client_assertion_type' = 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer'
'client_assertion' = $jwt
'scope' = $tokenScope
}
$tokenHeaders = @{
Authorization = "Bearer $jwt"
}
$tokenResponse = Invoke-RestMethod -Uri $tokenUrl -Method POST -Headers $tokenHeaders -Body $tokenBody -ContentType 'application/x-www-form-urlencoded'
$token = $tokenResponse.access_token
return $token
}
function Get-RS256jwt {
param ($ServicePrincipalId, $TenantId, $ClientCertificate)
# Base64url encode the hash
$x5t = [System.Convert]::ToBase64String($ClientCertificate.GetCertHash()) -replace '\+', '-' -replace '/', '_' -replace '='
# Create the header of the JWT
$header = @{
'alg' = 'RS256'
'typ' = 'JWT'
'x5t' = $x5t
}
# Create the payload of the JWT
$now = [DateTimeOffset]::Now.ToUnixTimeSeconds()
$expiry = $now + 3600
$payload = @{
'aud' = "https://login.microsoftonline.com/$TenantId/oauth2/v2.0/token"
'iss' = $ServicePrincipalId
'sub' = $ServicePrincipalId
'jti' = [Guid]::NewGuid().ToString()
'nbf' = $now
'exp' = $expiry
}
# Convert the header and payload to JSON
$headerJson = ConvertTo-Json -InputObject $header -Compress
$payloadJson = ConvertTo-Json -InputObject $payload -Compress
# Base64 encode the header and payload
$headerBase64Url = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($headerJson)) -replace '\+', '-' -replace '/', '_' -replace '='
$payloadBase64Url = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($payloadJson)) -replace '\+', '-' -replace '/', '_' -replace '='
# Create the unsigned JWT
$unsignedJwt = [System.Text.Encoding]::UTF8.GetBytes($headerBase64Url + '.' + $payloadBase64Url)
# Sign the JWT
$signedJwt = [Convert]::ToBase64String($ClientCertificate.PrivateKey.SignData($unsignedJwt, [Security.Cryptography.HashAlgorithmName]::SHA256, [Security.Cryptography.RSASignaturePadding]::Pkcs1)) -replace '\+', '-' -replace '/', '_' -replace '='
$jwt = $headerBase64Url + '.' + $payloadBase64Url + '.' + $signedJwt
return $jwt
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment