If you want to do the whole thing with direct HTTPS requests, this should cover the authentication part:
# Define variables
$tenantId = "tenant.onmicrosoft.com"
$appId = "xxxxxxxxxx"
$thumbprint = "xxxxxxxxxx"
$resource = "https://graph.microsoft.com"
$authUrl = "https://login.microsoftonline.com/$tenantId/oauth2/token"
# Create a certificate object
$cert = Get-Item -Path Cert:\CurrentUser\My\$thumbprint
# Construct the JWT token
$currentTime = [System.DateTime]::UtcNow
$expiryTime = $currentTime.AddMinutes(60) # Token valid for 1 hour
$jwtHeader = @{
alg = "RS256"
typ = "JWT"
x5t = [System.Convert]::ToBase64String($cert.GetCertHash())
}
$jwtPayload = @{
iss = $appId
sub = $appId
aud = $authUrl
nbf = [System.Math]::Floor($currentTime.Subtract((Get-Date "1970-01-01 00:00:00").ToUniversalTime()).TotalSeconds)
exp = [System.Math]::Floor($expiryTime.Subtract((Get-Date "1970-01-01 00:00:00").ToUniversalTime()).TotalSeconds)
}
$jwtHeaderBase64 = [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes(($jwtHeader | ConvertTo-Json)))
$jwtPayloadBase64 = [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes(($jwtPayload | ConvertTo-Json)))
$signatureInput = "$jwtHeaderBase64.$jwtPayloadBase64"
#$rsa = $cert.PublicKey.Key
$rsa = $cert.PrivateKey
$signatureBytes = $rsa.SignData([System.Text.Encoding]::UTF8.GetBytes($signatureInput), [System.Security.Cryptography.HashAlgorithmName]::SHA256, [System.Security.Cryptography.RSASignaturePadding]::Pkcs1)
$jwtSignatureBase64 = [System.Convert]::ToBase64String($signatureBytes)
$jwtToken = "$jwtHeaderBase64.$jwtPayloadBase64.$jwtSignatureBase64"
# Construct the token request payload
$tokenRequest = @{
client_id = $appId
resource = $resource
client_assertion_type = "urn:ietf:params:oauth:client-assertion-type:jwt-bearer"
client_assertion = $jwtToken
grant_type = "client_credentials"
}
# Send a POST request to Azure AD to obtain the access token
$response = Invoke-RestMethod -Uri $authUrl -Method Post -ContentType "application/x-www-form-urlencoded" -Body $tokenRequest
# Extract the access token
$accessToken = $response.access_token
# Print the access token
Write-Host "Access Token: $accessToken"
The certificate must be imported on the local device, and the current user. Adjust the code otherwise ($cert part).
Much easier to get a token via MSAL though :)