В этом руководстве мы рассмотрим, как добавить социальную аутентификацию с помощью GitHub и Google в приложение на Flask.
От редакции Pythonist: реализацию социальной аутентификации в Django мы рассматривали в статье «Аутентификация в Django-REST с помощью Auth.js».
Социальная аутентификация — это процесс аутентификации пользователя через сторонний сервис, а не через собственный. Например, кнопка «Войти с помощью Google», которую вы видите на многих сайтах, является лучшим примером социальной аутентификации. Google проверяет подлинность пользователя и предоставляет приложению токен для управления пользовательской сессией.
Использование социальной аутентификации имеет свои преимущества. Вам не нужно будет настраивать аутентификацию для веб-приложения, поскольку этим занимается сторонний провайдер OAuth. Кроме того, поскольку такие провайдеры, как Google, Facebook и GitHub, проводят тщательные проверки для предотвращения несанкционированного доступа к своим сервисам, использование социальной аутентификации вместо создания собственного механизма может повысить безопасность вашего приложения.
Наряду с Flask мы будем использовать Flask-Dance для включения социальной аутентификации, Flask-Login для регистрации пользователей и управления сессиями, а также Flask-SQLAlchemy для взаимодействия с базой данных для хранения пользовательских данных.
Содержание
- Зачем использовать социальную аутентификацию?
- OAuth
- Flask-Dance
- Социальная аутентификация через GitHub
- Управление пользователями
- Социальная аутентификация через Google
- Заключение
Зачем использовать социальную аутентификацию?
Почему стоит использовать социальную аутентификацию, а не создавать свою собственную?
Плюсы
- Не нужно создавать собственный рабочий процесс аутентификации.
- Повышенная безопасность. Сторонние провайдеры аутентификации, такие как Google, Facebook и т. д., уделяют большое внимание безопасности. Использование таких сервисов может повысить безопасность вашего приложения.
- Можно автоматически получать имя пользователя, электронную почту и другие данные от провайдера аутентификации. Это улучшает процесс регистрации, поскольку не нужно просить пользователя вводить все это вручную.
Минусы
- Ваше приложение теперь зависит от другого приложения, которое вы не контролируете. Если стороннее приложение выйдет из строя, пользователи не смогут зарегистрироваться или войти в систему.
- Люди склонны игнорировать разрешения, запрашиваемые провайдером аутентификации. Некоторые приложения могут получить доступ к данным, которые им не требуются.
- Пользователи, не имеющие учетной записи хотя бы у одного из предложенных вами провайдеров, не смогут получить доступ к вашему приложению. Лучше всего реализовать как социальную аутентификацию, так и аутентификацию через логин и пароль, чтобы предоставить пользователю право выбора.
OAuth
Социальная аутентификация чаще всего реализуется с помощью OAuth — открытого стандартного протокола авторизации, в котором сторонний провайдер проверяет личность пользователя.
Наиболее распространенным потоком (или грантом) является код авторизации:
- Пользователь пытается войти в ваше приложение, используя свою учетную запись от стороннего провайдера авторизации.
- Пользователь перенаправляется к провайдеру аутентификации для проверки.
- После проверки они снова попадают в ваше приложение с кодом авторизации.
- Вы делаете запрос с кодом авторизации к провайдеру аутентификации для получения маркера доступа.
- После того как провайдер проверит код авторизации, он отправит обратно маркер доступа.
- После этого пользователь входит в систему и получает доступ к защищенным ресурсам.
- Токен доступа можно использовать для получения данных от провайдера.
Давайте рассмотрим быстрый пример этого потока на примере GitHub:
""" Импортировать необходимые модули. - `os` для чтения переменной env - `requests` для запросов GET/POST - `parse_qs` для парсинга ответа """ import os import requests from urllib.parse import parse_qs """ Определить переменные окружения GITHUB_ID и GITHUB_SECRET, а также конечные точки. """ CLIENT_ID = os.getenv("GITHUB_ID") CLIENT_SECRET = os.getenv("GITHUB_SECRET") AUTHORIZATION_ENDPOINT = f"https://github.com/login/oauth/authorize?response_type=code&client_id={os.getenv('GITHUB_ID')}" TOKEN_ENDPOINT = "https://github.com/login/oauth/access_token" USER_ENDPOINT = "https://api.github.com/user" """ 1. Войти через браузер, используя 'Authorization URL', выведенный в терминале. (Если вы уже залогинены в GitHub, нужно или вылогиниться, или тестировать в браузере в режиме инкогнито). 2. Сразу после входа страница будет перенаправлена. Скопируйте код из URL перенаправления. 3. Вставьте код в терминал. """ print(f"Authorization URL: {AUTHORIZATION_ENDPOINT}") code = input("Enter the code: ") """ Используя код авторизации, мы можем запросить токен доступа. """ # Получив код, мы отсылаем его эндпоинту токена авторизации # (с id и secret). Ответ содержит # access_token, который мы парсим при помощи parse_qs res = requests.post( TOKEN_ENDPOINT, data=dict( client_id=os.getenv("GITHUB_ID"), client_secret=os.getenv("GITHUB_SECRET"), code=code, ), ) res = parse_qs(res.content.decode("utf-8")) token = res["access_token"][0] """ Наконец, мы можем использовать токен доступа для получения информации о пользователе. """ user_data = requests.get(USER_ENDPOINT, headers=dict(Authorization=f"token {token}")) username = user_data.json()["login"] print(f"You are {username} on GitHub")
Чтобы протестировать, сохраните этот код в файл под названием oauth.py. Не забудьте просмотреть комментарии.
Далее вам нужно создать OAuth-приложение и получить OAuth-ключи от GitHub. Войдите в свой аккаунт на GitHub, а затем перейдите на https://github.com/settings/applications/new, чтобы создать новое OAuth-приложение:
Application name: Testing Flask-Dance Homepage URL: http://127.0.0.1:5000 Callback URL: http://127.0.0.1:5000/login/github/authorized
Нажмите «Register application». Вы будете перенаправлены в ваше приложение. Обратите внимание на Client ID и Client Secret:
Если Client Secret не сгенерировался, нажмите «Generate a new client secret».
Установите сгенерированные Client ID и Client Secret в качестве переменных окружения:
$ export GITHUB_ID=<your-github-id> $ export GITHUB_SECRET=<your-github-secret> # for windows machine, use `set` instead of `export`
Установите библиотеку Requests, а затем запустите скрипт:
$ pip install requests $ python oauth.py
Вы должны увидеть следующее:
Authorization URL: https://github.com/login/oauth/authorize?response_type=code&client_id=cde067521efaefe0c927 Enter the code:
Перейдите по этому URL-адресу. Авторизуйте приложение. Затем возьмите код из URL-адреса перенаправления. Например:
http://127.0.0.1:5000/login/github/authorized?code=5e54f2d755e450a64af3
Добавьте код обратно в окно терминала:
Authorization URL: https://github.com/login/oauth/authorize?response_type=code&client_id=cde067521efaefe0c927 Enter the code: 5e54f2d755e450a64af3
Вы должны увидеть имя пользователя GitHub, выведенное следующим образом:
You are mjhea0 on GitHub
А теперь давайте рассмотрим, как добавить социальную аутентификацию в приложение на Flask.
Flask-Dance
OAuthLib — это популярная, хорошо поддерживаемая библиотека Python, которая реализует OAuth. Хотя вы можете использовать эту библиотеку отдельно, в этом руководстве мы будем использовать Flask-Dance.
Flask-Dance — это библиотека, построенная поверх OAuthLib и предназначенная специально для Flask. Она имеет простой API, позволяющий быстро добавить социальный аутентификатор в приложение на Flask. Она также является самой популярной среди библиотек OAuth, разработанных для Flask.
Создайте новое приложение Flask, активируйте виртуальное окружение и установите необходимые зависимости:
$ mkdir flask-social-auth && cd flask-social-auth $ python3.12 -m venv .venv $ source .venv/bin/activate (.venv)$ pip install Flask==3.0.2 Flask-Dance==7.0.1 python-dotenv==1.0.1
Далее создайте файл main.py:
# main.py from flask import Flask, jsonify app = Flask(__name__) @app.route("/ping") def ping(): return jsonify(ping="pong") if __name__ == "__main__": app.run(debug=True)
Запустите сервер:
(.venv)$ python main.py
Перейдите по адресу http://127.0.0.1:5000/ping. Вы должны увидеть следующее:
{ "ping": "pong" }
Социальная аутентификация через GitHub
Сохраните GitHub Client ID и Client Secret, которые вы создали ранее, в новом файле .env:
GITHUB_ID=<YOUR_ID_HERE> GITHUB_SECRET=<YOUR_SECRET_HERE> OAUTHLIB_INSECURE_TRANSPORT=1
Примечания:
- Поскольку мы установили python-dotenv, Flask будет автоматически устанавливать переменные окружения из файла .env при запуске приложения.
- OAUTHLIB_INSECURE_TRANSPORT=1 требуется в целях тестирования, так как OAuthLib по умолчанию требует HTTPS.
Flask-Dance предоставляет Flask blueprints (шаблоны) для каждого провайдера. Давайте создадим такой blueprint для провайдера GitHub в файле app/oauth.py:
# app/oauth.py import os from flask_dance.contrib.github import make_github_blueprint github_blueprint = make_github_blueprint( client_id=os.getenv("GITHUB_ID"), client_secret=os.getenv("GITHUB_SECRET"), )
Импортируйте и зарегистрируйте blueprint в main.py и проложите новый маршрут:
# main.py from flask import Flask, jsonify, redirect, url_for from flask_dance.contrib.github import github from app.oauth import github_blueprint app = Flask(__name__) app.secret_key = "supersecretkey" app.register_blueprint(github_blueprint, url_prefix="/login") @app.route("/ping") def ping(): return jsonify(ping="pong") @app.route("/github") def login(): if not github.authorized: return redirect(url_for("github.login")) res = github.get("/user") return f"You are @{res.json()['login']} on GitHub" if __name__ == "__main__": app.run(debug=True)
Маршрут /github
перенаправляет на GitHub для проверки, если пользователь еще не вошел в систему. После входа отображается имя пользователя.
Запустите приложение, выполнив команду python main.py
. Перейдите на http://127.0.0.1:5000/github и протестируйте приложение. После проверки на GitHub вы будете перенаправлены обратно. Вы должны увидеть что-то типа этого:
You are @mjhea0 on GitHub
Управление пользователями
Теперь давайте подключим Flask-Login для управления сессиями пользователей и Flask-SQLAlchemy для добавления поддержки SQLAlchemy, чтобы хранить данные о пользователях в базе данных.
Установите зависимости:
(.venv)$ pip install Flask-Login==0.6.3 Flask-SQLAlchemy==3.1.1 SQLAlchemy-Utils==0.41.1
Модели
Создайте модели для хранения информации о пользователях и OAuth в новом файле app/models.py:
# app/models.py from flask_sqlalchemy import SQLAlchemy from flask_login import UserMixin, LoginManager from flask_dance.consumer.storage.sqla import OAuthConsumerMixin db = SQLAlchemy() class User(UserMixin, db.Model): id = db.Column(db.Integer, primary_key=True) username = db.Column(db.String(250), unique=True) class OAuth(OAuthConsumerMixin, db.Model): user_id = db.Column(db.Integer, db.ForeignKey(User.id)) user = db.relationship(User) login_manager = LoginManager() @login_manager.user_loader def load_user(user_id): return User.query.get(user_id)
Примечания:
- OAuthConsumerMixin из Flask-Dance автоматически добавит необходимые поля для хранения информации OAuth.
- LoginManager из Flask-Login будет извлекать пользователей из таблицы user.
В результате будут созданы две таблицы, user
и flask_dance_oauth
:
# user table name type -------- ------------ id INTEGER username VARCHAR(250) # flask_dance_oauth table name type ---------- ----------- id INTEGER provider VARCHAR(50) created_at DATETIME token TEXT user_id INTEGER
GitHub blueprint
Далее измените созданный ранее GitHub blueprint, чтобы добавить таблицу OAuth
в качестве хранилища:
# app/oauth.py import os from flask_login import current_user from flask_dance.contrib.github import make_github_blueprint from flask_dance.consumer.storage.sqla import SQLAlchemyStorage from app.models import OAuth, db github_blueprint = make_github_blueprint( client_id=os.getenv("GITHUB_ID"), client_secret=os.getenv("GITHUB_SECRET"), storage=SQLAlchemyStorage( OAuth, db.session, user=current_user, user_required=False, ), )
Здесь мы передали:
storage
как хранилище SQLAlchemy с модельюOAuth
db.session
, который являетсяsqlalchemy.session
- пользователя как
current_user
из Flask Login
Конечные точки
Давайте определим соответствующие конечные точки в main.py — login
, logout
и homepage
:
# main.py from flask import Flask, jsonify, redirect, render_template, url_for from flask_dance.contrib.github import github from flask_login import logout_user, login_required from app.models import db, login_manager from app.oauth import github_blueprint app = Flask(__name__) app.secret_key = "supersecretkey" app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///./users.db" app.register_blueprint(github_blueprint, url_prefix="/login") db.init_app(app) login_manager.init_app(app) with app.app_context(): db.create_all() @app.route("/ping") def ping(): return jsonify(ping="pong") @app.route("/") def homepage(): return render_template("index.html") @app.route("/github") def login(): if not github.authorized: return redirect(url_for("github.login")) res = github.get("/user") username = res.json()["login"] return f"You are @{username} on GitHub" @app.route("/logout") @login_required def logout(): logout_user() return redirect(url_for("homepage")) if __name__ == "__main__": app.run(debug=True)
Здесь мы инициализировали db
и login_manager
, определенные ранее в models.py.
Представление homepage
отображает шаблон index.html, который мы скоро добавим. Далее представление login
аутентифицирует пользователя на GitHub и возвращает имя пользователя. Маршрут logout
выводит пользователя из системы.
Все маршруты уже настроены, но мы еще не вошли в систему. Для этого мы воспользуемся сигналами Flask (Signals).
Сигналы
Сигналы позволяют выполнять действия при наступлении предопределенных событий. В нашем случае мы будем регистрировать пользователя при успешной аутентификации через GitHub.
Для работы сигналов требуется Binker, поэтому установите его прямо сейчас:
(.venv)$ pip install blinker==1.7.0
Добавьте новый helper в app/oauth.py:
# app/oauth.py import os from flask_login import current_user, login_user from flask_dance.consumer import oauth_authorized from flask_dance.contrib.github import github, make_github_blueprint from flask_dance.consumer.storage.sqla import SQLAlchemyStorage from sqlalchemy.orm.exc import NoResultFound from app.models import db, OAuth, User github_blueprint = make_github_blueprint( client_id=os.getenv("GITHUB_ID"), client_secret=os.getenv("GITHUB_SECRET"), storage=SQLAlchemyStorage( OAuth, db.session, user=current_user, user_required=False, ), ) @oauth_authorized.connect_via(github_blueprint) def github_logged_in(blueprint, token): info = github.get("/user") if info.ok: account_info = info.json() username = account_info["login"] query = User.query.filter_by(username=username) try: user = query.one() except NoResultFound: user = User(username=username) db.session.add(user) db.session.commit() login_user(user)
Когда пользователь подключается через github_blueprint
, выполняется функция github_logged_in
. Она принимает два параметра: blueprint
и token
(от GitHub). Затем мы получаем имя пользователя от провайдера и выполняем одно из двух действий:
- Если имя пользователя уже присутствует в таблицах, мы обеспечиваем пользователю вход.
- Если нет — создаем нового пользователя, а затем обеспечиваем ему вход.
Шаблоны
Наконец, давайте добавим шаблоны:
(.venv)$ mkdir templates && cd templates (.venv)$ touch _base.html (.venv)$ touch index.html
Шаблон _base.html содержит общий макет:
<!-- templates/_base.html --> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.2/dist/css/bootstrap.min.css" rel="stylesheet" /> <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Flask Social Login</title> </head> <body style="padding-top: 10%;"> {% block content %} {% endblock content %} </body> </html>
Далее добавим кнопку «Login with GitHub» в index.html:
<!-- templates/index.html --> {% extends '_base.html' %} {% block content %} <div style="text-align:center;"> {% if current_user.is_authenticated %} <h1>You are logged in as {{current_user.username}}</h1> <br><br> <a href="{{url_for('logout')}}" class="btn btn-danger">Logout</a> {% else %} <!-- GitHub button starts here --> <a href="{{url_for('login')}}" class="btn btn-secondary"> <i class="fa fa-github fa-fw"></i> <span>Login with GitHub</span> </a> <!-- GitHub button ends here --> {% endif %} </div> {% endblock content %}
После этого запустите приложение и перейдите по адресу http://127.0.0.1:5000. Протестируйте поток аутентификации.
Структура проекта:
├── .env ├── app │ ├── __init__.py │ ├── models.py │ └── oauth.py ├── main.py └── templates ├── _base.html └── index.html
Социальная аутентификация через Google
Теперь, когда вы знаете шаги по подключению нового провайдера OAuth и настройке Flask-Login, вы должны суметь настроить нового провайдера.
Например, вот шаги для Google:
- Создать приложение OAuth в Google
- Настроить Google blueprint в файле app/oauth.py
- Настроить маршрут для перенаправления на вход в Google в файле main.py
- Создать новую конечную точку для входа в Google (
@app.route("/google")
) в main.py - Создать новый сигнал Flask для входа пользователя в систему, когда он аутентифицируется через Google (
@oauth_authorized.connect_via(google_blueprint)
) в app/oauth.py - Обновить шаблон templates/index.html
Попробуйте сделать это самостоятельно.
Заключение
В этом руководстве мы подробно рассказали о том, как добавить социальный аутентификатор в приложение на Flask с помощью Flask-Dance. Настроив GitHub и Google, вы должны получить представление о том, как подключать новых провайдеров социальной аутентификации:
- Получить токены для каждого провайдера, создав OAuth-приложения
- Настроить модели баз данных для хранения данных пользователей и OAuth
- Создать blueprint для каждого провайдера и добавить созданную модель OAuth в качестве хранилища
- Добавить маршрут для аутентификации у провайдера
- Добавить сигнал для входа пользователя в систему после аутентификации
Ищете дополнительные задачи?
- Разберитесь, как связать несколько логинов в социальных сетях с одним аккаунтом (чтобы при входе пользователя через аккаунт в другой социальной сети не создавалась новая строка в таблице
user
, а новый аккаунт в социальной сети был связан с существующим пользователем). - Получите от социального провайдера дополнительную информацию о пользователе (например, email, язык, страну), указав области OAuth.
Код можно взять из репозитория flask-social-auth на GitHub.
Перевод статьи «Adding Social Authentication to Flask».