Source code for mud_server.admin_gradio.api.auth

"""
Authentication API client for MUD Server.

This module handles all authentication-related API operations:
- User login with password authentication
- New user registration
- User logout and session cleanup

All functions follow a consistent pattern:
    - Validate input using validators module
    - Make API request using BaseAPIClient
    - Return standardized response dictionaries

Response Format:
    All functions return dictionaries with:
    {
        "success": bool,         # Whether operation succeeded
        "message": str,          # User-facing message
        "data": dict | None,     # Additional data (session_id, role, etc.)
        "error": str | None,     # Error details if failed
    }
"""

import logging

from mud_server.admin_gradio.api.base import BaseAPIClient
from mud_server.admin_gradio.ui.validators import (
    validate_password,
    validate_password_confirmation,
    validate_username,
)

logger = logging.getLogger(__name__)


[docs] class AuthAPIClient(BaseAPIClient): """ API client for authentication operations. This client handles login, registration, and logout operations, providing clean separation between API logic and UI concerns. Example: >>> client = AuthAPIClient() >>> result = client.login("alice", "password123") >>> if result["success"]: ... print(f"Logged in: {result['data']['session_id']}") """
[docs] def login(self, username: str, password: str) -> dict: """ Authenticate user and create session. Validates credentials, sends login request to backend, and returns session data on success. Args: username: Username to login with password: Plain text password Returns: Dictionary with structure: { "success": bool, "message": str, "data": { "session_id": str, "username": str, "role": str, } | None, "error": str | None, } Examples: >>> client = AuthAPIClient() >>> result = client.login("alice", "password123") >>> result["success"] True >>> result["data"]["role"] 'player' """ # Validate username is_valid, error = validate_username(username) if not is_valid: return { "success": False, "message": error, "data": None, "error": error, } # Validate password is_valid, error = validate_password(password) if not is_valid: return { "success": False, "message": error, "data": None, "error": error, } # Make API request response = self.post( "/login", json={"username": username.strip(), "password": password}, headers={"X-Client-Type": "gradio"}, ) if response["success"]: # Extract login data from response data = response["data"] return { "success": True, "message": data["message"], "data": { "session_id": data["session_id"], "username": username.strip(), "role": data.get("role", "player"), }, "error": None, } else: # Return error return { "success": False, "message": f"Login failed: {response['error']}", "data": None, "error": response["error"], }
[docs] def register( self, username: str, password: str, password_confirm: str, ) -> dict: """ Register a new user account. Validates input, sends registration request to backend API, and returns status message indicating success or failure. Args: username: Desired username for new account password: Plain text password for new account password_confirm: Password confirmation (must match password) Returns: Dictionary with structure: { "success": bool, "message": str, "data": None, "error": str | None, } Examples: >>> client = AuthAPIClient() >>> result = client.register("bob", "password123", "password123") >>> result["success"] True >>> "You can now login" in result["message"] True """ # Validate username is_valid, error = validate_username(username) if not is_valid: return { "success": False, "message": error, "data": None, "error": error, } # Validate password length is_valid, error = validate_password(password) if not is_valid: return { "success": False, "message": error, "data": None, "error": error, } # Validate password confirmation is_valid, error = validate_password_confirmation(password, password_confirm) if not is_valid: return { "success": False, "message": error, "data": None, "error": error, } # Make API request response = self.post( "/register", json={ "username": username.strip(), "password": password, "password_confirm": password_confirm, }, ) if response["success"]: data = response["data"] # Add success indicator and instructions message = f"✅ {data['message']}\n\nYou can now login with your credentials." return { "success": True, "message": message, "data": None, "error": None, } else: return { "success": False, "message": f"Registration failed: {response['error']}", "data": None, "error": response["error"], }
[docs] def logout(self, session_id: str | None) -> dict: """ Logout user and clean up session. Sends logout request to backend API and returns confirmation. Args: session_id: Session ID to logout (can be None if not logged in) Returns: Dictionary with structure: { "success": bool, "message": str, "data": None, "error": str | None, } Examples: >>> client = AuthAPIClient() >>> result = client.logout("abc123") >>> result["success"] True >>> result["message"] 'You have been logged out.' """ # If no session ID, user wasn't logged in if not session_id: return { "success": False, "message": "Not logged in.", "data": None, "error": "No active session", } # Make API request (ignore response, just try to clean up) # Note: We don't check the response because we want to clear # the client-side session regardless of server response try: self.post( "/logout", json={"session_id": session_id, "command": "logout"}, ) except Exception as exc: # Ignore errors - we'll clear session anyway, but log for visibility. logger.warning("Logout request failed: %s", exc) return { "success": True, "message": "You have been logged out.", "data": None, "error": None, }