Question: I am trying to connect to Google Sheets' API from a Django view. The bulk of the code I have taken from this link: https://developers.google.com/sheets/api/quickstart/python Anyway, here are the codes: sheets.py (Copy pasted from the link above, function renamed) 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/spreadsheets.readonly'] # The ID and range of a sample spreadsheet. SAMPLE_SPREADSHEET_ID = '1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms' SAMPLE_RANGE_NAME = 'Class Data!A2:E' def test(): """Shows basic usage of the Sheets API. Prints values from a sample spreadsheet. """ 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('sheets', 'v4', credentials=creds) # Call the Sheets API sheet = service.spreadsheets() result = sheet.values().get(spreadsheetId=SAMPLE_SPREADSHEET_ID, range=SAMPLE_RANGE_NAME).execute() values = result.get('values', []) if not values: print('No data found.') else: print('Name, Major:') for row in values: # Print columns A and E, which correspond to indices 0 and 4. print('%s, %s' % (row[0], row[4]))  urls.py urlpatterns = [ path('', views.index, name='index') ]  views.py from django.http import HttpResponse from django.shortcuts import render from .sheets import test # Views def index(request): test() return HttpResponse('Hello world')  All the view function does is just call the test() method from the sheets.py module. Anyway, when I run my server and go the URL, another tab opens up for the Google oAuth2, which means that the credentials file is detected and everything. However, in this tab, the following error message is displayed from Google: Error 400: redirect_uri_mismatch The redirect URI in the request, http://localhost:65262/, does not match the ones authorized for the OAuth client.  In my API console, I have the callback URL set exactly to 127.0.0.1:8000 to match my Django's view URL. I don't even know where the http://localhost:65262/ URL comes from. Any help in fixing this? And can someone explain to me why this is happening? Thanks in advance. EDIT I tried to remove the port=0 in the flow method as mentioned in the comment, then the URL mismatch occurs with http://localhost:8080/, which is again pretty weird because my Django app is running in the 8000 port.

Answer: You shouldn't be using Flow.run_local_server() unless you don't have the intention of deploying the code. This is because run_local_server launches a browser on the server to complete the flow. This works just fine if you're developing the project locally for yourself. If you're intent on using the local server to negotiate the OAuth flow. The Redirect URI configured in your secrets must match that, the local server default for the host is localhost and port is 8080.  If you're looking to deploy the code, you must perform the flow via an exchange between the user's browser, your server and Google. Since you have a Django server already running, you can use that to negotiate the flow. For example, Say there is a tweets app in a Django project with urls.py module as follows. from django.urls import path, include from . import views urlpatterns = [ path('google_oauth', views.google_oath, name='google_oauth'), path('hello', views.say_hello, name='hello'), ] urls = include(urlpatterns)  You could implement a guard for views that require credentials as follow. import functools import json import urllib from google.oauth2.credentials import Credentials from google_auth_oauthlib.flow import InstalledAppFlow from google.auth.transport.requests import Request from django.shortcuts import redirect from django.http import HttpResponse SCOPES = ['https://www.googleapis.com/auth/userinfo.email', 'https://www.googleapis.com/auth/userinfo.profile', 'openid'] def provides_credentials(func): @functools.wraps(func) def wraps(request): # If OAuth redirect response, get credentials flow = InstalledAppFlow.from_client_secrets_file( 'credentials.json', SCOPES, redirect_uri="http://localhost:8000/tweet/hello") existing_state = request.GET.get('state', None) current_path = request.path if existing_state: secure_uri = request.build_absolute_uri( ).replace('http', 'https') location_path = urllib.parse.urlparse(existing_state).path flow.fetch_token( authorization_response=secure_uri, state=existing_state ) request.session['credentials'] = flow.credentials.to_json() if location_path == current_path: return func(request, flow.credentials) # Head back to location stored in state when # it is different from the configured redirect uri return redirect(existing_state) # Otherwise, retrieve credential from request session. stored_credentials = request.session.get('credentials', None) if not stored_credentials: # It's strongly recommended to encrypt state. # location is needed in state to remember it. location = request.build_absolute_uri() # Commence OAuth dance. auth_url, _ = flow.authorization_url(state=location) return redirect(auth_url) # Hydrate stored credentials. credentials = Credentials(**json.loads(stored_credentials)) # If credential is expired, refresh it. if credentials.expired and creds.refresh_token: creds.refresh(Request()) # Store JSON representation of credentials in session. request.session['credentials'] = credentials.to_json() return func(request, credentials=credentials) return wraps @provides_credentials def google_oauth(request, credentials): return HttpResponse('Google OAUTH <a href="/tweet/hello">Say Hello</a>') @provides_credentials def say_hello(request, credentials): # Use credentials for whatever return HttpResponse('Hello')  Note that this is only an example. If you decide to go this route, I recommend looking into extracting the OAuth flow to its very own Django App.