From fb5db7a953c1f30713789c644920fa4da32bd224 Mon Sep 17 00:00:00 2001 From: idittrich-valueon Date: Wed, 14 May 2025 12:40:39 +0200 Subject: [PATCH] registration and token aquisition with msal --- app.py | 2 +- env_dev.env | 2 +- env_prod.env | 2 +- routes/routeMsft.py | 66 +++++++++++++++++++++++++++++++++++++++++++- routes/routeUsers.py | 57 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 125 insertions(+), 4 deletions(-) diff --git a/app.py b/app.py index 038e2af5..9ce30d13 100644 --- a/app.py +++ b/app.py @@ -86,7 +86,7 @@ async def lifespan(app: FastAPI): # Parse CORS origins from environment variable def get_allowed_origins(): - origins_str = APP_CONFIG.get("APP_ALLOWED_ORIGINS", "http://localhost:8080") + origins_str = APP_CONFIG.get("APP_ALLOWED_ORIGINS", "http://localhost:5176") # Split by comma and strip whitespace origins = [origin.strip() for origin in origins_str.split(",")] logger.info(f"CORS allowed origins: {origins}") diff --git a/env_dev.env b/env_dev.env index 8ecb6aed..71cd2dff 100644 --- a/env_dev.env +++ b/env_dev.env @@ -22,7 +22,7 @@ APP_JWT_SECRET_SECRET=dev_jwt_secret_token APP_TOKEN_EXPIRY=300 # CORS Configuration -APP_ALLOWED_ORIGINS=http://localhost:8080,https://playground.poweron-center.net +APP_ALLOWED_ORIGINS=http://localhost:8080,https://playground.poweron-center.net,http://localhost:5176,https://nyla.poweron-center.net # Logging configuration APP_LOGGING_LOG_LEVEL = DEBUG diff --git a/env_prod.env b/env_prod.env index 57b1da4b..47133cd6 100644 --- a/env_prod.env +++ b/env_prod.env @@ -22,7 +22,7 @@ APP_JWT_SECRET_SECRET=dev_jwt_secret_token APP_TOKEN_EXPIRY=300 # CORS Configuration -APP_ALLOWED_ORIGINS=http://localhost:8080,https://playground.poweron-center.net +APP_ALLOWED_ORIGINS=http://localhost:8080,https://playground.poweron-center.net,http://localhost:5176,https://nyla.poweron-center.net # Logging configuration APP_LOGGING_LOG_LEVEL = WARNING diff --git a/routes/routeMsft.py b/routes/routeMsft.py index 1f81793d..0c6edbd1 100644 --- a/routes/routeMsft.py +++ b/routes/routeMsft.py @@ -8,9 +8,10 @@ import json from typing import Dict, Any, Optional from datetime import datetime, timedelta -from modules.auth import getCurrentActiveUser, getUserContext +from modules.auth import getCurrentActiveUser, getUserContext, createAccessToken, ACCESS_TOKEN_EXPIRE_MINUTES from modules.configuration import APP_CONFIG from modules.lucydomInterface import getLucydomInterface +from modules.gatewayInterface import getGatewayInterface # Configure logger logger = logging.getLogger(__name__) @@ -403,3 +404,66 @@ async def auth_status( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, content={"message": f"Error checking auth status: {str(e)}"} ) + +@router.post("/token") +async def get_backend_token(request: Request): + """Convert MSAL token to backend token""" + try: + # Get the authorization header + auth_header = request.headers.get('Authorization') + if not auth_header or not auth_header.startswith('Bearer '): + raise HTTPException( + status_code=status.HTTP_401_UNAUTHORIZED, + detail="Missing or invalid authorization header" + ) + + # Extract the MSAL token + msal_token = auth_header.split(' ')[1] + + # Verify the MSAL token and get user info + user_info = get_user_info_from_token(msal_token) + if not user_info: + raise HTTPException( + status_code=status.HTTP_401_UNAUTHORIZED, + detail="Invalid MSAL token" + ) + + # Get the user from the database using the email + gateway = getGatewayInterface() + user = gateway.getUserByUsername(user_info["email"]) + + if not user: + raise HTTPException( + status_code=status.HTTP_401_UNAUTHORIZED, + detail="User not registered in the system" + ) + + # Create backend token + access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES) + access_token = createAccessToken( + data={ + "sub": user["username"], + "mandateId": user["mandateId"] + }, + expiresDelta=access_token_expires + ) + + return { + "accessToken": access_token, + "tokenType": "bearer", + "user": { + "username": user["username"], + "email": user["email"], + "fullName": user.get("fullName", ""), + "mandateId": user["mandateId"] + } + } + + except HTTPException: + raise + except Exception as e: + logger.error(f"Error in MSAL token conversion: {str(e)}") + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail=f"Error processing MSAL token: {str(e)}" + ) diff --git a/routes/routeUsers.py b/routes/routeUsers.py index d149125e..dffa63f8 100644 --- a/routes/routeUsers.py +++ b/routes/routeUsers.py @@ -123,6 +123,63 @@ async def registerUser(userData: dict = Body(...)): logger.error(traceback.format_exc()) raise HTTPException(status_code=500, detail=f"Registration failed: {str(e)}") +@router.post("/register-with-msal", response_model=Dict[str, Any]) +async def registerUserWithMsal(userData: dict = Body(...)): + """Register a new user using Microsoft authentication""" + # Add debug logging + import logging + logger = logging.getLogger(__name__) + logger.info(f"MSAL Registration request data: {userData}") + + # Get the initial IDs for mandate and admin user + adminGateway = getGatewayInterface() + + # Get ID of the root mandate - we'll use this for new users + rootMandateId = adminGateway.getInitialId("mandates") + adminUserId = adminGateway.getInitialId("users") + + if not rootMandateId or not adminUserId: + raise HTTPException( + status_code=500, + detail="System is not properly initialized with root mandate and admin user" + ) + + # Use a gateway with admin context for user creation + gateway = getGatewayInterface(rootMandateId, adminUserId) + + if "username" not in userData: + raise HTTPException(status_code=400, detail="Username required") + + try: + # Create user data with a random password since it won't be used + import secrets + random_password = secrets.token_urlsafe(32) + + # Create user with required fields + newUser = gateway.createUser( + username=userData["username"], + password=random_password, # Random password since MSAL auth will be used + email=userData.get("email"), + fullName=userData.get("fullName"), + language=userData.get("language", "de"), + mandateId=rootMandateId, + disabled=False, + privilege="user" + ) + + return newUser + except ValueError as e: + logger.error(f"ValueError in MSAL registration: {str(e)}") + raise HTTPException(status_code=400, detail=str(e)) + except PermissionError as e: + logger.error(f"PermissionError in MSAL registration: {str(e)}") + raise HTTPException(status_code=403, detail=str(e)) + except Exception as e: + import traceback + logger.error(f"Unexpected error in MSAL registration: {str(e)}") + logger.error(traceback.format_exc()) + raise HTTPException(status_code=500, detail=f"MSAL Registration failed: {str(e)}") + @router.get("/{userId}", response_model=Dict[str, Any]) async def getUser( userId: int,