From c741ffc827cd0f0c62d6ec57f77ba7c3315a9209 Mon Sep 17 00:00:00 2001 From: Johan LEROY Date: Thu, 17 Sep 2026 15:31:14 +0200 Subject: [PATCH] fix(auth): corrige la CI cassee par le nouvel endpoint de validation /auth/reset-password/validate manquait a la liste explicite des routes publiques (test_route_protection) et n'avait pas le modele de reponse 422 declare (openapi.json desynchronise du contrat genere). --- apps/backend/app/api/v1/endpoints/auth.py | 1 + apps/backend/openapi.json | 65 +++++++++++++++++++ .../tests/api/test_route_protection.py | 3 + 3 files changed, 69 insertions(+) diff --git a/apps/backend/app/api/v1/endpoints/auth.py b/apps/backend/app/api/v1/endpoints/auth.py index 548c0e2..9fd374f 100644 --- a/apps/backend/app/api/v1/endpoints/auth.py +++ b/apps/backend/app/api/v1/endpoints/auth.py @@ -325,6 +325,7 @@ async def forgot_password( "/reset-password/validate", response_model=ResetTokenValidationResponse, summary="Vérifie sans le consommer si un lien de réinitialisation est encore valide", + responses=REPONSE_VALIDATION, ) async def validate_reset_token(token: str, service: AuthServiceDep) -> ResetTokenValidationResponse: return ResetTokenValidationResponse(valid=await service.is_reset_token_valid(token=token)) diff --git a/apps/backend/openapi.json b/apps/backend/openapi.json index 7a4fe8a..3f414fa 100644 --- a/apps/backend/openapi.json +++ b/apps/backend/openapi.json @@ -491,6 +491,58 @@ } } }, + "/api/v1/auth/reset-password/validate": { + "get": { + "tags": [ + "auth" + ], + "summary": "Vérifie sans le consommer si un lien de réinitialisation est encore valide", + "operationId": "validate_reset_token_api_v1_auth_reset_password_validate_get", + "parameters": [ + { + "name": "token", + "in": "query", + "required": true, + "schema": { + "type": "string", + "title": "Token" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ResetTokenValidationResponse" + } + } + } + }, + "500": { + "description": "Erreur interne. `correlation` identifie la trace côté serveur, qui n'est pas renvoyée au client.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InternalErrorResponse" + } + } + } + }, + "422": { + "description": "Corps invalide. Le détail nomme le champ fautif et le type d'erreur, jamais la valeur envoyée.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ValidationErrorResponse" + } + } + } + } + } + } + }, "/api/v1/auth/reset-password": { "post": { "tags": [ @@ -2166,6 +2218,19 @@ ], "title": "ResetPasswordRequest" }, + "ResetTokenValidationResponse": { + "properties": { + "valid": { + "type": "boolean", + "title": "Valid" + } + }, + "type": "object", + "required": [ + "valid" + ], + "title": "ResetTokenValidationResponse" + }, "Role": { "type": "string", "enum": [ diff --git a/apps/backend/tests/api/test_route_protection.py b/apps/backend/tests/api/test_route_protection.py index 1080dce..734c9db 100644 --- a/apps/backend/tests/api/test_route_protection.py +++ b/apps/backend/tests/api/test_route_protection.py @@ -22,6 +22,9 @@ ROUTES_PUBLIQUES = frozenset( # Protégée par le jeton dans le corps de la requête, pas par un `Principal` : aucune # authentification préalable ne s'applique, c'est la validité du jeton qui tranche. ("POST", "/api/v1/auth/reset-password"), + # Même raison : lecture seule, protégée par le jeton passé en paramètre, pas par un + # `Principal`. Le jeton est un secret de 256 bits, non brute-forçable. + ("GET", "/api/v1/auth/reset-password/validate"), ("GET", "/metrics"), } )