2
votes

I've been stuck for hours getting the Google OAuth2 flow going in my Node/Express application. The basics are fairly straight-forward:

  1. Create Google application credentials
  2. Redirect users to consent screen
  3. Receive a code value
  4. Exchange code for access_token and refresh_token
  5. Store refresh_token for the future

For whatever reason, I cannot pass Step 4 above and always get the following response:

{
  "error": "invalid_grant",
  "description": "Bad Request"
}

There are a ton of other solutions asking about the above, and no solution has yet worked for me:

  • Don't use the same code regardless of result
  • Wait a while after adding new Authorized Redirect URIs
  • Include http://localhost:3000 as an Authorized Redirect URI
  • Include all variants with/without protocol, port, trailing-slash
  • Set access_type to offline previously if needed (and it is)
  • Use Request instead of Axios
  • Include a null scope parameter like the OAuth Playground
  • Encode parameters using Querystring
  • Confirm application/x-www-urlencoded is the Content-Type
  • Log out of all Google accounts when retrying
  • Be aware of Google's token creation limit

With the above stated, here's the specifics of my plight:

Host: http://localhost:3000

Callback #1: http://localhost:3000/auth/google/callback (where code is sent and exchange action below is run)

Callback #2: http://localhost:3000/auth/google/callbacktwo (where access_token and refresh_token should be returned)

Exchange Action: Adjusted for Request module

{
    method: 'POST',
    uri: 'https://www.googleapis.com/oauth2/v4/token',
    headers: {
      'content-type': 'application/x-www-form-urlencoded'
    },
    body: {
      'code': req.query.code,
      'redirect_uri': 'http://localhost:3000/auth/google/callbacktwo',
      'client_id': '***.apps.googleusercontent.com',
      'client_secret': '***',
      'scope': null,
      'grant_type': 'authorization_code'
    }
}

Authorized Redirect URIs: Visible in Application > Credentials > OAuth 2.0 client IDs

See screenshot


All-in-all, I'm stuck. There's either something fundamentally wrong with my setup/approach or I'm the first developer encountering this bug (the latter can't be true). Can pay for assistance too.

1

1 Answers

1
votes

In a shocking turn of events, I resolved this within minutes of posting above with the changes below:


Change #1

Added 'http://127.0.0.1' variants to my Authorized Redirect URIs list

Since '127.0.0.1' equates to 'localhost', thought I'd give it a try. Read this elsewhere.


Change #2

Changed the Exchange Action's redirect_uri to Callback #1, which is where the code was sent.

I thought "what if Google isn't liking the redirect URI not being where it sent the code?".


Change #3

Removed the Exchange Action's scope parameter

Though it's clearly visible in the OAuth Playground, it's not in the documentation.


And the result? Received the object I've been seeking for hours and hours:

{
   "access_token": "****",
   "expires_in": 3600,
   "refresh_token": "***",
   "scope": "https://www.googleapis.com/auth/userinfo.profile",
   "token_type": "Bearer",
   "id_token": "*****"
}

Disclaimer: This does not mean the above will work for you, but it did for me and I'll take it.