fix(storefront-auth): forgot/reset password accept JSON body, not query
Some checks failed
CI / ruff (push) Successful in 16s
CI / validate (push) Has been cancelled
CI / dependency-scanning (push) Has been cancelled
CI / docs (push) Has been cancelled
CI / deploy (push) Has been cancelled
CI / pytest (push) Has been cancelled

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: <Schema>`. 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) <noreply@anthropic.com>
This commit is contained in:
2026-05-24 23:06:10 +02:00
parent dd1f9af811
commit 478c3a9c50
3 changed files with 26 additions and 4 deletions

View File

@@ -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()

View File

@@ -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",

View File

@@ -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."""