Google OAuth token request returns "invalid_client": "Unauthorized"

13,410

Invalid client means that the client id or the client secret that you are using are not valid. They must be the ones you have downloaded from Google Developer console.

Tip: You might want to consider using the Google python client library it does all the heavy lifting for you.

from __future__ import print_function
import pickle
import os.path
from googleapiclient.discovery import build
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request

# If modifying these scopes, delete the file token.pickle.
SCOPES = ['https://www.googleapis.com/auth/drive.metadata.readonly']

def main():
    """Shows basic usage of the Drive v3 API.
    Prints the names and ids of the first 10 files the user has access to.
    """
    creds = None
    # The file token.pickle stores the user's access and refresh tokens, and is
    # created automatically when the authorization flow completes for the first
    # time.
    if os.path.exists('token.pickle'):
        with open('token.pickle', 'rb') as token:
            creds = pickle.load(token)
    # If there are no (valid) credentials available, let the user log in.
    if not creds or not creds.valid:
        if creds and creds.expired and creds.refresh_token:
            creds.refresh(Request())
        else:
            flow = InstalledAppFlow.from_client_secrets_file(
                'credentials.json', SCOPES)
            creds = flow.run_local_server(port=0)
        # Save the credentials for the next run
        with open('token.pickle', 'wb') as token:
            pickle.dump(creds, token)

    service = build('drive', 'v3', credentials=creds)

    # Call the Drive v3 API
    results = service.files().list(
        pageSize=10, fields="nextPageToken, files(id, name)").execute()
    items = results.get('files', [])

    if not items:
        print('No files found.')
    else:
        print('Files:')
        for item in items:
            print(u'{0} ({1})'.format(item['name'], item['id']))

if __name__ == '__main__':
    main()
Share:
13,410
Jaza
Author by

Jaza

Web developer based in Sydney Australia, specialising in Flask / Django (Python), and Symfony / Drupal (PHP).

Updated on July 20, 2022

Comments

  • Jaza
    Jaza almost 2 years

    Trying to get OAuth2 Google login working, this is the raw request that my app makes:

    Method: POST

    URL: https://www.googleapis.com/oauth2/v3/token

    Headers: Content-Type: application/x-www-form-urlencoded

    Values:

    client_id: XXX-0123456789abcdef0123456789abcdef.apps.googleusercontent.com

    client_secret: A1b2C3d4E5f6G7h8I9j0K1l2M

    code: 1/A1b2C3d4E5f6G7h8I9j0K1l2M3n4O5p6Q7r8S9t0U1v

    grant_type: authorization_code

    redirect_uri: http://localhost:5000/callback/google/

    And this is the response:

    Status: 401 Unauthorized

    Body:

    {
        "error": "invalid_client",
        "error_description": "Unauthorized"
    }
    

    Have verified that this is the exact request / response that my app is making (a Python app using Flask and rauth), and have verified that I can reproduce the exact same request / response using Postman.

    Per instructions in other threads, I have done all of the following in the Google APIs console:

    • In "OAuth consent screen" settings, set "Product name" to something different than "Project name"
    • Also in "OAuth consent screen" settings, double-check that email is set
    • Enable the Google+ API
    • Enable the Gmail API
    • Recreate the client ID / secret
    • Double-check that there are no leading or trailing spaces in the client ID / secret values, I have copied them correctly from the API console

    No matter what I do, still getting the same response of "invalid_client": "Unauthorized".

    Help with this would be appreciated. Am trying to set up OAuth2-powered "Log in with X" functionality in my app, have gotten Facebook and Twitter working without issues, would like to get Google working too, but if I can't resolve this then I'm afraid I'll have to ditch Google auth.

  • Jaza
    Jaza over 6 years
    Thanks! I thought I had double-checked it thoroughly, but no... I had defined the client secret in an env variable correctly, but I was then passing in the wrong env variable when actually making the token request in my app. Also, the Google python client library uses https://www.googleapis.com/oauth2/v4/token (instead of v3), so I've changed my app to do the same, seems to work fine.
  • Jaza
    Jaza over 6 years
    And the Google python client library also passes in the scope parameter when making the token request, so I've made my app do likewise, and pass in scope: email.
  • sameerfair
    sameerfair over 3 years
    I had mistakenly added a space to my secret string, which was causing the issue
  • Nayeemul Islam Swad
    Nayeemul Islam Swad over 3 years
    Spent a day after it, went this close to being full-scale mental. Finally printed out the query string and found out I forgot to pass the client_secret variable, so it was being evaluated to undefined ;-; I feel so dumb.
  • DaImTo
    DaImTo over 3 years
    Would the negative voter on a two year old question care to comment i am happy to add additional information this is still valid.
  • stroopwafel
    stroopwafel over 2 years
    I refreshed my client secret file and still got the invalid_client/unauthorized error. I had to delete token.pickle. Once token.pickle was recreated with the new client secret the error disappeared.
  • DaImTo
    DaImTo over 2 years
    Yes you need to remove the token.pickle when ever you make any changes to the authorization this will cause the app to request access again.