Description
The PATCH /users/me endpoint allows users to change their password and email without proper security checks, bypassing the secure /users/me/change-password endpoint.
Steps to Reproduce
- Register a user with email
test@example.com and password OldPassword123
- Login to get JWT token
- Call
PATCH /users/me with {"password": "NewPassword456"} (no old password required)
- Try logging in with old password - fails
- Try logging in with new password - succeeds
Expected Behavior
- Password change should require old password verification (like
/users/me/change-password does)
- Email change should require verification of new email ownership
Actual Behavior
- Password can be changed without providing old password
- Email can be changed without verifying ownership of new address
- Both changes persist to database immediately
Root Cause
fastapi-users provides PATCH /users/me with UserUpdate schema that includes password: Optional[str] and email: Optional[EmailStr]. The custom /users/me/change-password endpoint was added with proper old password
verification, but the PATCH endpoint still exists and bypasses this security measure.
Files involved:
backend/api/users.py:36-40 - mounts fastapi-users router
backend/schemas/user.py:42-62 - UserUpdate schema
Possible Solutions
- Option A: Override UserUpdate to exclude password field
class UserUpdate(schemas.BaseUserUpdate):
password: None = None
@field_validator("password", mode="before")
@classmethod
def force_password_none(cls, v):
return None
- Option B: Block both password and email changes via PATCH
class UserUpdate(schemas.BaseUserUpdate):
password: Optional[str] = None
email: Optional[str] = None
@field_validator("password", "email", mode="before")
@classmethod
def block_sensitive_fields(cls, v):
return None
Description
The
PATCH /users/meendpoint allows users to change their password and email without proper security checks, bypassing the secure/users/me/change-passwordendpoint.Steps to Reproduce
test@example.comand passwordOldPassword123PATCH /users/mewith{"password": "NewPassword456"}(no old password required)Expected Behavior
/users/me/change-passworddoes)Actual Behavior
Root Cause
fastapi-users provides
PATCH /users/mewithUserUpdateschema that includespassword: Optional[str]andemail: Optional[EmailStr]. The custom/users/me/change-passwordendpoint was added with proper old passwordverification, but the PATCH endpoint still exists and bypasses this security measure.
Files involved:
backend/api/users.py:36-40- mounts fastapi-users routerbackend/schemas/user.py:42-62- UserUpdate schemaPossible Solutions