Server-to-Server OAuth returns 400 when requesting access token - Powershell

API Endpoint
https://zoom.us/oauth/token?grant_type=account_credentials&account_id=a12b3c4d5e6f

Description

I read the docs, cannot figure this out. Using Powershell, when I send a POST containing the header “Authorization: Basic {Base64 Encoded client_id:client_secret}” and to the endpoint above (using the account_id on the same page from the App Credentials), I always get a 400 Bad Request.

I know I can call REST APIs using this same/similar code, because in the same script I’m making successful calls to the Microsoft Graph API.

I tried disabling and re-enabling the app, regenerating the client secret, passing ContentType as application/json and application/x-www-form-urlencoded, even used curl exactly as it’s shown in the docs - no dice.
``
Example Code

$zoomAuthSecret = "client_id:client_secret" $zasBytes = [System.Text.Encoding]::Unicode.GetBytes($zoomAuthSecret) $EncodedZas = [Convert]::ToBase64String($zasBytes)

Invoke-RestMethod -Headers @{Authorization = "Basic $EncodedZas"} -Uri "https://zoom.us/oauth/token?grant_type=account_credentials&account_id={account_id}" -Method Post

Hi, @zoom123,

Thank you for posting in the Developer Forum – I am happy to help. Sorry to hear that you getting 400 bad request. Can you try to make the request with the following curl command :

curl --location --request POST 'https://zoom.us/oauth/token?grant_type=account_credentials&account_id={accountId}' \
--header 'Authorization: Basic Base64Encoder(clientId:clientSecret)' \
--data-raw ''

Also, there is a helpful utility script to generate a Zoom Server-to-Server Oauth Token and copy it to the clipboard on our GitHub as well:

That worked on curl! But that begs the question, why does that not work in powershell?

@zoom123,

Can you try this Powershell RestMethod and share the result:

$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
$headers.Add("Authorization", "Basic Base64Encoder(clientId:clientSecret)")

$body = ""

$response = Invoke-RestMethod 'https://zoom.us/oauth/token?grant_type=account_credentials&account_id={accountId}' -Method 'POST' -Headers $headers -Body $body
$response | ConvertTo-Json
$zoomAuthSecret = "client_id:client_secret"
$zasBytes = [System.Text.Encoding]::Unicode.GetBytes($zoomAuthSecret)
$EncodedZas =[Convert]::ToBase64String($zasBytes)

$zoomheaders = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
$zoomheaders.Add("Authorization", "Basic $EncodedZas")

$body = ""

$response = Invoke-RestMethod 'https://zoom.us/oauth/token?grant_type=account_credentials&account_id={account_id}' -Method 'POST' -Headers $zoomheaders -Body $body

$response | ConvertTo-Json

This unfortunately still returns a 400.

Some potential areas you may want to check:

  • Use a single-byte encoding like ASCII or UTF-8.
  • Encode using base64url encoding, which replaces certain characters and discards padding. Actually, my current Server-to-Server OAuth implementation is using base64 encoding; it was JWT that used base64url encoding.

A .NET Framework / PowerShell compatible implementation for both of the above might be available via Microsoft.IdentityModel.Tokens.Base64UrlEncoder.Encode.

Looks like encoding was the issue! When converting the string above to bytes, I was converting it using Unicode:

$zasBytes = [System.Text.Encoding]::Unicode.GetBytes($zoomAuthSecret)

But if I change it to UTF8:

$zasBytes = [System.Text.Encoding]::UTF8.GetBytes($zoomAuthSecret)

Success!

Thank you so much

1 Like

Awesome, glad to hear you were able to solve the issue. Can you share a snippet of both the failed and successful requests? It will be a great benefit to the community.

Failed response using unicode encoding for Base64 Credentials

Invoke-RestMethod : The remote server returned an error: (400) Bad Request.
At line:10 char:13
+ $response = Invoke-RestMethod 'https://zoom.us/oauth/token?grant_type ...
+             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-RestMethod], WebException
    + FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeRestMethodCommand

Successful response using UTF8 encoding for Base64 Credentials

access_token and scope redacted *

{
    "access_token":  "",
    "token_type":  "bearer",
    "expires_in":  3599,
    "scope":  ""
}
2 Likes

Thanks for taking the time to share your solution with the community, @zoom123 !

$zoomAuthSecret = "client_id:client_secret"

$zasBytes = [System.Text.Encoding]::UTF8.GetBytes($zoomAuthSecret)
$EncodedZas =[Convert]::ToBase64String($zasBytes)

$zoomheaders = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
$zoomheaders.Add("Authorization", "Basic $EncodedZas")

$body = ""

$response = Invoke-RestMethod 'https://zoom.us/oauth/token?grant_type=account_credentials&account_id={account_id}' -Method 'POST' -Headers $zoomheaders -Body $body

$response | ConvertTo-Json