Получение ошибки 400: redirect_uri_mismatch при попытке использовать OAuth2 с Google Таблицами из представления Django - PullRequest
4 голосов
/ 30 мая 2020

Я пытаюсь подключиться к API Google Таблиц из вида Django. Основная часть кода, который я взял по этой ссылке: https://developers.google.com/sheets/api/quickstart/python

В любом случае, вот коды:

sheet.py ( Копия вставлена ​​по ссылке выше, функция переименована)

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')

Все, что делает функция просмотра, это просто вызывает метод test() из модуля sheet.py . В любом случае, когда я запускаю свой сервер и go URL, открывается другая вкладка для Google oAuth2, что означает, что файл учетных данных обнаружен и все такое. Однако на этой вкладке от 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.

В моей консоли API URL-адрес обратного вызова установлен точно на 127.0.0.1:8000, чтобы соответствовать моему Django URL-адресу просмотра. . Я даже не знаю, откуда взялся URL http://localhost:65262/. Любая помощь в исправлении этого? И может кто-нибудь объяснить мне, почему это происходит? Заранее спасибо.

EDIT Я попытался удалить port=0 в методе потока, как указано в комментарии, тогда несоответствие URL-адресов происходит с http://localhost:8080/, что снова довольно странно потому что мое приложение Django работает в порту 8000.

Ответы [ 3 ]

4 голосов
/ 05 июня 2020

Вам не следует использовать Flow.run_local_server () , если у вас нет намерения развертывать код. Это потому, что run_local_server запускает браузер на сервере для завершения потока.

Это прекрасно работает, если вы разрабатываете проект локально для себя.

Если вы намерены использование локального сервера для согласования потока OAuth. URI перенаправления, настроенный в ваших секретах, должен совпадать с этим, локальный сервер по умолчанию для хоста - localhost, а порт - 8080.

Если вы хотите развернуть код, вы должны выполнить поток через обмен между браузером пользователя, вашим сервером и Google.

Поскольку у вас уже запущен сервер Django , вы можете использовать это для согласования потока.

Например,

Допустим, в проекте Django есть приложение для твитов с модулем urls.py, как показано ниже.

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)

Вы можете реализовать защиту для представлений, требующих учетных данных, как показано ниже.

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')

Обратите внимание, что это только пример. Если вы решите использовать go этот маршрут, я рекомендую изучить возможность извлечения потока OAuth в его собственное приложение Django.

0 голосов
/ 10 августа 2020

У меня была та же проблема с ошибкой redirect_uri, и оказалось (как подразумевается выше), что я создал свои учетные данные в консоли Google как «Веб-сервер» вместо «настольное приложение». Я создал новые кредиты как «настольное приложение», загрузил JSON, и он сработал.

В конечном итоге я хочу использовать GMAIL API для веб-сервера, но это другой процесс, чем в примере.

0 голосов
/ 05 июня 2020

URI перенаправления сообщает Google, в какое место вы хотите вернуть авторизацию. Это должно быть правильно настроено в консоли разработчика Google, чтобы избежать взлома вашего клиента. Он должен точно совпадать.

Кому Консоль разработчика Google . Отредактируйте клиент, который вы в настоящее время используете, и добавьте следующий код в качестве uri перенаправления

http://localhost:65262/

enter image description here

Совет, щелкните маленький значок карандаша, чтобы отредактировать клиента: )

TBH, пока в разработке, проще просто добавить порт, который, по словам Google, вы вызываете, после чего поиграйте с настройками в своем приложении.

...