I'm using the Google OAuth2 flow, writing a function that takes a refresh_token I've saved to the database and makes the call to obtain a refreshed access_token. The problem is that when I make the call via Postman, it succeeds, but when I try to do it in the code via axios, it fails.
My Postman configuration for the call looks like this:
My code snippet looks like this:
export const getNewAccessToken = async (refreshToken: string): Promise<string> => {
const url = 'https://oauth2.googleapis.com/token';
const data = {
refresh_token: refreshToken,
grant_type: "refresh_token",
client_id: process.env.GOOGLE_CLIENT_ID,
client_secret: process.env.GOOGLE_CLIENT_SECRET,
};
try {
let res = await axios.post(url, data, {
headers: {
'content-type': 'application/x-www-form-urlencoded'
},
}).then(response => {
debugger;
}).catch(e => {
// It always enters the 'catch' here
debugger;
});
} catch (e) {
debugger;
}
}
I've checked and the refresh_token I'm using to test this, client_id and client_secret are the same in both cases.
When I make this call, the error in the catch
shows a 400 Bad Request, and the response.data is {error: 'unsupported_grant_type', error_description:'Invalid grant_type: '}
Am I missing something obvious? What can I try to do to debug?
One thing I've tried is to look at the error e
to see what request is being made, but I can't seem to find where the original request is in that ClientRequest object.
Edit 1:
Here's the curl command from Postman:
curl --location --request POST 'https://oauth2.googleapis.com/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'refresh_token=[confirmed same refresh_token as the code]' \
--data-urlencode 'client_id=[ditto]' \
--data-urlencode 'client_secret=[ditto]' \
--data-urlencode 'grant_type=refresh_token'
CodePudding user response:
Try this code
export const getNewAccessToken = async (refreshToken: string): Promise<string> => {
const url = 'https://oauth2.googleapis.com/token';
const data = new URLSearchParams({
refresh_token: refreshToken,
grant_type: 'refresh_token',
client_id: process.env.GOOGLE_CLIENT_ID,
client_secret: process.env.GOOGLE_CLIENT_SECRET,
});
try {
let res = await axios.post(url, data, {
headers: {
'content-type': 'application/x-www-form-urlencoded',
'Accept-Charset': 'UTF-8'
},
}).then(response => {
debugger;
}).catch(e => {
// It always enters the 'catch' here
debugger;
});
} catch (e) {
debugger;
}
}
CodePudding user response:
If you give Axios a JavaScript object to send as a request body, Axios will serialize it to JSON. But in this case, you are setting the Content-Type
header to application/x-www-form-urlencoded
which signals intent to provide a URL-encoded body, not JSON. A URL-encoded body looks like this:
refresh_token=abc&grant_type=abc&client_id=abc&client_secret=abc
There are quite a few different ways to create such a string to send as the body, a number of which are detailed in the Axios documentation. I personally would do the following:
const data = new URLSearchParams({
refresh_token: refreshToken,
grant_type: 'refresh_token',
client_id: process.env.GOOGLE_CLIENT_ID,
client_secret: process.env.GOOGLE_CLIENT_SECRET,
});
Alternatively, it might also work if you just set the Content-Type
header to application/json
instead, but that depends on the API and whether it is able to accept JSON.