from services.log import access_logger as logger from nicegui import app, ui from fastapi import Request from services.auth.oidc import oidc_config import time @ui.page('/auth/callback') async def auth_callback(request: Request, code: str = None, state: str = None, error: str = None, redirect_to: str = "/") -> None: logger.info(f"Auth callback received - code: {'present' if code else 'missing'}, state: {state}, error: {error}") if error: logger.error(f"Authentication error: {error}") ui.notify(f'Authentication failed: {error}', color='negative') ui.label(f'Authentication failed: {error}').classes('text-red-500') #ui.navigate.to('/login') return if not code or not state: logger.error(f"Missing callback parameters - code: {'present' if code else 'missing'}, state: {'present' if state else 'missing'}") ui.notify('Invalid callback parameters', color='negative') ui.label('Invalid callback parameters').classes('text-red-500') #ui.navigate.to('/login') return # Verify state parameter to prevent CSRF attacks stored_state = app.storage.user.get('oidc_state') state = app.storage.user.get('oidc_state', 'not set') # For debugging purposes, you can display the state parameter ui.label(f'OIDC state: {state}') if not stored_state or stored_state != state: logger.error(f"State parameter mismatch - expected: {stored_state}, received: {state}") ui.notify('Invalid state parameter', color='negative') ui.label('Invalid state parameter').classes('text-red-500') #ui.navigate.to('/login') return logger.info("State parameter verified successfully") try: # Exchange code for tokens logger.info("Exchanging authorization code for tokens") tokens = await oidc_config.exchange_code_for_tokens(code, redirect_uri=oidc_config.redirect_uri) # Validate and decode ID token to get user info logger.info("Validating ID token") user_info = oidc_config.validate_token(tokens['id_token']) if not user_info: logger.error("Invalid ID token received") ui.notify('Invalid ID token', color='negative') #ui.navigate.to('/login') return app.storage.user['id_token'] = tokens['id_token'] logger.info("ID token validated successfully") user_id = user_info.get('sub') username = user_info.get('preferred_username', user_info.get('email', 'Unknown')) email = user_info.get('email') logger.info(f"User authenticated successfully - ID: {user_id}, Username: {username}, Email: {email}") # Store user session app.storage.user.update({ 'username': username, 'email': email, 'user_id': user_id, 'authenticated': True, 'access_token': tokens['access_token'], 'refresh_token': tokens.get('refresh_token'), 'token_expires_at': time.time() + tokens.get('expires_in', 3600) }) # Start token refresh timer logger.info(f"Starting token refresh timer for user {user_id}") # Redirect to original destination or home redirect_to = app.storage.user.get('redirect_to', '/') app.storage.user.pop('redirect_to', None) # Clean up app.storage.user.pop('oidc_state', None) # Clean up logger.info(f"Authentication complete for user {username}, redirecting to: {redirect_to}") ui.navigate.to(redirect_to) except Exception as e: logger.error(f"Authentication failed with exception: {str(e)}", exc_info=True) ui.notify(f'Authentication failed: {str(e)}', color='negative') #ui.navigate.to('/login')