Get an Graph Authentication Token with PowerShell – No Modules Needed/HTTP-Only (Certificate-Based)

Most Important – Why Should You Avoid Client Secrets?

Nowadays, it is recommended to avoid using client secrets and instead opt for certificate-based authentication. Client secrets pose a security risk as they are often stored in scripts or environment variables and can be compromised. Certificate-based authentication is more secure as it relies on asymmetric encryption, ensuring that private keys are not directly shared.

Why This Method?

Although the PowerShell module Connect-MGGraph provides an easy way to authenticate with Microsoft Graph, it requires module installation and may not work in all environments. This script performs authentication entirely without additional PowerShell modules, increasing flexibility and portability.

Prerequisites

Before proceeding, ensure you have the following:

  • An Azure AD App Registration with the necessary API permissions.
  • A valid X.509 certificate installed in the certificate store.
  • The Tenant ID, Application (Client) ID, and the Thumbprint of the certificate.

How to create an Azure AD App-Registration will be covered in this Blog-Post.

PowerShell Script for Token Generation

The following script authenticates with Azure AD and retrieves an access token that can be used for API calls.

Define Required Variables

$tenantID = "YOUR TENANT ID HERE"
$appID = "YOUR APP ID HERE"
$thumbprint = "YOUR CERTIFICATE THUMBPRINT HERE"
$resource = "https://graph.microsoft.com"

Token Generation Function

function get_token($TID, $AID, $thumbpr, $ress) {
    $tenantID = $TID
    $appId = $AID
    $thumbprint = $thumbpr
    $resource = $ress
    $authUrl = "https://login.microsoftonline.com/$tenantId/oauth2/token"

    # Retrieve certificate from store
    $cert = Get-Item -Path Cert:\CurrentUser\My\$thumbprint

    # Create 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"

    # Sign token with private 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"

    # Send token request to Azure AD
    $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"
    }

    # Retrieve access token
    $response = Invoke-RestMethod -Uri $authUrl -Method Post -ContentType "application/x-www-form-urlencoded" -Body $tokenRequest

    # Return token
    return $response.access_token
}

Execute Function

$Key = get_token -TID $tenantID -AID $appID -thumbpr $thumbprint -ress $resource
Write-Host $Key

Script Explanation

  1. Retrieve Certificate: The script loads the certificate from the certificate store using its thumbprint.
  2. Create JWT Token: A JSON Web Token (JWT) is generated with the required claims.
  3. Sign Token: The JWT is signed using the private key of the certificate.
  4. Request Token: The signed JWT is sent to Azure AD to obtain an access token.
  5. Output Token: The script returns the access token, which can be used for API authentication.

Using the Access Token

Once you have the token, you can use it for API requests, such as:

$headers = @{ Authorization = "Bearer $Key" }
$response = Invoke-RestMethod -Uri "https://graph.microsoft.com/v1.0/me" -Headers $headers -Method Get
$response

Conclusion

Certificate-based authentication significantly enhances security by eliminating the need to store static secrets. This PowerShell script enables full authentication without requiring additional modules like Connect-MGGraph, offering greater flexibility and portability.

Leave a Reply

Your email address will not be published. Required fields are marked *