Search code examples
node.jsaxiosgoogle-oauthtokenauth-token

401 failed request when trying to exchange authorization code for tokens w/ google


I'm struggling with this specific step in the Google OAuth process: "Exchange authorization code for tokens" (As called in Google Developers' "OAuth 2.0 Playground", Step 2).

function getGoogleAuthURL() {
    const rootUrl = "https://accounts.google.com/o/oauth2/v2/auth";
    const options = {
      redirect_uri: `http://${HOST}:${PORT}/auth/google`,
      client_id: `${googleConfig.clientId}`,
      access_type: "offline",
      response_type: "code",
      prompt: "consent",
      scope: defaultScope.join(" "), // A list of scopes declared in the file scope
    };
    return `${rootUrl}?${querystring.stringify(options)}`;
}

app.get("/auth/google/url", (req, res) => {
    res.redirect(getGoogleAuthURL());
});

After redircting the user to the consent prompt, the code above redirect the user to: http://${HOST}:${PORT}/auth/google, giving the 'Authorization code' necessary to get the refresh and access tokens.

app.get(`/auth/google`, async (req, res) => {
    const code = req.query.code
    const tokens = await getTokens({code})
});

My problem is coming from the getToken() function that is used to POST and return the tokens.

function getTokens({code}) {
    const url = 'https://accounts.google.com/o/oauth2/token';
    const values = {
      code,
      client_id: googleConfig.clientId,
      client_secret: googleConfig.clientSecret,
      redirect_uri: googleConfig.redirect,
      grant_type: 'authorization_code',
    };
    console.log(`${url}${querystring.stringify(values)}`)

    return axios
      .post(url, querystring.stringify(values), {
        headers: {
          'Content-Type': 'application/x-www-form-url-encoded',
        },
      })
      .then((res) => res.data)
      .catch((error) => {
        throw new Error(error.message);
    });
}

I get Error: Request failed with status code 401 from the .catch((error) ...

The console.log(${url}${querystring.stringify(values)}) gives the full built link, which is:

https://accounts.google.com/o/oauth2/tokencode=X&client_id=X.apps.googleusercontent.com&client_secret=X&redirect_uri=http%3A%2F%2Flocalhost%3A8000%2Fdashboard%3B&grant_type=authorization_code

What I troubleshooted:

I've compared many times my 'request' and 'url' with the ones on Google Developers' "OAuth 2.0 Playground", and the syntax doesn't seem to be the problem with the fields, though I have doubt about the punctuation between /token and code=... that I changed to /token and ?code=..., but still resulting in a failed request (401).

I've also been using two different ${url}:

https://oauth2.googleapis.com/token https://accounts.google.com/o/oauth2/v2/auth

But I can tell which one I should be using.


Solution

  • The second param of axios post is the body, not query params.

    You can set params in the third argument.

    axios.post(url, {}, {
       headers: { 'Content-Type': 'application/x-www-form-url-encoded' },
       params: {
         tokencode: values.code, 
         // etc
       }
    }
    

    You are also missing key for code in your values object.

    You can also just append the querystring in the first argument:

    axios.post(url + querystring.stringify(values),{}, {
            headers: {
              'Content-Type': 'application/x-www-form-url-encoded'
            },
          })