пʼятниця, 9 травня 2025 р.

Налаштування Google OAuth 2.0 у Flask (PythonAnywhere)

Налаштування Google OAuth 2.0 у Flask (PythonAnywhere)

Цей допис — покрокова інструкція для себе, щоб наступного разу налаштування авторизації через Google у Flask не забрало півдня і не викликало MismatchingStateError.

📦 Залежності

  • flask
  • authlib
  • python-dotenv
  • flask-login (опційно)
  • werkzeug.middleware.proxy_fix.ProxyFix

1. Створити файл .env


GOOGLE_CLIENT_ID=тут_твій_id
GOOGLE_CLIENT_SECRET=тут_секрет
SECRET_KEY=фіксований_ключ

2. Налаштування flask_app.py


from flask import Flask
from werkzeug.middleware.proxy_fix import ProxyFix
from dotenv import load_dotenv
import os
from pathlib import Path

project_root = Path(__file__).resolve().parent
load_dotenv(dotenv_path=project_root / '.env')

app = Flask(__name__)
app.wsgi_app = ProxyFix(app.wsgi_app, x_proto=1, x_host=1)

app.config.update(
    SECRET_KEY=os.getenv("SECRET_KEY"),
    SESSION_COOKIE_SECURE=True,
    SESSION_COOKIE_SAMESITE="None",
    PREFERRED_URL_SCHEME="https",
    GOOGLE_CLIENT_ID=os.getenv("GOOGLE_CLIENT_ID"),
    GOOGLE_CLIENT_SECRET=os.getenv("GOOGLE_CLIENT_SECRET"),
)

from auth import auth_bp, oauth, login_manager, init_oauth
init_oauth(app)
app.register_blueprint(auth_bp)
oauth.init_app(app)
login_manager.init_app(app)

3. Налаштування auth.py


from flask import Blueprint
from authlib.integrations.flask_client import OAuth
from flask_login import LoginManager

auth_bp = Blueprint('auth', __name__)
oauth = OAuth()
login_manager = LoginManager()

google = None

def init_oauth(app):
    global google
    google = oauth.register(
        name='google',
        client_id=app.config.get("GOOGLE_CLIENT_ID"),
        client_secret=app.config.get("GOOGLE_CLIENT_SECRET"),
        access_token_url='https://oauth2.googleapis.com/token',
        authorize_url='https://accounts.google.com/o/oauth2/auth',
        client_kwargs={
            'scope': 'openid https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile'
        }
    )

4. Роути /login та /auth/callback


@auth_bp.route('/login')
def login():
    redirect_uri = url_for('auth_callback', _external=True, _scheme="https")
    print(f"[DEBUG] session before redirect: {dict(session)}")
    return google.authorize_redirect(redirect_uri)

@auth_bp.route('/auth/callback')
def auth_callback():
    print(f"[DEBUG] session on callback: {dict(session)}")
    try:
        token = google.authorize_access_token()
        userinfo = google.parse_id_token(token)
        print("[DEBUG] userinfo:", userinfo)
        return redirect(url_for("review"))
    except Exception as e:
        print("[ERROR]", e)
        return "OAuth error"

❌ Типові помилки і як їх уникнути

ПомилкаПричинаРішення
MismatchingStateErrorКука не зберігається або не повертаєтьсяДодати ProxyFix, SESSION_COOKIE_SAMESITE="None", SECURE=True
client_id=Noneclient_id зчитується до load_dotenv()Зберігати client_id у app.config, а не через os.getenv під час імпорту
RuntimeError: working outside of application contextcurrent_app використовується під час імпортуВикликати oauth.register у функції init_oauth(app)
Cookie зникає після редиректуSESSION_COOKIE_SAMESITE неправильнийПоставити SESSION_COOKIE_SAMESITE="None", SECURE=True

🧪 Як перевірити

  • У браузері відкрий DevTools (F12) → Application → Cookies → leox.pythonanywhere.com
  • Перевір, чи є session-cookie
  • У вкладці Network → auth/callback → заголовки запиту → перевір, чи передалась кука

✅ Тестування


print("[DEBUG] session:", dict(session))
print("[DEBUG] redirect_uri:", redirect_uri)

Якщо після логіну все працює — побачите токен, і Flask сесія залишиться живою після редиректу.

---

🧠 Урок: у Flask найважливіше — порядок ініціалізації, контекст і cookie. Якщо ці три речі під контролем — навіть Google OAuth здається логічним.

Немає коментарів:

Дописати коментар