|
|
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}) |