{"record":{"id":"42001f4e533714a0","repo":"tiangolo/fastapi","slug":"incorrect-username-or-password-42001f","errorCode":null,"errorMessage":"Incorrect username or password","messagePattern":"Incorrect username or password","errorType":"http","errorClass":"HTTPException","httpStatus":401,"severity":"error","filePath":"docs_src/security/tutorial007_py310.py","lineNumber":23,"sourceCode":"\napp = FastAPI()\n\nsecurity = HTTPBasic()\n\n\ndef get_current_username(credentials: HTTPBasicCredentials = Depends(security)):\n    current_username_bytes = credentials.username.encode(\"utf8\")\n    correct_username_bytes = b\"stanleyjobson\"\n    is_correct_username = secrets.compare_digest(\n        current_username_bytes, correct_username_bytes\n    )\n    current_password_bytes = credentials.password.encode(\"utf8\")\n    correct_password_bytes = b\"swordfish\"\n    is_correct_password = secrets.compare_digest(\n        current_password_bytes, correct_password_bytes\n    )\n    if not (is_correct_username and is_correct_password):\n        raise HTTPException(\n            status_code=status.HTTP_401_UNAUTHORIZED,\n            detail=\"Incorrect username or password\",\n            headers={\"WWW-Authenticate\": \"Basic\"},\n        )\n    return credentials.username\n\n\n@app.get(\"/users/me\")\ndef read_current_user(username: str = Depends(get_current_username)):\n    return {\"username\": username}\n","sourceCodeStart":5,"sourceCodeEnd":34,"githubUrl":"https://github.com/tiangolo/fastapi/blob/3e8d1526d83a90aaf7d6eb6dc682bf150f180b25/docs_src/security/tutorial007_py310.py#L5-L34","documentation":"Legacy-DI variant of error 57. get_current_username compares username/password with secrets.compare_digest and raises HTTP 401 'Incorrect username or password' with WWW-Authenticate: Basic when either fails. The combined AND prevents timing short-circuit. Semantics identical to the Annotated version; only the DI syntax differs.","triggerScenarios":"GET /users/me with Basic credentials whose username is not 'stanleyjobson' or whose password is not 'swordfish' (or with no Authorization header).","commonSituations":"Credential typo; cached browser creds; mis-encoded test header; rotated demo creds.","solutions":["Send Authorization: Basic <base64('stanleyjobson:swordfish')>.","Clear cached Basic credentials in the browser and re-authenticate.","Externalize credentials to env/secrets instead of hardcoding."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"import base64, secrets\nUSER, PW = b\"stanleyjobson\", b\"swordfish\"\ndef basic_header(username: str, password: str) -> str | None:\n    if not (secrets.compare_digest(username.encode(), USER)\n            and secrets.compare_digest(password.encode(), PW)):\n        return None\n    token = base64.b64encode(f\"{username}:{password}\".encode()).decode()\n    return f\"Basic {token}\"","typeGuard":"from typing import TypeGuard\ndef is_valid_basic_pair(pair: tuple) -> TypeGuard[tuple[str, str]]:\n    u, p = pair\n    return isinstance(u, str) and isinstance(p, str) and u and p","tryCatchPattern":"import httpx\ntry:\n    r = httpx.get(\"/users/me\", headers={\"Authorization\": basic_header(u, p)})\n    r.raise_for_status()\nexcept httpx.HTTPStatusError as e:\n    if e.response.status_code == 401:\n        prompt_credentials()","preventionTips":["Encode the Basic header correctly with base64.","Clear cached browser Basic creds after a change.","Externalize credentials to env/secrets."],"tags":["fastapi","authentication","http-basic","secrets","legacy-di"],"backgroundTag":null,"analyzedSha":"3e8d1526d83a90aaf7d6eb6dc682bf150f180b25","analyzedAt":"2026-08-11T02:34:52.986Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}