affaan-m/ECC · error · HTTPException
Incorrect username or password
Error message
Incorrect username or password
What it means
In the `login` route (OAuth2 password flow), `UserService.authenticate(username, password)` returns `None` when credentials do not validate; the handler raises `HTTPException(401, "Incorrect username or password", headers={"WWW-Authenticate":"Bearer"})`. The deliberate vague message does not distinguish "no such user" from "wrong password" to prevent user enumeration.
Source
Thrown at skills/fastapi-patterns/SKILL.md:295
service = UserService(db)
try:
user = await service.update(user_id, payload)
except DuplicateUserError:
raise HTTPException(status_code=400, detail="Email already registered")
if user is None:
raise HTTPException(status_code=404, detail="User not found")
return user
@router.post("/token")
async def login(
form_data: Annotated[OAuth2PasswordRequestForm, Depends()],
db: DbDep,
) -> dict[str, str]:
service = UserService(db)
token = await service.authenticate(form_data.username, form_data.password)
if token is None:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Incorrect username or password",
headers={"WWW-Authenticate": "Bearer"},
)
return {"access_token": token, "token_type": "bearer"}
```
---
## Service Layer
```python
# app/services/user_service.py
from datetime import datetime, timedelta, timezone
from jose import jwt
from passlib.context import CryptContext
from sqlalchemy import func, selectView on GitHub (pinned to 01e15490f0)
Solutions
- Front-end: show the same "invalid credentials" message for both unknown-user and wrong-password; never reveal which.
- Ensure `UserService.authenticate` runs the password hash verify even when the user is not found, so timing does not leak existence.
- Confirm the client sends `application/x-www-form-urlencoded` with `username` and `password` fields (OAuth2 form), not JSON.
- Check rate limiting on `/token` to slow brute force.
Example fix
# before
token = await service.authenticate(form_data.username, form_data.password)
if token is None:
raise HTTPException(status_code=401, detail="Incorrect username or password", headers={"WWW-Authenticate":"Bearer"})
# after — identical path; add brute-force throttle
from slowapi import Limiter
@router.post("/token")
@limiter.limit("5/minute")
async def login(request: Request, form_data: ...):
...
if token is None:
raise HTTPException(status_code=401, detail="Incorrect username or password", headers={"WWW-Authenticate":"Bearer"}) Defensive patterns
Strategy: try-catch
Validate before calling
null
Type guard
null
Try / catch
from fastapi import HTTPException
try:
token = await client.post('/token', data={'username':u,'password':p})
except HTTPException as e:
if e.status_code == 401:
show_error('Incorrect username or password') # do NOT reveal which
raise Prevention
- Run the password verify even when the user is missing so timing is uniform.
- Use `secrets.compare_digest`/passlib for the compare.
- Rate-limit `/token` (e.g. slowapi 5/min/IP) to blunt brute force.
When it happens
Trigger: POST `/token` with `OAuth2PasswordRequestForm` whose `username` does not exist OR whose `password` does not hash-match. Either case yields `token is None` → 401.
Common situations: Wrong password typo. User registered with email but tries username. Account was deleted. Timing-attack-prone compare in `authenticate` (use `secrets.compare_digest` / `pwd_ctx.verify`).
Related errors
- Passwords do not match
- Inactive user
- Email already registered
- Not authorized
- Invalid ECC repo root: missing install script at ${installAp
AI-assisted analysis of affaan-m/ECC@01e15490f0 (2026-08-13).
Data as JSON: /api/errors/bb875db63562b689.
Report an issue: GitHub.