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
This my relevant code snippet (credit to @Will_Nevis to give me a clue)
...
$jws = "$encodedheader.$encodedclaim";
$encodedjws = [System.Text.Encoding]::UTF8.GetBytes($jws);
$rsaobj = New-Object System.Security.Cryptography.RSACryptoServiceProvider;
$rsaobj.FromXmlString($rsaobj.ToXmlString($jsonfile.private_key));
$sha256OID = [System.Security.Cryptography.CryptoConfig]::MapNameToOID("SHA256");
$signature = $rsaobj.SignData($encodedjws, $sha256OID);
$encodedsignature = [System.Convert]::ToBase64String($signature);
$encodedsignature = $encodedsignature.Split('=')[0]
$encodedsignature = $encodedsignature.Replace('+', '-')
$encodedsignature = $encodedsignature.Replace('/', '_')
$jwt = "$encodedheader.$encodedclaim.$encodedsignature"
...
It generates an impressive looking signature, shorter than the tutorial example although I have no idea if it's right. It doesn't actually work (400) Bad Request.
Is the above code just wrong or is there another way to do it in Powershell..?
I guess it's essential to generate the signature correctly.
Appreciate the question has been asked before, but no-one seems to have it "nailed"
Below is an sample of the Json private key file generated for my Firebase Admin SDK service account. This is all that is required to generate the token;
{
"type": "service_account",
"project_id": "abc-xyz",
"private_key_id": "nL9zQkSB1GXwF49MNcaEgCp9i7BmFr9JCBKYwgg",
"private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvADANB_massive_key... 03i8lG\nrlsDWw==\n-----END PRIVATE KEY-----\n",
"client_email": "firebase-adminsdk-fuhti@abc-xyz.iam.gserviceaccount.com",
"client_id": "156461654611800605490",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://accounts.google.com/o/oauth2/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/firebase-adminsdk-fuhti%40abc-xyz.iam.gserviceaccount.com"
}
Test scenario
([system.security.cryptography.rsacryptograpgyserviceprovider]::create(2048)).toxmlstring($true)
yields this piece of XML (values truncated for readability) "D" is the privateExponent (private_key?) apparently
<RSAKeyValue>
<Modulus>2htVi ... Fay9qQ==</Modulus>
<Exponent>AQAB</Exponent>
<P>4dOudM8 ... t0JM=</P>
<Q>9z+W6qw ... p+KlM=</Q>
<DP>lOgN6 ... tbApX0=</DP>
<DQ>h75s0 ... w93Jys=</DQ>
<InverseQ>dDFa8H ... BlTuWs=</InverseQ>
<D>n4EN9rDQm ... Nj7lY7G4Q==</D>
</RSAKeyValue>
That pretty much confirms that this line of code does absolutely nothing
$rsaobj.FromXmlString($rsaobj.ToXmlString($jsonfile.private_key));