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