From 478c3a9c505759abadac26be4654c9498e16ef47 Mon Sep 17 00:00:00 2001 From: Samir Boulahtit Date: Sun, 24 May 2026 23:06:10 +0200 Subject: [PATCH] fix(storefront-auth): forgot/reset password accept JSON body, not query MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit POST /api/v1/storefront/auth/forgot-password and .../reset-password were both declared with bare `email: str` / `reset_token: str, new_password: str` parameters. FastAPI treats unannotated str params as query parameters, so the frontend's JSON body was ignored and the endpoint 422'd with "missing query parameter 'email'". The docstrings on both endpoints already said "Request Body" — intent was clear, implementation drifted. Add two new Pydantic body schemas in tenancy/schemas/auth.py: PasswordResetRequest { email: str } (forgot) PasswordResetConfirm { reset_token: str, new_password: str } (reset) Re-export from tenancy/schemas/__init__.py, import in customers/routes/api/storefront.py, and switch both endpoint signatures to take `body: `. Internal usage reads body.email / body.reset_token / body.new_password. Surfaced during Test 5 when user clicked "forgot password" on the customer storefront login page to set a password for the first time after a self-enrollment flow. Co-Authored-By: Claude Opus 4.7 (1M context) --- app/modules/customers/routes/api/storefront.py | 13 +++++++++---- app/modules/tenancy/schemas/__init__.py | 4 ++++ app/modules/tenancy/schemas/auth.py | 13 +++++++++++++ 3 files changed, 26 insertions(+), 4 deletions(-) diff --git a/app/modules/customers/routes/api/storefront.py b/app/modules/customers/routes/api/storefront.py index c9bf83d7..bc97ee4f 100644 --- a/app/modules/customers/routes/api/storefront.py +++ b/app/modules/customers/routes/api/storefront.py @@ -49,6 +49,8 @@ from app.modules.messaging.services.email_service import ( from app.modules.tenancy.exceptions import StoreNotFoundException from app.modules.tenancy.schemas.auth import ( LogoutResponse, + PasswordResetConfirm, + PasswordResetRequest, PasswordResetRequestResponse, PasswordResetResponse, UserLogin, @@ -230,7 +232,9 @@ def customer_logout(request: Request, response: Response): @router.post("/auth/forgot-password", response_model=PasswordResetRequestResponse) # public -def forgot_password(request: Request, email: str, db: Session = Depends(get_db)): +def forgot_password( + request: Request, body: PasswordResetRequest, db: Session = Depends(get_db) +): """ Request password reset for customer. @@ -245,6 +249,7 @@ def forgot_password(request: Request, email: str, db: Session = Depends(get_db)) if not store: raise StoreNotFoundException("context", identifier_type="subdomain") + email = body.email logger.debug( f"[CUSTOMER_STOREFRONT] forgot_password for store {store.subdomain}", extra={ @@ -299,7 +304,7 @@ def forgot_password(request: Request, email: str, db: Session = Depends(get_db)) @router.post("/auth/reset-password", response_model=PasswordResetResponse) # public def reset_password( - request: Request, reset_token: str, new_password: str, db: Session = Depends(get_db) + request: Request, body: PasswordResetConfirm, db: Session = Depends(get_db) ): """ Reset customer password using reset token. @@ -323,8 +328,8 @@ def reset_password( customer = customer_service.validate_and_reset_password( db=db, store_id=store.id, - reset_token=reset_token, - new_password=new_password, + reset_token=body.reset_token, + new_password=body.new_password, ) db.commit() diff --git a/app/modules/tenancy/schemas/__init__.py b/app/modules/tenancy/schemas/__init__.py index 77282922..1de159e1 100644 --- a/app/modules/tenancy/schemas/__init__.py +++ b/app/modules/tenancy/schemas/__init__.py @@ -53,6 +53,8 @@ from app.modules.tenancy.schemas.auth import ( LoginResponse, LogoutResponse, OwnedMerchantSummary, + PasswordResetConfirm, + PasswordResetRequest, PasswordResetRequestResponse, PasswordResetResponse, PlatformSelectResponse, @@ -153,6 +155,8 @@ __all__ = [ "LoginResponse", "LogoutResponse", "OwnedMerchantSummary", + "PasswordResetConfirm", + "PasswordResetRequest", "PasswordResetRequestResponse", "PasswordResetResponse", "PlatformSelectResponse", diff --git a/app/modules/tenancy/schemas/auth.py b/app/modules/tenancy/schemas/auth.py index f5b7a783..e9b5ab8f 100644 --- a/app/modules/tenancy/schemas/auth.py +++ b/app/modules/tenancy/schemas/auth.py @@ -183,12 +183,25 @@ class LogoutResponse(BaseModel): message: str +class PasswordResetRequest(BaseModel): + """Schema for password reset request body (customer / storefront forgot-password).""" + + email: str + + class PasswordResetRequestResponse(BaseModel): """Schema for password reset request response.""" message: str +class PasswordResetConfirm(BaseModel): + """Schema for password reset confirm body (customer / storefront reset-password).""" + + reset_token: str + new_password: str + + class PasswordResetResponse(BaseModel): """Schema for password reset response."""