texlab / controller /auth_controller.py
syk101's picture
Upload 239 files
d05a9d0 verified
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
# Disable SSL requirement for local development
os.environ['OAUTHLIB_INSECURE_TRANSPORT'] = '1'
# Ensure OAuthlib allows HTTP for local development
os.environ['OAUTHLIB_RELAX_TOKEN_SCOPE'] = '1'
os.environ['OAUTHLIB_IGNORE_SCOPE_CHANGE'] = '1'
auth_bp = Blueprint('auth', __name__)
# Google OAuth configuration with your provided credentials
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 OAuth URLs
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"
# Initialize login manager
login_manager = LoginManager()
def init_app(app):
"""Initialize the login manager with the app"""
login_manager.init_app(app)
# Set login view - using setattr to avoid type checking issues
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 configuration
validate_oauth_config()
# Clear any existing OAuth state to prevent conflicts
session.pop('oauth_state', None)
# Create OAuth2Session with Google
google = OAuth2Session(
GOOGLE_CLIENT_ID,
scope=["openid", "email", "profile"],
redirect_uri=GOOGLE_REDIRECT_URI
)
# Generate authorization URL
authorization_url, state = google.authorization_url(
GOOGLE_AUTHORIZATION_BASE_URL,
access_type="offline",
prompt="select_account"
)
# Store state in session for security
session['oauth_state'] = state
print(f"Stored OAuth state: {state}")
print(f"Authorization URL: {authorization_url}")
# Redirect user to Google for authorization
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 configuration
validate_oauth_config()
# Get state parameters
received_state = request.args.get('state')
stored_state = session.get('oauth_state')
# Log state values for debugging
print(f"Received state: {received_state}")
print(f"Stored state: {stored_state}")
# Verify state parameter (allow None for development/testing)
if stored_state and received_state != stored_state:
print("State parameter mismatch")
return redirect(url_for('auth.login'))
# Create OAuth2Session with Google
google = OAuth2Session(
GOOGLE_CLIENT_ID,
state=stored_state, # Use stored state if available
redirect_uri=GOOGLE_REDIRECT_URI
)
# Fetch token
token = google.fetch_token(
GOOGLE_TOKEN_URL,
client_secret=GOOGLE_CLIENT_SECRET,
authorization_response=request.url,
# Include these parameters to handle token fetching properly
include_client_id=True
)
# Fetch user info
user_info = google.get(GOOGLE_USER_INFO_URL).json()
print(f"User info: {user_info}")
# Create or update user in our database
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)
# Clean up session
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()
# Log additional debugging information
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
# Create a guest user
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})