powershelloauth-2.0rsagoogle-play-services

Google Identity Platform: Using OAuth 2.0 in Powershell using Firebase Admin SDK private key


Attempting to implement Firebase Admin SDK service account access using Powershell HTTP/REST and following this tutorial (there's no handy API in Powershell);

Using OAuth 2.0 for Server to Server Applications

Forming the JWT header and JWT claim set are straightforward enough and I can reproduce the examples in the tutorial, however this is where it gets tricky;

Sign the UTF-8 representation of the input using SHA256withRSA (also known as RSASSA-PKCS1-V1_5-SIGN with the SHA-256 hash function) with the private key obtained from the Google API Console. The output will be a byte array. The signature must then be Base64url encoded

RESOLVED 28 JUNE 2023, JOB DONE (5 years later)

There are two Powershell libraries available on JSON Web Token Libraries that work just great creating the required signatures for Google server to server services.


Solution

  • RESOLVED 28 JUNE 2023, JOB DONE (5 years later)

    There are two excellent Powershell libraries available on JSON Web Token Libraries that work just great creating the required signatures for Google server to server services. Simply include a library in your Powershell script then generate your JWT as below;

       $headerTable = [ordered]@{
           'alg' = 'RS256'
           'typ' = 'JWT'
       }
    
       $headerjson = $headerTable | ConvertTo-Json -Compress;
    
       # Setup of Payload claims
       $now = (Get-Date).ToUniversalTime()
       $createDate = [Math]::Floor([decimal](Get-Date($now) -UFormat "%s"))
       $expiryDate = [Math]::Floor([decimal](Get-Date($now.AddHours(1)) -UFormat "%s"))
       $payloadTable = [ordered]@{
           'iss' = $jsonfile.client_email
           'scope' = $Scope
           'aud' = $jsonfile.token_uri
           'exp' = $expiryDate
           'iat' = $createDate
       }
    
       $payloadjson = $payloadTable | ConvertTo-Json -Compress;
       $headerBase64 = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($headerjson)).Split('=')[0].Replace('+', '-').Replace('/', '_')
       $payloadBase64 = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($payloadjson)).Split('=')[0].Replace('+', '-').Replace('/', '_')
       $ToBeSigned = $headerBase64 + "." + $payloadBase64;
       $signature = Get-SignatureRS "RS256" $rsaPrivateKey $ToBeSigned;
       $jwt = ($ToBeSigned + "." + $signature);
       $requestUri = $jsonfile.token_uri;
       $method = 'POST';
       $grant_type = [System.Web.HttpUtility]::UrlEncode("urn:ietf:params:oauth:grant-type:jwt-bearer");
       $body = ("grant_type=" + $grant_type + "&assertion=" + $jwt);
    
       try
         { 
           $encodedbody = [System.Text.Encoding]::UTF8.GetBytes($body)
    
           $JWTRequest = [System.Net.WebRequest]::Create($requestUri)
           $JWTRequest.Method = "Post"
           $JWTRequest.ContentType = "application/x-www-form-urlencoded"
           $JWTRequest.ContentLength = $encodedbody.length
    
           $requestStream = $JWTRequest.GetRequestStream()
           $requestStream.Write($encodedbody, 0, $encodedbody.length)
           $requestStream.Close()
    
           [System.Net.WebResponse] $JWTresponse = $JWTRequest.GetResponse();
           if($JWTresponse -ne $null) 
               {
                   $JWTstatus = ($JWTresponse.StatusCode.value__).ToString().Trim();
                   $JWTstatusDescription = ($JWTresponse.StatusDescription).ToString().Trim();
                   $JWTrstream = $JWTresponse.GetResponseStream();
                   [System.IO.StreamReader] $JWTsread = New-Object System.IO.StreamReader -argumentList $JWTrstream;
                   $JWTstatusJsonContent = $JWTsread.ReadToEnd();
    
                   }
           }
    
           etc...
    

    This has served us well for the last 12 months