""" Endpoints API para autenticación """ from fastapi import APIRouter, Depends, HTTPException from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials from sqlalchemy.orm import Session from core.database import get_core_db from core.security import get_current_user from .dto import ( LoginRequestDTO, TokenResponseDTO, RefreshTokenRequestDTO, UserInfoResponseDTO, LogoutRequestDTO, RegisterRequestDTO, RegisterResponseDTO, ExchangeCodeRequestDTO ) from .service import AuthService router = APIRouter(prefix="/auth", tags=["Authentication"]) security = HTTPBearer() @router.post("/register", response_model=RegisterResponseDTO, status_code=201) async def register( register_data: RegisterRequestDTO, db: Session = Depends(get_core_db) ): """ Registra un nuevo usuario en Keycloak El usuario debe proporcionar: - username: Nombre de usuario único - email: Email único - password: Contraseña (mínimo 8 caracteres) - first_name: Nombre - last_name: Apellido - tenant_slug: Slug del tenant al que pertenece El usuario se crea automáticamente en Keycloak con: - Cuenta habilitada - Rol 'user' asignado por defecto - Atributos de tenant """ service = AuthService(db) return service.register(register_data) @router.post("/login", response_model=TokenResponseDTO) async def login( login_data: LoginRequestDTO, db: Session = Depends(get_core_db) ): """ Autentica usuario con Keycloak y retorna tokens JWT El usuario debe proporcionar: - username: Usuario o email - password: Contraseña - tenant_slug: Slug del tenant al que pertenece """ service = AuthService(db) return service.login(login_data) @router.post("/refresh", response_model=TokenResponseDTO) async def refresh_token( refresh_data: RefreshTokenRequestDTO, db: Session = Depends(get_core_db) ): """ Refresca el access token usando el refresh token """ service = AuthService(db) return service.refresh_token(refresh_data) @router.get("/me", response_model=UserInfoResponseDTO) async def get_current_user_info( credentials: HTTPAuthorizationCredentials = Depends(security), db: Session = Depends(get_core_db) ): """ Obtiene información del usuario actual desde el token """ service = AuthService(db) return service.get_user_info(credentials.credentials) @router.post("/logout") async def logout( logout_data: LogoutRequestDTO, db: Session = Depends(get_core_db), current_user: dict = Depends(get_current_user) ): """ Cierra sesión invalidando el refresh token """ service = AuthService(db) return service.logout(logout_data) @router.post("/exchange-code", response_model=TokenResponseDTO) async def exchange_code( exchange_data: ExchangeCodeRequestDTO, db: Session = Depends(get_core_db) ): """ Intercambia un authorization code de OAuth2 por tokens Este endpoint es útil cuando el frontend usa el flujo de autorización con proveedores externos (Microsoft, Google, etc.) a través de Keycloak. El código se obtiene después de que el usuario se autentica con el proveedor externo y Keycloak lo redirige al frontend con el código en los query params. """ service = AuthService(db) return service.exchange_code(exchange_data) @router.get("/health") async def auth_health(): """ Health check del módulo de autenticación """ return { "status": "ok", "module": "authentication", "provider": "keycloak" }