API Endpoint(s)
/oauth/token
Description
Hello everyone,
we have an integration with the Zoom meetings API which uses the account_credentials
grant type to get an Access Toke via a Server-to-Server app in order to use the Meetings API to create/update meetings etc.
Error?
We recently started getting 400 Bad Request response to /oauth/token
:
400 Client Error: Bad Request for url: https://zoom.us/oauth/token?grant_type=account_credentials&account_id=[REDACTED]
Further details
Our code sends the grant_type
and account_id
params in the query string of the POST request to the /oauth/token
(the code was written a few years ago).
A quick check of the docs reveals we should be
- putting the params in the request body
- set the Content-Type header as
application/x-www-form-urlencoded
I checked the changelog and couldn’t see recent changes about the /token
endpoint.
Has this recently changed? Or was the query string params an undocumented feature that has recently been tightened?
Thank you
Hello everyone,
I see you’re facing an issue with Zoom’s Meetings API integration using the account_credentials
grant type for obtaining an Access Token via a Server-to-Server app.
The Problem
You’re encountering a 400 Bad Request
error when making a request to the /oauth/token
endpoint:
400 Client Error: Bad Request for url: https://zoom.us/oauth/token?grant_type=account_credentials&account_id=[REDACTED]
This happens if your code sends the grant_type
and account_id
parameters in the query string of the POST request. The correct method is to:
- Send the parameters in the request body
- Set the
Content-Type
header to application/x-www-form-urlencoded
Solution
Here’s a working NodeJS implementation to fix the issue:
const axios = require("axios");
class Rest {
static async getHeaders() {
const { data, status } = await this.getAccessToken();
return {
authorization: `Bearer ${data.access_token}`,
'Content-Type': 'application/json',
'Accept': 'application/json'
}
}
static normalizeUrl(url) {
const parsedUrl = new URL(url);
return parsedUrl.toString();
}
static async request(config, retry = 2) {
try {
config.url = await this.normalizeUrl(config.url);
config.headers = await this.getHeaders();
return axios(config)
.then(function (response) {
return response;
})
.catch(async function (error) {
const { response } = error;
const { data, status } = response;
const { message } = data;
return { message, status };
});
} catch (error) {
console.log(`-- Error while request, error message: ${error?.message}`);
}
}
static async getAccessToken() {
try {
// Zoom account details
const accountID = process.env.ZOOM_ACCOUNT_ID;
const clientId = process.env.ZOOM_CLIENT_ID;
const clientSecret = process.env.ZOOM_CLIENT_SECRET;
// Encode client ID and secret in Base64
const authToken = Buffer.from(`${clientId}:${clientSecret}`).toString('base64');
// Configure the request
const config = {
method: 'post',
url: 'https://zoom.us/oauth/token',
params: {
grant_type: 'account_credentials',
account_id: accountID,
},
headers: {
'Host': 'zoom.us',
'Authorization': `Basic ${authToken}`,
},
};
// Make the request
return await axios(config)
.then((response) => {
return response;
})
.catch((error) => {
const { response } = error;
return response;
});
} catch (error) {
console.log(`-- Error while get access token, error message: ${error?.message}`);
}
}
}
module.exports = {
Rest,
};
Key Fixes Implemented:
- Moved parameters to the request body using
params
in Axios.
- Implemented proper Basic Authentication with Base64 encoding.
- Added error handling for better debugging and logging.
I hope this solution helps you resolve the issue.
Let me know if you have any questions!
Thank you!
1 Like