{"id":"b7bb8d01417dc191","repo":"tiangolo/fastapi","slug":"incorrect-username-or-password","errorCode":null,"errorMessage":"Incorrect username or password","messagePattern":"Incorrect username or password","errorType":"http","errorClass":"HTTPException","httpStatus":400,"severity":"error","filePath":"docs_src/security/tutorial003_an_py310.py","lineNumber":81,"sourceCode":"            detail=\"Not authenticated\",\n            headers={\"WWW-Authenticate\": \"Bearer\"},\n        )\n    return user\n\n\nasync def get_current_active_user(\n    current_user: Annotated[User, Depends(get_current_user)],\n):\n    if current_user.disabled:\n        raise HTTPException(status_code=400, detail=\"Inactive user\")\n    return current_user\n\n\n@app.post(\"/token\")\nasync def login(form_data: Annotated[OAuth2PasswordRequestForm, Depends()]):\n    user_dict = fake_users_db.get(form_data.username)\n    if not user_dict:\n        raise HTTPException(status_code=400, detail=\"Incorrect username or password\")\n    user = UserInDB(**user_dict)\n    hashed_password = fake_hash_password(form_data.password)\n    if not hashed_password == user.hashed_password:\n        raise HTTPException(status_code=400, detail=\"Incorrect username or password\")\n\n    return {\"access_token\": user.username, \"token_type\": \"bearer\"}\n\n\n@app.get(\"/users/me\")\nasync def read_users_me(\n    current_user: Annotated[User, Depends(get_current_active_user)],\n):\n    return current_user\n","sourceCodeStart":63,"sourceCodeEnd":95,"githubUrl":"https://github.com/tiangolo/fastapi/blob/42a41db11f6882807ac3c057b942178d53b97438/docs_src/security/tutorial003_an_py310.py#L63-L95","documentation":"Raised by the `/token` login handler with HTTP 400 when `fake_users_db.get(form_data.username)` returns nothing - i.e. the submitted username does not exist. The message deliberately does not distinguish 'no such user' from 'wrong password' to avoid user enumeration.","triggerScenarios":"`POST /token` (form-encoded) with `username=nobody` where `nobody` is not a key in `fake_users_db`. The check is `if not user_dict:` at line 80.","commonSituations":"Typo in the username; client posting JSON instead of OAuth2 form data so `form_data.username` is empty; user not yet provisioned; environment DB missing the seed users.","solutions":["Send credentials as `application/x-www-form-urlencoded` (OAuth2PasswordRequestForm), not JSON.","Confirm the username exists in the user store (e.g. `johndoe`).","Check the password too - the same message is reused for wrong password (line 85)."],"exampleFix":"# before\ncurl -X POST http://localhost:8000/token -H 'Content-Type: application/json' \\\n  -d '{\"username\":\"johndoe\",\"password\":\"secret\"}'\n# after\ncurl -X POST http://localhost:8000/token \\\n  -d 'username=johndoe&password=secret'","handlingStrategy":"try-catch","validationCode":"def is_form_login(username: str, password: str) -> bool:\n    return bool(username) and bool(password)\n# POST with Content-Type: application/x-www-form-urlencoded when True","typeGuard":"def is_credentials_error(resp) -> bool:\n    return getattr(resp, 'status_code', None) in (400, 401) and \\\n           resp.json().get('detail') == 'Incorrect username or password'","tryCatchPattern":"try:\n    tok = client.post('/token', data={'username': u, 'password': p})\n    tok.raise_for_status()\nexcept httpx.HTTPStatusError as e:\n    if e.response.json().get('detail') == 'Incorrect username or password':\n        show_user_friendly_login_error()  # do NOT reveal which part was wrong\n    raise","preventionTips":["Always POST /token as form-encoded, per the OAuth2 password grant spec.","Show the user the exact generic message the server returns - do not add detail that enables enumeration.","Rate-limit /token to blunt brute-force attempts."],"tags":["authentication","oauth2","login","security","fastapi"],"analyzedSha":"42a41db11f6882807ac3c057b942178d53b97438","analyzedAt":"2026-08-04T19:23:32.007Z","schemaVersion":2}