Google API OAuth 2.0 can’t authenticate Error 400: redirect_uri_mismatch

I want to create an application that will have read only access to multiple users calendar events. However, I constantly fail to pass through authentication step. Browser opens new consent screen with Error 400: redirect_uri_mismatch. Here is what i did:

For this I set up my project in google console. I did following:

  1. Start by creating a new project, and enabled the Google Calendar API on the “APIs” page.
  2. Configured consent screen
  3. Created Credentials via selecting Web Application for application type and OAuth 2.0 for authentication method, and set “Authorized redirect URIs” to “http://localhost:8080”. Right now web app is hosted locally.
  4. Downloaded credentials.json file locally. json file is below:
{"web":
    {
        "client_id":"123234-jjjjj.apps.googleusercontent.com",
        "project_id":"ppppp-1234",
        "auth_uri":"https://accounts.google.com/o/oauth2/auth",
        "token_uri":"https://oauth2.googleapis.com/token",
        "auth_provider_x509_cert_url":"https://www.googleapis.com/oauth2/v1/certs",
        "client_secret":"zzzzzzzzzzzzzzzzzz",
        "redirect_uris":["http://localhost:8080"],
        "javascript_origins":["http://localhost"]
    }
}

I use following code to call google API:

import pickle
import os
from google_auth_oauthlib.flow import Flow, InstalledAppFlow
from googleapiclient.discovery import build
from googleapiclient.http import MediaFileUpload
from google.auth.transport.requests import Request

def Create_Service(client_secret_file, api_name, api_version, *scopes):
    CLIENT_SECRET_FILE = client_secret_file
    API_SERVICE_NAME = api_name
    API_VERSION = api_version
    SCOPES = [scope for scope in scopes[0]]
    
    cred = None

    pickle_file = f'token_{API_SERVICE_NAME}_{API_VERSION}.pickle'
    
    if os.path.exists(pickle_file):
        with open(pickle_file, 'rb') as token:
            cred = pickle.load(token)

    if not cred or not cred.valid:
        if cred and cred.expired and cred.refresh_token:
            cred.refresh(Request())
        else:
            flow = InstalledAppFlow.from_client_secrets_file(CLIENT_SECRET_FILE, SCOPES)
            cred = flow.run_local_server()

        with open(pickle_file, 'wb') as token:
            pickle.dump(cred, token)

    try:
        service = build(API_SERVICE_NAME, API_VERSION, credentials=cred)
        print(API_SERVICE_NAME, 'service created successfully')
        return service
    except Exception as e:
        print(e)
        return None


CLIENT_SECRET_FILE = 'token.json'
API_NAME = 'calendar'
API_VERSION = 'v3'
SCOPES = ['https://www.googleapis.com/auth/calendar.events.readonly']

service = Create_Service(CLIENT_SECRET_FILE, API_NAME, API_VERSION, SCOPES)
colors = service.colors().get().execute()

print(colors)

When I run it, browser opens a consent screen with

Error 400: redirect_uri_mismatch. Then I follow http://localhost:8080/ and I see message “The authentication flow has completed. You may close this window.”

enter image description here

And in the console I see following errors:

 File "main.py", line 48, in <module>
    service = Create_Service(CLIENT_SECRET_FILE, API_NAME, API_VERSION, SCOPES)
  File "main.py", line 29, in Create_Service
    cred = flow.run_local_server()
  File "/home/ev/Projects/TIM/web/.venv/lib/python3.8/site-packages/google_auth_oauthlib/flow.py", line 480, in run_local_server
    self.fetch_token(authorization_response=authorization_response)
  File "/home/ev/Projects/TIM/web/.venv/lib/python3.8/site-packages/google_auth_oauthlib/flow.py", line 288, in fetch_token
    return self.oauth2session.fetch_token(self.client_config["token_uri"], **kwargs)
  File "/home/ev/Projects/TIM/web/.venv/lib/python3.8/site-packages/requests_oauthlib/oauth2_session.py", line 239, in fetch_token
    self._client.parse_request_uri_response(
  File "/home/ev/Projects/TIM/web/.venv/lib/python3.8/site-packages/oauthlib/oauth2/rfc6749/clients/web_application.py", line 203, in parse_request_uri_response
    response = parse_authorization_code_response(uri, state=state)
  File "/home/ev/Projects/TIM/web/.venv/lib/python3.8/site-packages/oauthlib/oauth2/rfc6749/parameters.py", line 268, in parse_authorization_code_response
    raise MismatchingStateError()
oauthlib.oauth2.rfc6749.errors.MismatchingStateError: (mismatching_state) CSRF Warning! State not equal in request and response.

So, the question how to authenticate to google API via OAuth 2.0 in a correct way?

Answer

redirect_uri_mismatch

The redirect uri is used by Web applications to denote the location that your application is prepared to accept the authorization response from googles authorization server. It is basically a URL on the web server prepared to respond to authorization. Redirect uris are only used in with web applications.

In your case you are creating an installed application InstalledAppFlow in which case you should have created installed (Desktop) credentials on Google developer console. Desktop clients are able to accept the authorization responds from the location they are sending from this being most often localhost and so the authorization server just returns the token back to where the call originated from.

As your code is intended to run as an installed application by giving it web application credentials, will cause it to fail, due to the fact that the application is not sending the proper request.

I have a video here which will show you how to create How to create Google Oauth2 installed application credentials on google developer console.