| import os
|
| import time
|
| from flask import Blueprint, render_template, request, jsonify, redirect, url_for, session, current_app
|
| from flask_login import LoginManager, login_user, logout_user, login_required, current_user
|
| import requests
|
| from requests_oauthlib import OAuth2Session
|
| from models.user import User
|
|
|
|
|
| os.environ['OAUTHLIB_INSECURE_TRANSPORT'] = '1'
|
|
|
| os.environ['OAUTHLIB_RELAX_TOKEN_SCOPE'] = '1'
|
| os.environ['OAUTHLIB_IGNORE_SCOPE_CHANGE'] = '1'
|
|
|
| auth_bp = Blueprint('auth', __name__)
|
|
|
|
|
| GOOGLE_CLIENT_ID = os.environ.get('GOOGLE_CLIENT_ID')
|
| GOOGLE_CLIENT_SECRET = os.environ.get('GOOGLE_CLIENT_SECRET')
|
| GOOGLE_REDIRECT_URI = os.environ.get('GOOGLE_REDIRECT_URI', 'http://localhost:5000/auth/google/callback')
|
|
|
| print(f"OAuth Configuration Loaded - Client ID: {GOOGLE_CLIENT_ID[:10] if GOOGLE_CLIENT_ID else 'None'}...")
|
|
|
| def validate_oauth_config():
|
| """Validate OAuth configuration and raise error if missing"""
|
| if not GOOGLE_CLIENT_ID or not GOOGLE_CLIENT_SECRET:
|
| raise ValueError("Google OAuth credentials not found in environment variables. Please check your .env file.")
|
|
|
|
|
| GOOGLE_AUTHORIZATION_BASE_URL = "https://accounts.google.com/o/oauth2/v2/auth"
|
| GOOGLE_TOKEN_URL = "https://oauth2.googleapis.com/token"
|
| GOOGLE_USER_INFO_URL = "https://www.googleapis.com/oauth2/v2/userinfo"
|
|
|
|
|
| login_manager = LoginManager()
|
|
|
| def init_app(app):
|
| """Initialize the login manager with the app"""
|
| login_manager.init_app(app)
|
|
|
| setattr(login_manager, 'login_view', 'auth.login')
|
| return login_manager
|
|
|
| @login_manager.user_loader
|
| def load_user(user_id):
|
| return User.get(user_id)
|
|
|
|
|
| @auth_bp.route("/login")
|
| def login():
|
| """Display the login page"""
|
| return render_template('login.html')
|
|
|
|
|
| @auth_bp.route("/google/login")
|
| def google_login():
|
| """Redirect to Google OAuth login"""
|
| try:
|
|
|
| validate_oauth_config()
|
|
|
|
|
| session.pop('oauth_state', None)
|
|
|
|
|
| google = OAuth2Session(
|
| GOOGLE_CLIENT_ID,
|
| scope=["openid", "email", "profile"],
|
| redirect_uri=GOOGLE_REDIRECT_URI
|
| )
|
|
|
|
|
| authorization_url, state = google.authorization_url(
|
| GOOGLE_AUTHORIZATION_BASE_URL,
|
| access_type="offline",
|
| prompt="select_account"
|
| )
|
|
|
|
|
| session['oauth_state'] = state
|
| print(f"Stored OAuth state: {state}")
|
| print(f"Authorization URL: {authorization_url}")
|
|
|
|
|
| return redirect(authorization_url)
|
|
|
| except Exception as e:
|
| print(f"Error in Google OAuth login: {e}")
|
| import traceback
|
| traceback.print_exc()
|
| return redirect(url_for('auth.login'))
|
|
|
|
|
| @auth_bp.route("/google/callback")
|
| def google_callback():
|
| """Handle Google OAuth callback"""
|
| try:
|
|
|
| validate_oauth_config()
|
|
|
|
|
| received_state = request.args.get('state')
|
| stored_state = session.get('oauth_state')
|
|
|
|
|
| print(f"Received state: {received_state}")
|
| print(f"Stored state: {stored_state}")
|
|
|
|
|
| if stored_state and received_state != stored_state:
|
| print("State parameter mismatch")
|
| return redirect(url_for('auth.login'))
|
|
|
|
|
| google = OAuth2Session(
|
| GOOGLE_CLIENT_ID,
|
| state=stored_state,
|
| redirect_uri=GOOGLE_REDIRECT_URI
|
| )
|
|
|
|
|
| token = google.fetch_token(
|
| GOOGLE_TOKEN_URL,
|
| client_secret=GOOGLE_CLIENT_SECRET,
|
| authorization_response=request.url,
|
|
|
| include_client_id=True
|
| )
|
|
|
|
|
| user_info = google.get(GOOGLE_USER_INFO_URL).json()
|
| print(f"User info: {user_info}")
|
|
|
|
|
| user = User.create_or_update(
|
| id=f"google_{user_info['id']}",
|
| email=user_info['email'],
|
| name=user_info.get('name', user_info['email'].split('@')[0]),
|
| picture=user_info.get('picture')
|
| )
|
|
|
| if user:
|
| login_user(user)
|
|
|
| session.pop('oauth_state', None)
|
| return redirect(url_for('index'))
|
| else:
|
| return redirect(url_for('auth.login'))
|
|
|
| except Exception as e:
|
| print(f"Error in Google OAuth callback: {e}")
|
| import traceback
|
| traceback.print_exc()
|
|
|
| print(f"Request args: {request.args}")
|
| print(f"Session data: {dict(session)}")
|
| return redirect(url_for('auth.login'))
|
|
|
|
|
| @auth_bp.route("/guest/login", methods=['POST'])
|
| def guest_login():
|
| """Login as a guest user"""
|
| name = request.form.get('name')
|
| if not name:
|
| return "Name is required", 400
|
|
|
|
|
| user = User.create_or_update(
|
| id=f"guest_{name.lower().replace(' ', '_')}_{int(time.time())}",
|
| email=f"{name.lower().replace(' ', '.')}@guest.texlab.com",
|
| name=name,
|
| picture=None
|
| )
|
|
|
| if user:
|
| login_user(user)
|
| return redirect(url_for('index'))
|
| else:
|
| return "Failed to create user", 500
|
|
|
|
|
| @auth_bp.route("/logout")
|
| @login_required
|
| def logout():
|
| logout_user()
|
| return redirect(url_for('auth.login'))
|
|
|
|
|
| @auth_bp.route("/user")
|
| def get_current_user():
|
| if current_user.is_authenticated:
|
| return jsonify({
|
| 'authenticated': True,
|
| 'user': {
|
| 'id': current_user.id,
|
| 'email': current_user.email,
|
| 'name': current_user.name,
|
| 'picture': current_user.picture
|
| }
|
| })
|
| else:
|
| return jsonify({'authenticated': False}) |