Авторизация в приложении Flask с помощью Google
Оглавление
- Зачем Вашим пользователям использовать Google Login?
- Как приложения используют вход в систему Google
- Сведения о подключении OpenID
- Создание клиента Google
- Создание Вашего собственного веб-приложения
- Заключение
Вы, вероятно, видели опцию для входа в Google на различных веб-сайтах. На некоторых сайтах также есть дополнительные опции, такие как Вход в Facebook или Вход в GitHub. Все эти опции позволяют пользователям использовать существующие учетные записи для использования нового сервиса.
В этой статье вы узнаете о создании веб-приложения Flask. Ваше приложение позволит пользователю входить в систему, используя свой идентификатор Google, вместо создания новой учетной записи. Этот метод управления пользователями дает массу преимуществ. Он будет безопаснее и проще, чем управление традиционными комбинациями имени пользователя и пароля.
Эта статья будет более понятной, если вы уже знакомы с основами Python. Было бы также полезно немного узнать о веб-фреймворках и HTTP-запросах, но это не обязательно.
К концу этой статьи вы сможете:
- Создайте веб-приложение Flask, позволяющее пользователям входить в Google
- Создайте учетные данные клиента для взаимодействия с Google
- Используйте Flask-логин для управления сеансами пользователей в приложении Flask
- Лучше разбирайтесь в OAuth 2 и OpenID Connect (OIDC)
Зачем вашим пользователям использовать Google Login?
Возможно, вы хотите, чтобы профили были у отдельных пользователей. Или, возможно, вы хотите предоставить функции только определенным пользователям. В любом случае вам нужно знать, кто взаимодействует с вашим приложением. Другими словами, вам нужно будет аутентифицировать пользователей и идентифицировать их каким-то уникальным способом.
Традиционным решением является использование уникального имени пользователя и секретного пароля. Ваше приложение будет хранить эту информацию и запрашивать ее при необходимости. Однако у этого решения есть несколько недостатков:
- Вы должны надежно управлять паролями.
- Вам необходимо реализовать любую функциональность, связанную с учетной записью:
- Двухфакторная аутентификация
- Сброс пароля
- Вы должны обеспечить защиту от попыток входа в систему со стороны злоумышленников.
- Ваши пользователи должны запомнить еще одно имя пользователя и пароль.
Используя логин Google для своих пользователей, вы перекладываете всю ответственность на них. Ваше приложение ожидает, пока пользователь пройдет аутентификацию. Затем Google сообщает вашему приложению об этом пользователе. После этого вы сможете эффективно регистрировать их в своем приложении.
Вам не нужно хранить никаких паролей, и Google обеспечивает полную безопасность.
Как приложения используют логин Google
Существуют две очень популярные и важные спецификации, которые называются OAuth 2 и OpenID Connect (OIDC). OIDC построен поверх OAuth 2, добавив несколько новых идей и концепций.
Эти спецификации определяют, как стороннее приложение может получать информацию из другого сервиса. Обычно для этого требуется получить согласие пользователя. Чтобы немного разобраться в этом, давайте посмотрим, как это применимо к приложению, которое вы собираетесь создавать.
Вы собираетесь написать стороннее приложение, которое позволит пользователю использовать кнопку Google Login для входа в систему. Для этого Google необходимо знать о вашем приложении. К счастью, вы можете зарегистрировать свое приложение в качестве клиента Google.
Как только пользователь заходит в ваше приложение и нажимает кнопку Входа в Google, вы можете отправить его в Google. После этого Google должен убедиться, что пользователь согласен передать свой адрес электронной почты и другую информацию в ваше приложение. В случае получения согласия пользователя Google отправит некоторую информацию обратно в ваше приложение. Затем вы сохраняете эту информацию и можете ссылаться на нее позже, фактически регистрируя пользователя в системе.
Сведения о подключении OpenID
Чтобы запросить информацию от имени пользователя, вы должны стать client
пользователем сервера аутентификации, также известного как поставщик. Первое, что вы поймете, если углубитесь в эти спецификации, - это то, что существует множество пересекающихся терминов и концепций.
Итак, как стороннее приложение (также известное как клиент), вы хотите получать информацию от поставщика от имени пользователя. Существует ряд шагов, которые позволяют это сделать, и эти шаги должны выполняться в определенном порядке. Вот почему иногда можно услышать, что OAuth 2 и OpenID Connect связаны рукопожатием, flow или танцем.
В широком смысле это следующие шаги:
- Вы регистрируете стороннее приложение в качестве клиента поставщика:
- Вы получаете уникальные учетные данные клиента от поставщика.
- Позже вы будете использовать эти учетные данные клиента для аутентификации (подтверждения того, кто вы есть) перед поставщиком.
- Клиент отправляет запрос на URL-адрес провайдера
authorization
- Провайдер просит пользователя пройти аутентификацию (подтвердить, кто он такой)
- Провайдер просит пользователя дать согласие на то, чтобы клиент действовал от его имени:
- Обычно это включает в себя ограниченный доступ, и пользователю становится ясно, о чем просит клиент.
- Это похоже на то, когда вам нужно одобрить приложение на вашем телефоне, чтобы получить доступ к местоположению или контактам.
- Провайдер отправляет клиенту уникальный код авторизации.
- Клиент отправляет код авторизации обратно на адрес Провайдера
token
. - Провайдер отправляет токены клиента для использования с URL-адресами других провайдеров от имени пользователя.
Примечание: Описанные выше шаги предназначены для обработки кода авторизации, как определено в OAuth 2.
Эти шаги включают в себя оба упомянутых выше стандарта. OpenID Connect (OIDC) построен поверх OAuth 2, добавляя несколько дополнительных функций и требований, в основном связанных с процессом аутентификации. Помимо аутентификации, упомянутой в приведенном выше потоке, важными концепциями OIDC для вашего приложения являются конфигурация поставщика и конечная точка userinfo.
Конфигурация поставщика содержит информацию о поставщике, включая точные URL-адреса, которые необходимо использовать для потока OAuth 2. У поставщика OIDC есть стандартный URL-адрес, который вы можете использовать, чтобы вернуть документ с стандартизированными полями.
Конечная точка userinfo вернет информацию о пользователе после того, как вы пройдете процедуру OAuth 2. Это будет включать их адрес электронной почты и некоторую базовую информацию о профиле, которую вы будете использовать в своем приложении. Чтобы получить эту информацию о пользователе, вам понадобится токен от поставщика, как описано в последнем шаге приведенного выше порядка действий.
Позже вы увидите подробную информацию о том, как можно использовать конечную точку конфигурации поставщика и информацию о пользователе.
Создание клиента Google
Первым шагом для включения функции входа в Google является регистрация вашего приложения в качестве клиента Google. Давайте рассмотрим, как это сделать.
Во-первых, обратите внимание, что вам понадобится учетная запись Google. Если вы пользуетесь Gmail, она у вас уже есть.
Далее перейдите на страницу учетных данных разработчиков Google .
После входа вам может быть предложено согласиться с их условиями предоставления услуг. Если вы согласны с ними, нажмите кнопку Создать учетные данные на следующей странице. Выберите параметр для Идентификатора клиента OAuth:
Выберите опцию Web application
вверху. Вы также можете указать имя клиента в поле Name
. Указанное вами имя будет отображаться пользователям, когда они дадут согласие на то, чтобы ваше приложение действовало от их имени.
На данный момент вы будете запускать свое веб-приложение локально, поэтому вы можете установить для Authorized JavaScript origins
значение https://127.0.0.1:5000
, а для Authorized redirect URIs
значение https://127.0.0.1:5000/login/callback
. Это позволит вашему локальному приложению Flask взаимодействовать с Google.
Наконец, нажмите Create
и обратите внимание на client ID
и client secret
. Позже вам понадобятся оба варианта.
Создание Вашего собственного веб-приложения
Теперь самое интересное, когда вы применяете полученные знания для создания реального веб-приложения!
Давайте начнем с поставленной цели. Вы хотите создать приложение, позволяющее пользователям входить в систему с помощью своей учетной записи Google. Это приложение должно иметь возможность получать некоторую базовую информацию о пользователе из Google, например, его адрес электронной почты. Затем приложение должно сохранить основную информацию о пользователе в базе данных.
Однако сначала давайте взглянем на фреймворк и библиотеки, которые вы будете использовать.
Flask
Flask - это легкий веб-фреймворк, самопровозглашенный микропроцессор. Он поставляется со встроенными инструментами для выполнения основных задач, которые будет выполнять веб-приложение, таких как маршрутизация URL-адресов и обработка HTTP-запросов.
Я решил использовать Flask в качестве примера как из-за его популярности, так и из-за простоты. Однако то, что вы узнали об OAuth 2 и OIDC, не относится конкретно к Flask. Фактически, даже библиотека, которую вы будете использовать для упрощения работы с OAuth 2 и OIDC, может быть использована в любом коде на Python. Другими словами, с некоторыми незначительными изменениями вы можете использовать то, что узнаете здесь, и применить это к другому фреймворку по вашему выбору.
Flask-Вход в систему
Еще одним инструментом, который вы можете использовать для упрощения работы с пользователями, является flask_login, который обеспечивает управление сеансами пользователей.
Эта библиотека выполняет несколько функций за кулисами и предоставляет вам некоторые инструменты для работы с пользователями. А именно, она предоставляет утилиты, позволяющие узнать, когда пользователь вошел в систему и вышел из нее. Он делает это, управляя сеансом пользователя внутри файла cookie браузера.
Это также позволяет пользователям входить в систему и выходить из нее, включая создание записей в базе данных для этих пользователей. Однако, с точки зрения вашего кода, это на самом деле просто упрощает все (что вы скоро увидите).
OAuthLib
Есть распространенная фраза, которая очень актуальна для кода, связанного с безопасностью и соответствующего стандартам: “Не изобретайте велосипед заново”.
Стандарты OAuth2 и OpenID Connect сложны. Ознакомьтесь с RFC и спецификациями, и вы поймете. Они довольно сложные. Одна ошибка означает, что вы, возможно, обнаруживаете уязвимость в своем приложении.
Итак, вы не собираетесь писать код для реализации этих стандартов. Вы собираетесь использовать пакет Python, который был выбран по некоторым очень специфическим критериям:
- Это популярная и обычно рекомендуемая библиотека. Многие другие пакеты используют эту библиотеку внутри себя.
- Она очень активна, и люди часто исправляют ошибки.
- Она закалена в боях и существует с 2012 года.
Существуют пакеты для веб-фреймворков, которые используют эту библиотеку для более тесной интеграции с Flask, Django, Pyramid и другими. Однако, чтобы код, который вы изучаете здесь, не зависел от фреймворка, вы будете использовать эту библиотеку напрямую, без каких-либо причудливых оберток.
Установка зависимостей
Существует ряд зависимостей от сторонних разработчиков , которые вы можете использовать, чтобы облегчить себе жизнь. Вот краткое описание этих зависимостей:
- Веб-фреймворк для упрощения типичных задач веб-приложений (Flask)
- Упрощенный способ управления пользовательскими сессиями (Flask-Login)
- Проверенная в боях библиотека OIDC (
oauthlib
)
Кроме того, вы будете использовать следующее:
- База данных для хранения некоторой информации о пользователях, которые входят в систему (SQLite
- Удобный способ отправки HTTP-запросов в Google (
requests
) - Быстрый способ обеспечить безопасную работу с
https
локально (pyOpenSSL)
SQLite является частью стандартной библиотеки Python, а другие пакеты - нет. Итак, вам нужно установить пару зависимостей. А пока давайте просто пошагово рассмотрим создание этого приложения.
Сначала вам нужно установить те сторонние зависимости, о которых говорилось выше. Для этого вы создадите файл requirements.txt
со следующим содержимым:
requests==2.21.0
Flask==1.0.2
oauthlib==3.0.1
pyOpenSSL==19.0.0
Flask-Login==0.4.1
Примечание: Могут работать другие версии пакетов, но это версии, которые использовались при написании и тестировании этой статьи.
Далее вы можете установить эти зависимости с помощью pip
, установщика пакетов Python.
Примечание: Обычно рекомендуется использовать виртуальные среды, если вы собираетесь устанавливать зависимости для различных приложений Python на свой компьютер. Смотрите Виртуальные среды Python: учебник для начинающих, чтобы узнать больше.
Для установки из файла requirements.txt
выполните следующую команду в вашем терминале:
$ pip install -r requirements.txt
Теперь вы готовы к рок-н-роллу! Давайте разберемся с кодом.
Импорт, конфигурирование и настройка
Начните с добавления нескольких файлов для поддержки некоторых базовых функций базы данных и управления пользователями. Они не будут описаны по частям, в основном потому, что погружение в Базу данных Python подробности реализации - это кроличья нора, которая отвлекла бы нас от нашей цели.
Этот файл будет обрабатывать некоторые функции базы данных. Это почти построчно взято из официального руководства по работе с базой данных Flask:
# http://flask.pocoo.org/docs/1.0/tutorial/database/
import sqlite3
import click
from flask import current_app, g
from flask.cli import with_appcontext
def get_db():
if "db" not in g:
g.db = sqlite3.connect(
"sqlite_db", detect_types=sqlite3.PARSE_DECLTYPES
)
g.db.row_factory = sqlite3.Row
return g.db
def close_db(e=None):
db = g.pop("db", None)
if db is not None:
db.close()
def init_db():
db = get_db()
with current_app.open_resource("schema.sql") as f:
db.executescript(f.read().decode("utf8"))
@click.command("init-db")
@with_appcontext
def init_db_command():
"""Clear the existing data and create new tables."""
init_db()
click.echo("Initialized the database.")
def init_app(app):
app.teardown_appcontext(close_db)
app.cli.add_command(init_db_command)
Теперь, когда у вас есть некоторые утилиты для работы с базами данных, вы можете начать думать о схеме. Вы можете заметить, что этот код ищет файл schema.sql
, который вы создадите следующим.
Файл schema.sql
- это просто SQL-код, который создаст таблицу пользователей в нашей базе данных. В этом файле вы можете увидеть поля, которые вы будете сохранять для каждого пользователя.
Здесь у вас есть единственная таблица, user
, в которой будут указаны некоторые данные, относящиеся к пользователям (их имя, адрес электронной почты, с которого они входят в систему, и фотография их профиля в Google):
CREATE TABLE user (
id TEXT PRIMARY KEY,
name TEXT NOT NULL,
email TEXT UNIQUE NOT NULL,
profile_pic TEXT NOT NULL
);
Код в файле db.py
фактически выполнит этот SQL-код для создания таблицы в вашей базе данных.
Следующий файл содержит наш класс User
, который будет хранить и извлекать информацию из базы данных. Имя, адрес электронной почты и фотография профиля будут получены из Google, что вы увидите далее в статье.
В классе User
есть методы для получения существующего пользователя из базы данных и создания нового пользователя:
from flask_login import UserMixin
from db import get_db
class User(UserMixin):
def __init__(self, id_, name, email, profile_pic):
self.id = id_
self.name = name
self.email = email
self.profile_pic = profile_pic
@staticmethod
def get(user_id):
db = get_db()
user = db.execute(
"SELECT * FROM user WHERE id = ?", (user_id,)
).fetchone()
if not user:
return None
user = User(
id_=user[0], name=user[1], email=user[2], profile_pic=user[3]
)
return user
@staticmethod
def create(id_, name, email, profile_pic):
db = get_db()
db.execute(
"INSERT INTO user (id, name, email, profile_pic) "
"VALUES (?, ?, ?, ?)",
(id_, name, email, profile_pic),
)
db.commit()
Код выполняет инструкции SQL для базы данных, которая извлекается с помощью функции get_db()
из предыдущего файла db.py
. Каждый новый пользователь приводит к добавлению дополнительной строки в базу данных.
После того, как вы создали файлы db.py
, schema.sql
, и user.py
с помощью приведенного выше кода, вы можете создать новый файл app.py
. Добавьте к нему следующие импортные данные:
# Python standard libraries
import json
import os
import sqlite3
# Third-party libraries
from flask import Flask, redirect, request, url_for
from flask_login import (
LoginManager,
current_user,
login_required,
login_user,
logout_user,
)
from oauthlib.oauth2 import WebApplicationClient
import requests
# Internal imports
from db import init_db_command
from user import User
Вы будете использовать все это позже, поэтому сейчас не так важно понимать каждое из них. Следующая часть вашего app.py
- это некоторая конфигурация:
# Configuration
GOOGLE_CLIENT_ID = os.environ.get("GOOGLE_CLIENT_ID", None)
GOOGLE_CLIENT_SECRET = os.environ.get("GOOGLE_CLIENT_SECRET", None)
GOOGLE_DISCOVERY_URL = (
"https://accounts.google.com/.well-known/openid-configuration"
)
Вот как вы будете хранить идентификатор клиента Google и секрет клиента, которые вы должны были создать ранее в статье. Они будут использованы позже в процессе OIDC.
Ваше приложение попытается получить учетные данные клиента, считав переменные среды. Для этого есть несколько причин:
- Вам не нужно менять свой код позже, если вы захотите использовать другие учетные данные, вам просто нужно обновить среду.
- Вы не можете случайно передать свои секретные учетные данные в GitHub (или в другое общедоступное хранилище).
Многие люди случайно передают секреты в общедоступные хранилища, что создает довольно серьезную угрозу безопасности. Лучше защититься от этого, используя переменные среды.
Совет: Вы можете задать свои учетные данные клиента в качестве переменных среды в Linux bash terminal и Mac OS X terminal, используя export GOOGLE_CLIENT_ID=your_client_id
(аналогично для GOOGLE_CLIENT_SECRET
).
Если вы используете Windows, вы можете использовать set GOOGLE_CLIENT_ID=your_client_id
в командной строке.
В качестве альтернативы, вы могли бы вставить строки непосредственно сюда и сохранить их в этих переменных. Однако секрет клиента не должен передаваться или фиксироваться в каком-либо общедоступном репозитории. Другими словами, будьте очень осторожны и не проверяйте этот файл, если вы вводите сюда свои настоящие учетные данные клиента.
Наконец, ниже приведен некоторый код с глобальными переменными и некоторая наивная логика инициализации базы данных. Большая часть этого, за исключением инициализации базы данных, является стандартным способом настройки Flask, Flask-Login и OAuthLib, о которых вы читали ранее:
# Flask app setup
app = Flask(__name__)
app.secret_key = os.environ.get("SECRET_KEY") or os.urandom(24)
# User session management setup
# https://flask-login.readthedocs.io/en/latest
login_manager = LoginManager()
login_manager.init_app(app)
# Naive database setup
try:
init_db_command()
except sqlite3.OperationalError:
# Assume it's already been created
pass
# OAuth 2 client setup
client = WebApplicationClient(GOOGLE_CLIENT_ID)
# Flask-Login helper to retrieve a user from our db
@login_manager.user_loader
def load_user(user_id):
return User.get(user_id)
Обратите внимание, что вы уже используете ClientID от Google для инициализации нашего oauthlib
клиента в WebApplicationClient
.
Вы можете создать другую переменную окружения SECRET_KEY
, которую Flask и Flask-Login будут использовать для криптографической подписи файлов cookie и других элементов.
Конечные точки веб-приложений
А теперь самое интересное. Вы собираетесь написать четыре конечные точки для вашего веб-приложения:
- Один для главной страницы
- Один для начала процесса входа пользователя в систему
- Один для обратного вызова, на который Google будет перенаправлять пользователя после входа в систему
- Один для выхода из системы
Эти конечные точки будут определены разными URL-адресами в вашем приложении с очень креативными названиями:
- Домашняя страница:
/
- Войти в систему:
/login
- Обратный вызов для входа в систему:
/login/callback
- Выход из системы:
/logout
Конечно, вы можете захотеть добавить дополнительные страницы и функции позже. Конечный результат работы этого приложения будет полностью расширяемым, и вы сможете добавлять в него все, что захотите.
Вы добавите весь следующий код для этих конечных точек в файл app.py
. Давайте рассмотрим код каждой из этих конечных точек по очереди.
Домашняя страница
Визуально в этом нет ничего необычного, но вы добавите некоторую логику, чтобы отображать что-то другое, если пользователь вошел в систему. Если они не войдут в систему, появится ссылка с надписью Вход в Google.
При нажатии на ссылку они будут перенаправлены на вашу конечную точку /login
, которая запустит процесс входа в систему. После успешного входа в систему на главной странице будет отображаться электронная почта пользователя в Google и его общедоступная фотография профиля в Google!
Не мудрствуя лукаво, вы можете начать добавлять дополнительный код в свой файл app.py
:
@app.route("/")
def index():
if current_user.is_authenticated:
return (
"<p>Hello, {}! You're logged in! Email: {}</p>"
"<div><p>Google Profile Picture:</p>"
'<img src="{}" alt="Google profile pic"></img></div>'
'<a class="button" href="/logout">Logout</a>'.format(
current_user.name, current_user.email, current_user.profile_pic
)
)
else:
return '<a class="button" href="/login">Google Login</a>'
Вы заметите, что возвращаете HTML-код в виде строки, которую сможет использовать Flask. current_user.is_authenticated
является прекрасным дополнением к библиотеке Flask-Login
. Это простой способ определить, вошел ли в систему текущий пользователь, взаимодействующий с вашим приложением, или нет. Это позволяет применить условную логику. В данном случае отображается некоторая информация, которую вы сохранили о пользователе, если он вошел в систему.
Вы можете получить поля из вашей записи базы данных для пользователя, просто обратившись к ним как к атрибутам объекта current_user
, например, current_user.email
. Это еще одно дополнение к Flask-Login
.
Вход в систему
Теперь давайте перейдем к потоку OAuth 2. Кнопка Вход в Google, расположенная сверху, перенаправит на эту конечную точку. Первый шаг в этом процессе - выяснить, где находится конечная точка авторизации Google OAuth 2.
Вот где границы между тем, что определено OAuth 2 и OpenID Connect (OIDC), начинают стираться. Как обсуждалось ранее, OIDC имеет стандартную конечную точку для конфигурации провайдера , которая содержит информацию OAuth 2 и OIDC. Документ с этой информацией подается из стандартной конечной точки повсеместно, .well-known/openid-configuration
.
Предполагая, что вы скопировали предыдущий код, который определял GOOGLE_DISCOVERY_URL
, вот простая функция для получения конфигурации провайдера Google:
def get_google_provider_cfg():
return requests.get(GOOGLE_DISCOVERY_URL).json()
Совет: Чтобы сделать это более надежным, вам следует добавить обработку ошибок в вызов Google API на тот случай, если API Google вернет ошибку, а не действительный документ конфигурации поставщика.
Поле из документа конфигурации провайдера, которое вам нужно, называется authorization_endpoint
. Оно будет содержать URL-адрес, который вам нужно использовать для запуска потока OAuth 2 в Google из вашего клиентского приложения.
Вы можете объединить всю эту логику со следующим кодом:
@app.route("/login")
def login():
# Find out what URL to hit for Google login
google_provider_cfg = get_google_provider_cfg()
authorization_endpoint = google_provider_cfg["authorization_endpoint"]
# Use library to construct the request for Google login and provide
# scopes that let you retrieve user's profile from Google
request_uri = client.prepare_request_uri(
authorization_endpoint,
redirect_uri=request.base_url + "/callback",
scope=["openid", "email", "profile"],
)
return redirect(request_uri)
К счастью, oauthlib
упрощает сам запрос в Google. Вы использовали предварительно настроенный client
, для которого у вас уже есть идентификатор клиента Google. Далее вы указали перенаправление, которое хотите использовать в Google. Наконец, вы запросили у Google ряд OAuth-запросов. 2 scopes
.
Вы можете рассматривать каждую область как отдельную информацию о пользователе. В вашем случае вы запрашиваете адрес электронной почты пользователя и основную информацию о профиле в Google. Пользователь, разумеется, должен будет дать согласие на предоставление вам этой информации.
Примечание: openid
это обязательная область действия, позволяющая Google инициировать поток OIDC, который будет аутентифицировать пользователя, попросив его войти в систему. OAuth 2 на самом деле не стандартизирует процесс аутентификации, поэтому в данном случае это необходимо для нашего потока.
Обратный вызов для входа в систему
Давайте разберем это по частям, так как это немного сложнее, чем несколько предыдущих конечных точек.
Как только вы перенаправляетесь на конечную точку авторизации Google, многое происходит на стороне Google.
Конечная точка входа в ваше приложение является отправной точкой для всей работы Google по аутентификации пользователя и запросу согласия. Как только пользователь входит в систему Google и соглашается предоставить вашему приложению доступ к своей электронной почте и основной информации профиля, Google генерирует уникальный код, который отправляется обратно в ваше приложение.
В качестве напоминания, вот шаги OIDC, о которых вы читали ранее:
- Вы регистрируете стороннее приложение в качестве клиента поставщика.
- Клиент отправляет запрос на адрес провайдера
authorization
. - Провайдер просит пользователя пройти аутентификацию (подтвердить, кто он такой).
- Провайдер просит пользователя дать согласие на то, чтобы клиент действовал от его имени.
- Провайдер отправляет клиенту уникальный код авторизации
- Клиент отправляет код авторизации обратно на
token
URL провайдера - Провайдер отправляет токены клиента для использования с другими URL-адресами от имени пользователя
Когда Google отправит обратно этот уникальный код, он будет отправлен на конечную точку обратного вызова для входа в систему в вашем приложении. Итак, ваш первый шаг - определить конечную точку и получить это code
:
@app.route("/login/callback")
def callback():
# Get authorization code Google sent back to you
code = request.args.get("code")
Следующее, что вам нужно сделать, это отправить этот код обратно в конечную точку Google token
. После того, как Google проверит ваши учетные данные клиента, они отправят вам обратно токены, которые позволят вам пройти аутентификацию на других конечных точках Google от имени пользователя, включая конечную точку userinfo
, о которой вы читали ранее. В вашем случае вы просили только просмотреть основную информацию профиля, так что это единственное, что вы можете сделать с токенами.
Для начала вам нужно выяснить, что такое конечная точка Google token
. Вы снова воспользуетесь документом конфигурации провайдера:
# Find out what URL to hit to get tokens that allow you to ask for
# things on behalf of a user
google_provider_cfg = get_google_provider_cfg()
token_endpoint = google_provider_cfg["token_endpoint"]
oauthlib
в следующем блоке кода это несколько раз приходит вам на помощь. Сначала вам нужно создать запрос токена. Как только запрос будет сформирован, вы будете использовать библиотеку requests
для его фактической отправки. Затем oauthlib
, опять же, поможет вам с разбором токенов из ответа:
# Prepare and send a request to get tokens! Yay tokens!
token_url, headers, body = client.prepare_token_request(
token_endpoint,
authorization_response=request.url,
redirect_url=request.base_url,
code=code
)
token_response = requests.post(
token_url,
headers=headers,
data=body,
auth=(GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET),
)
# Parse the tokens!
client.parse_request_body_response(json.dumps(token_response.json()))
Теперь, когда у вас есть необходимые инструменты для получения информации о профиле пользователя, вам нужно запросить ее у Google. К счастью, OIDC определяет конечную точку получения пользовательской информации, и ее URL-адрес для данного провайдера стандартизирован в конфигурации провайдера. Вы можете узнать местоположение, установив флажок в поле userinfo_endpoint
в документе конфигурации провайдера. Затем вы можете использовать oauthlib
для добавления токена в свой запрос и использовать requests
для его отправки:
# Now that you have tokens (yay) let's find and hit the URL
# from Google that gives you the user's profile information,
# including their Google profile image and email
userinfo_endpoint = google_provider_cfg["userinfo_endpoint"]
uri, headers, body = client.add_token(userinfo_endpoint)
userinfo_response = requests.get(uri, headers=headers, data=body)
Следующим шагом в вашем путешествии будет анализ ответа от конечной точки userinfo
. Google использует необязательное поле email_verified
, чтобы подтвердить, что пользователь не только создал учетную запись, но и подтвердил адрес электронной почты для завершения создания учетной записи. Как правило, такая проверка является безопасной, поскольку это еще один уровень безопасности, который предлагает Google.
Как бы то ни было, вы проверите это, и если Google подтвердит, что пользователь верифицирован, вы проанализируете его информацию. Вот 4 основных элемента информации о профиле, которые вы будете использовать:
sub
: тема, уникальный идентификатор пользователя в Googleemail
: адрес электронной почты пользователя в Googlepicture
: фотография публичного профиля пользователя в Googlegiven_name
: имя и фамилия пользователя в Google
Результатом всего этого синтаксического анализа является следующий код:
# You want to make sure their email is verified.
# The user authenticated with Google, authorized your
# app, and now you've verified their email through Google!
if userinfo_response.json().get("email_verified"):
unique_id = userinfo_response.json()["sub"]
users_email = userinfo_response.json()["email"]
picture = userinfo_response.json()["picture"]
users_name = userinfo_response.json()["given_name"]
else:
return "User email not available or not verified by Google.", 400
Заключительными шагами в этом обратном вызове являются:
- Создайте пользователя в своей базе данных, используя информацию, которую вы только что получили от Google
- Начните сеанс пользователя, зарегистрировав его в системе
- Отправьте пользователя обратно на главную страницу (где теперь будет отображаться информация об его общедоступном профиле)
Код для выполнения этих действий выглядит следующим образом:
# Create a user in your db with the information provided
# by Google
user = User(
id_=unique_id, name=users_name, email=users_email, profile_pic=picture
)
# Doesn't exist? Add it to the database.
if not User.get(unique_id):
User.create(unique_id, users_name, users_email, picture)
# Begin user session by logging the user in
login_user(user)
# Send user back to homepage
return redirect(url_for("index"))
Итак, что вы делаете, так это создаете новую строку в своей базе данных для пользователя, если он еще не существует. Затем вы начинаете сеанс, используя Flask-Login.
Выход из системы
В конечной точке выхода из системы гораздо меньше кода, чем в предыдущих нескольких конечных точках. Вы просто вызываете функцию выхода из системы и перенаправляете обратно на домашнюю страницу. Готово. Вот оно:
@app.route("/logout")
@login_required
def logout():
logout_user()
return redirect(url_for("index"))
В @login_required
декоратор - это нечто важное, о чем следует упомянуть здесь. Это еще один инструмент из набора инструментов Flask-Login
, который гарантирует, что только зарегистрированные пользователи смогут получить доступ к этой конечной точке. Вы можете использовать это, если только зарегистрированные пользователи должны иметь доступ к чему-либо. В этом случае только зарегистрированные пользователи могут выйти из системы.
Локальное тестирование Вашего приложения
Вы можете запустить приложение Flask на своем локальном компьютере, чтобы протестировать процесс входа в систему, добавив в него окончательный код. app.py
:
if __name__ == "__main__":
app.run(ssl_context="adhoc")
Вы можете запустить свое приложение Flask с помощью следующей команды в вашем терминале:
$ python app.py
Примечание: Из-за простой логики инициализации базы данных при первом запуске этой команды база данных будет создана. Чтобы запустить ваше приложение, вам необходимо снова запустить ту же команду .
Flask должен печатать на вашем терминале, где запущен сервер разработки. Это должно быть https://127.0.0.1:5000/
.
Обратите внимание, что сервер разработки Flask работает локально и использует https
для обеспечения зашифрованного соединения с Google. Это достигается с помощью ssl_context="adhoc"
аргумента app.run
в приведенном выше коде. Для этого у вас должен быть установлен пакет PyOpenSSL
.
Недостатком является то, что используемый сертификат генерируется "на лету", поэтому при переходе к https://127.0.0.1:5000/
в вашем браузере, вероятно, появится большое предупреждение о том, что ваше соединение небезопасно или не является приватным. Вы можете эффективно игнорировать эти предупреждения.
Пройдя экран предупреждения, вы должны увидеть единственную кнопку с надписью Вход в Google. Нажав на нее, вы перейдете к официальному входу в Google. После того, как вы войдете в систему, Google предложит вам дать согласие на то, чтобы “стороннее приложение” получило доступ к вашей электронной почте и информации профиля.
После получения согласия вы будете перенаправлены обратно в ваше приложение Flask, где на странице должны быть указаны ваш адрес электронной почты Google и фотография публичного профиля! Наконец, кнопка Выход из системы позволяет вам выйти из системы.
Заключение
Разрешение пользователям использовать свои существующие учетные записи для входа в ваше веб-приложение имеет много преимуществ. Самое главное, безопасность и сложность управления учетными записями не ложатся на ваши плечи. Это освобождает вас от необходимости писать свое новое модное веб-приложение, не беспокоясь о мельчайших деталях двухфакторной аутентификации и тому подобном.
Вашим следующим шагом будет выполнение следующих действий:
- Переделайте инициализацию базы данных так, чтобы она выполнялась отдельно от запуска приложения
- Отделите HTML/CSS от кода на Python для упрощения управления:
- Вы могли бы использовать шаблонов.
- Вы также можете загружать статические файлы (например, JS и CSS) откуда-либо еще.
- Разместите свое приложение в облаке
- Приобретите доменное имя
- Используйте настоящий SSL-сертификат и избавьтесь от этого надоедливого предупреждения
В этой статье вы ознакомились с основами OAuth 2 и OpenID Connect. Вы видели, как использовать хорошо известные пакеты Python для создания веб-приложения, позволяющего пользователям входить в систему с помощью их существующей учетной записи Google. Самое главное, у вас есть пример кода, который послужит отличной отправной точкой для вашего следующего веб-приложения !
Back to Top