Public/Google/Get-GoogleApiAccessToken.ps1

function Get-GoogleApiAccessToken {
    <#
    .SYNOPSIS
    Retrieves an access token from Google API using a service account.
 
    .DESCRIPTION
    This function generates a JWT (JSON Web Token) and exchanges it for an access token from Google API.
    The access token can then be used for authenticated requests to Google services.
 
    .PARAMETER ServiceAccountKeyPath
    PAth to a JSON file containing the service account credentials, including the private key and client email.
    This is the Google credential file downloaded from the Google Cloud Console.
 
    .PARAMETER Scope
    The scope of access requested from Google API (e.g., 'https://www.googleapis.com/auth/drive').
 
    .PARAMETER TargetUserEmail
    The email address of the user for which the application is requesting delegated access.
 
    .EXAMPLE
    $credentialsJson = Get-Content 'C:\path\to\credentials.json' -Raw
    $accessToken = Get-GoogleApiAccessToken -ServiceAccountKeyPath $credentialsJson -Scope 'https://www.googleapis.com/auth/drive.readonly'
     
    Use the $accessToken for authenticated API requests.
 
    .NOTES
    Ensure that the service account has the necessary permissions for the requested scope.
 
    Created by: Sam Cattanach
    Modified: 02/18/2025 09:30:19 AM
    #>


    param (
        [Parameter(Mandatory)]
        [string]$ServiceAccountKeyPath,

        [Parameter(Mandatory)]
        [string]$Scope,

        [Parameter(Mandatory)]
        [string]$TargetUserEmail
    )

    # Convert JSON credentials into a PowerShell object
    $jsonContent = ConvertFrom-Json -InputObject (Get-Content $ServiceAccountKeyPath -Raw)
    $ServiceAccountEmail = $jsonContent.client_email
    $PrivateKey = $jsonContent.private_key -replace '-----BEGIN PRIVATE KEY-----\n' -replace '\n-----END PRIVATE KEY-----\n' -replace '\n'

    # Create JWT Header (Base64-encoded JSON)
    $header = @{
        alg = "RS256"
        typ = "JWT"
    }
    $headerBase64 = [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes(($header | ConvertTo-Json -Compress)))

    # Generate JWT Payload (Claim Set)
    $timestamp = [Math]::Round((Get-Date -UFormat %s))  # Current time in seconds
    $claimSet = @{
        iss   = $ServiceAccountEmail
        scope = $Scope
        aud   = "https://oauth2.googleapis.com/token"
        exp   = $timestamp + 3600  # Token expiration (1 hour)
        iat   = $timestamp         # Issued at time
        sub   = $TargetUserEmail   # Delegated user access
    }
    $claimSetBase64 = [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes(($claimSet | ConvertTo-Json -Compress)))

    # Generate JWT Signature
    $signatureInput = "$headerBase64.$claimSetBase64"
    $signatureBytes = [System.Text.Encoding]::UTF8.GetBytes($signatureInput)
    $privateKeyBytes = [System.Convert]::FromBase64String($PrivateKey)

    # Create RSA provider and import the private key
    $rsaProvider = [System.Security.Cryptography.RSA]::Create()
    $bytesRead = $null
    $rsaProvider.ImportPkcs8PrivateKey($privateKeyBytes, [ref]$bytesRead)

    # Sign the JWT using SHA-256 and PKCS1 padding
    $signature = $rsaProvider.SignData($signatureBytes, [System.Security.Cryptography.HashAlgorithmName]::SHA256, [System.Security.Cryptography.RSASignaturePadding]::Pkcs1)
    $signatureBase64 = [System.Convert]::ToBase64String($signature)

    # Construct the final JWT
    $jwt = "$headerBase64.$claimSetBase64.$signatureBase64"

    # Create request body for token exchange
    $body = @{
        grant_type = "urn:ietf:params:oauth:grant-type:jwt-bearer"
        assertion  = $jwt
    }

    # Request access token from Google OAuth2 API
    try {
        $response = Invoke-RestMethod -Uri "https://oauth2.googleapis.com/token" -Method POST -Body $body -ContentType "application/x-www-form-urlencoded"

        $accessToken = $response.access_token

        $headers = @{
            'Authorization' = "Bearer $accessToken"
            'Accept' = 'application/json'
        }
        
        Write-Log -Message "Successfully retrieved Google Access Token" -Level Trace

        return $headers
    } catch {
        Write-Log -Message "Failed to Retrieve Google Access Token" -Level Error
        Throw $_
    }
}