File size: 6,819 Bytes
d05a9d0 |
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 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 |
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}) |