You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
86 lines
3.8 KiB
86 lines
3.8 KiB
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')
|
|
|