File size: 4,332 Bytes
7644eac
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
import os
from flask import Blueprint, redirect, url_for, flash, current_app, session, request
from flask_dance.contrib.google import make_google_blueprint, google
from flask_login import login_user
from web_app.models import User
from web_app import db
import logging
from flask_dance.consumer.storage.session import SessionStorage

# Ensure local development can use HTTP for OAuth exchanges
if os.getenv("FLASK_ENV", "development") == "development" and not os.getenv("RENDER"):
    os.environ.setdefault("OAUTHLIB_INSECURE_TRANSPORT", "1")

# Set up logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger('google_oauth')

# Log environment variables only in debug mode
if os.getenv("DEBUG") == "True" and os.getenv("LOG_ENV_VARS") == "True":
    for key in os.environ:
        if 'SECRET' not in key and 'KEY' not in key:
            logger.info(f"ENV: {key}={os.environ.get(key)}")

# Create a very basic blueprint for Google OAuth
google_bp = make_google_blueprint(
    client_id=os.getenv("GOOGLE_OAUTH_CLIENT_ID"),
    client_secret=os.getenv("GOOGLE_OAUTH_CLIENT_SECRET"),
    scope=[
        "openid",
        "https://www.googleapis.com/auth/userinfo.email",
        "https://www.googleapis.com/auth/userinfo.profile"
    ],
    redirect_to="google_auth.google_callback",
    redirect_url="/auth/google/authorized",
    storage=SessionStorage()
)


# Create a separate blueprint for our callback route
bp = Blueprint("google_auth", __name__)

# Add login helpers that point to the Flask-Dance blueprint
@bp.route("/login")
def login():
    return redirect(url_for("google.login"))


@bp.route("/google")
def start_google_login():
    """Route used by the UI to start Google OAuth."""
    return redirect(url_for("google.login"))

@bp.route("/google/authorized")
@bp.route("/callback/google")
@bp.route("/google-callback")
def google_callback():
    """Handle the callback from Google OAuth"""
    # Log important debug info
    logger.info(f"Google OAuth callback received at {request.path}")
    logger.info(f"Full request URL: {request.url}")
    logger.info(f"Request args: {request.args}")
    logger.info(f"Is Google authorized? {google.authorized}")
    
    # If this route is hit directly without going through OAuth flow
    if not google.authorized:
        logger.error("Not authorized. Redirecting to login.")
        flash("Please try logging in again.", "info")
        return redirect(url_for("auth.login"))

    # Get user info from Google
    try:
        resp = google.get("/oauth2/v2/userinfo")
        if not resp.ok:
            logger.error(f"Failed to fetch user info: {resp.text}")
            flash("Failed to fetch user info from Google.", "danger")
            return redirect(url_for("auth.login"))

        info = resp.json()
        logger.info(f"Successfully retrieved user info")
        
        email = info.get("email")
        if not email:
            logger.error("Google account does not have an email.")
            flash("Google account does not have an email.", "danger")
            return redirect(url_for("auth.login"))

        # Find or create user
        user = User.query.filter_by(email=email).first()
        if not user:
            logger.info(f"Creating new user: {email}")
            user = User(
                username=info.get("name", email.split("@")[0]), 
                email=email,
                registration_source='google'
            )
            db.session.add(user)
            db.session.commit()
            flash("Welcome! Your account has been created.", "success")
        else:
            logger.info(f"Found existing user: {email}")

        # Log the user in with a permanent session
        session.permanent = True
        login_user(user, remember=True, duration=None)
        
        flash("Logged in with Google!", "success")
        return redirect("/")
        
    except Exception as e:
        logger.exception(f"Error in Google callback: {str(e)}")
        # Check if it's a state mismatch error
        if "MismatchingStateError" in str(type(e).__name__) or "state" in str(e).lower():
            flash("Session expired. Please try logging in again.", "warning")
        else:
            flash(f"An error occurred during login. Please try again.", "danger")
        return redirect(url_for("auth.login"))