Home > Blockchain >  Axios (doesn't work) vs Postman (works) difference - Google OAuth2 call to refresh an access to
Axios (doesn't work) vs Postman (works) difference - Google OAuth2 call to refresh an access to

Time:12-06

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: Postman screenshot

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.

  • Related