Created
February 20, 2025 13:15
-
-
Save KentNordstrom/9302175ddb4359fbd63f5824797eaf9c to your computer and use it in GitHub Desktop.
Get a Jwt Access Token using a ServicePrincipal with Certificate
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<# | |
.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