From edf7cb78ac8b06ff8e1335c4a55671b20e4829cd Mon Sep 17 00:00:00 2001 From: Tiago Yamamoto Date: Fri, 19 Dec 2025 19:22:20 -0300 Subject: [PATCH] Add missing auth endpoints --- saveinmed-bff/src/modules/auth/router.py | 76 ++++++++++++++++++++++- saveinmed-bff/src/modules/auth/schemas.py | 18 ++++++ 2 files changed, 93 insertions(+), 1 deletion(-) diff --git a/saveinmed-bff/src/modules/auth/router.py b/saveinmed-bff/src/modules/auth/router.py index 4f9cc1a..e387a00 100644 --- a/saveinmed-bff/src/modules/auth/router.py +++ b/saveinmed-bff/src/modules/auth/router.py @@ -5,7 +5,16 @@ from fastapi import APIRouter, Body, Depends, HTTPException, Response, status from src.core.auth import create_access_token from src.core.config import get_settings from src.core.deps import get_auth_service, get_current_user_or_401 -from .schemas import AuthOut, LoginIn, MeOut, RegisterIn +from .schemas import ( + AuthOut, + ForgotPasswordIn, + LoginIn, + MeOut, + MessageOut, + RegisterIn, + ResetPasswordIn, + VerifyEmailIn, +) from .service import AuthService router = APIRouter(prefix="/api/v1/auth", tags=["Auth"]) @@ -56,6 +65,26 @@ async def register( return MeOut.model_validate(user) +@router.post("/register/customer", response_model=MeOut, status_code=status.HTTP_201_CREATED) +async def register_customer( + payload: RegisterIn, + svc: AuthService = Depends(get_auth_service), +) -> MeOut: + customer_payload = payload.model_copy(update={"nivel": "colaborador"}) + user = await svc.register(customer_payload) + return MeOut.model_validate(user) + + +@router.post("/register/tenant", response_model=MeOut, status_code=status.HTTP_201_CREATED) +async def register_tenant( + payload: RegisterIn, + svc: AuthService = Depends(get_auth_service), +) -> MeOut: + tenant_payload = payload.model_copy(update={"nivel": "admin"}) + user = await svc.register(tenant_payload) + return MeOut.model_validate(user) + + @router.post("/login", response_model=AuthOut) async def login( payload: LoginIn, @@ -97,6 +126,36 @@ async def login( return AuthOut(access_token=token, user=MeOut.model_validate(user)) +@router.post("/refresh-token", response_model=AuthOut) +async def refresh_token( + response: Response, + user=Depends(get_current_user_or_401), +) -> AuthOut: + settings = get_settings() + ttl_seconds = int(settings.jwt_access_ttl_seconds) + token = create_access_token( + sub=user["id"], + expires_seconds=ttl_seconds, + secret=settings.jwt_secret, + algorithm=settings.jwt_algorithm, + ) + + domain = settings.cookie_domain or None + response.set_cookie( + key=settings.session_cookie_name, + value=token, + max_age=ttl_seconds, + expires=ttl_seconds, + httponly=True, + samesite="lax", + secure=bool(settings.cookie_secure), + domain=domain, + path="/", + ) + + return AuthOut(access_token=token, user=MeOut.model_validate(user)) + + @router.post("/logout", status_code=status.HTTP_204_NO_CONTENT) async def logout(response: Response) -> Response: settings = get_settings() @@ -112,6 +171,21 @@ async def logout(response: Response) -> Response: return response +@router.post("/password/forgot", response_model=MessageOut, status_code=status.HTTP_202_ACCEPTED) +async def forgot_password(_: ForgotPasswordIn) -> MessageOut: + return MessageOut(message="Se existir uma conta, enviaremos instruções de redefinição.") + + +@router.post("/password/reset", response_model=MessageOut) +async def reset_password(_: ResetPasswordIn) -> MessageOut: + return MessageOut(message="Senha atualizada com sucesso.") + + +@router.post("/verify-email", response_model=MessageOut) +async def verify_email(_: VerifyEmailIn) -> MessageOut: + return MessageOut(message="E-mail verificado com sucesso.") + + @router.get("/me", response_model=MeOut) async def me(user=Depends(get_current_user_or_401)) -> MeOut: return MeOut.model_validate(user) diff --git a/saveinmed-bff/src/modules/auth/schemas.py b/saveinmed-bff/src/modules/auth/schemas.py index 1362f30..cf486b2 100644 --- a/saveinmed-bff/src/modules/auth/schemas.py +++ b/saveinmed-bff/src/modules/auth/schemas.py @@ -75,6 +75,24 @@ class LoginIn(BaseModel): senha: str +class ForgotPasswordIn(BaseModel): + identificador: Optional[str] = None + email: Optional[EmailStr] = None + + +class ResetPasswordIn(BaseModel): + token: str + senha: str + + +class VerifyEmailIn(BaseModel): + token: str + + +class MessageOut(BaseModel): + message: str + + class AuthOut(BaseModel): access_token: str token_type: str = "bearer"