Create a service account like here
That creates and downloads you a json file (that should not be shared in public)
With this code (mostly created with Co-Pilot) from pwsh/Powershell Core, you can send a message via httpv1:
# This example currently only working in pwsh
$serviceAccountKey = "C:\Users\Florian\Downloads\fcm\fastfire-xxxxx-xxxxxxxxx.json"
$json = Get-Content $serviceAccountKey -Raw | ConvertFrom-Json
# Bereite den privaten Schlüssel vor
$privateKeyPem = $json.private_key
$privateKeyPem = $privateKeyPem -replace "-----BEGIN PRIVATE KEY-----", ""
$privateKeyPem = $privateKeyPem -replace "-----END PRIVATE KEY-----", ""
$privateKeyPem = $privateKeyPem -replace "`n", ""
$privateKeyBytes = [Convert]::FromBase64String($privateKeyPem)
# Lade den privaten Schlüssel
$rsa = [System.Security.Cryptography.RSA]::Create()
$rsa.ImportPkcs8PrivateKey($privateKeyBytes, [ref]0)
# Erstelle das JWT
$now = [DateTimeOffset]::UtcNow.ToUnixTimeSeconds()
$exp = $now + 3600
$header = @{
alg = "RS256"
typ = "JWT"
}
$claimSet = @{
iss = $json.client_email
scope = "https://www.googleapis.com/auth/cloud-platform"
aud = "https://oauth2.googleapis.com/token"
iat = $now
exp = $exp
}
$headerJson = $header | ConvertTo-Json -Compress
$claimSetJson = $claimSet | ConvertTo-Json -Compress
$headerBase64 = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($headerJson))
$claimSetBase64 = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($claimSetJson))
$unsignedToken = "$headerBase64.$claimSetBase64"
# Signiere das JWT
$signature = $rsa.SignData([System.Text.Encoding]::UTF8.GetBytes($unsignedToken), [System.Security.Cryptography.HashAlgorithmName]::SHA256, [System.Security.Cryptography.RSASignaturePadding]::Pkcs1)
$signedToken = "$unsignedToken." + [Convert]::ToBase64String($signature)
# Get the token
$response = Invoke-RestMethod -Uri "https://oauth2.googleapis.com/token" -Method Post -ContentType "application/x-www-form-urlencoded" -Body @{
grant_type = "urn:ietf:params:oauth:grant-type:jwt-bearer"
assertion = $signedToken
}
$accessToken = $response.access_token
# Then send the messages
$token = "ccRMv0ul3p_abcdef..."
$projectId = "fastfire-xxxxx"
$apiVersion = "v1"
$base = "https://fcm.googleapis.com"
$params = [Hashtable]@{
"Uri" = "$( $base )/$( $apiVersion )/projects/$( $projectId )/messages:send"
"Method" = "POST"
"ContentType" = "application/json; charset=utf-8"
"Headers" = [Hashtable]@{
"Authorization" = "Bearer $( $accessToken )"
}
"Body" = (ConvertTo-Json -Compress -InputObject (
[PSCustomObject]@{
"message" = [PSCustomObject]@{
"notification" = [PSCustomObject]@{
"title" = "Breaking News"
"body" = "New news story available."
}
"token"=$token
}
}
))
}
#exit 0
Invoke-RestMethod @params
The rsa import does create problems with Windows PowerShell 5.1, so better use PowerShell Core or try to solve it via BouncyCastle, here is an example I got to work
# Installiere BouncyCastle, falls noch nicht geschehen
Install-Package BouncyCastle -Source nuget.org
# Load Bouncy Castle
Add-Type -Path "C:\Program Files\PackageManagement\NuGet\Packages\BouncyCastle.1.8.9\lib\BouncyCastle.Crypto.dll"
$serviceAccountKey = "C:\Users\Florian\Downloads\fcm\fastfire-xxxxx-xxxxxxxxx.json"
$json = Get-Content $serviceAccountKey -Raw | ConvertFrom-Json
# Lade den privaten Schlüssel
$sr = [System.IO.StringReader]::new($json.private_key)
$reader = [Org.BouncyCastle.OpenSsl.PemReader]::new($sr)
$keyPair = [Org.BouncyCastle.Crypto.Parameters.RsaPrivateCrtKeyParameters]$reader.ReadObject() #[Org.BouncyCastle.Crypto.AsymmetricCipherKeyPair]
$rsaParams = [Org.BouncyCastle.Security.DotNetUtilities]::ToRSAParameters($keyPair)
$rsa = [System.Security.Cryptography.RSA]::Create()
$rsa.ImportParameters($rsaParams)
# Erstelle das JWT
$now = [DateTimeOffset]::UtcNow.ToUnixTimeSeconds()
$exp = $now + 3600
$header = @{
alg = "RS256"
typ = "JWT"
}
$claimSet = @{
iss = $json.client_email
scope = "https://www.googleapis.com/auth/cloud-platform"
aud = "https://oauth2.googleapis.com/token"
iat = $now
exp = $exp
}
$headerJson = $header | ConvertTo-Json -Compress
$claimSetJson = $claimSet | ConvertTo-Json -Compress
$headerBase64 = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($headerJson))
$claimSetBase64 = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($claimSetJson))
$unsignedToken = "$headerBase64.$claimSetBase64"
# Signiere das JWT
$signature = $rsa.SignData([System.Text.Encoding]::UTF8.GetBytes($unsignedToken), [System.Security.Cryptography.HashAlgorithmName]::SHA256, [System.Security.Cryptography.RSASignaturePadding]::Pkcs1)
$signedToken = "$unsignedToken." + [Convert]::ToBase64String($signature)
# Get the token
$response = Invoke-RestMethod -Uri "https://oauth2.googleapis.com/token" -Method Post -ContentType "application/x-www-form-urlencoded" -Body @{
grant_type = "urn:ietf:params:oauth:grant-type:jwt-bearer"
assertion = $signedToken
}
$accessToken = $response.access_token
# Then send the messages
$token = "ccRMv0ul3p_abcdef..."
$projectId = "fastfire-xxxxx"
$apiVersion = "v1"
$base = "https://fcm.googleapis.com"
$params = [Hashtable]@{
"Uri" = "$( $base )/$( $apiVersion )/projects/$( $projectId )/messages:send"
"Method" = "POST"
"ContentType" = "application/json; charset=utf-8"
"Headers" = [Hashtable]@{
"Authorization" = "Bearer $( $accessToken )"
}
"Body" = (ConvertTo-Json -Compress -InputObject (
[PSCustomObject]@{
"message" = [PSCustomObject]@{
"notification" = [PSCustomObject]@{
"title" = "Breaking News"
"body" = "New news story available."
}
"token"=$token
}
}
))
}
Invoke-RestMethod @params 