tiangolo/fastapi · error · HTTPException
Not authenticated
Error message
Not authenticated
What it means
Raised by get_current_user when fake_decode_token(token) returns None, i.e. the bearer token does not map to any user. It carries WWW-Authenticate: Bearer so clients re-prompt. Distinguish this from the automatic 401 that OAuth2PasswordBearer itself raises (same default detail 'Not authenticated') when the Authorization header is missing entirely — this line fires only when a token was supplied but did not resolve.
Solutions
- Send a token obtained from POST /token (here, literally the username 'johndoe').
- In Swagger UI, use Authorize and complete the OAuth2 password flow before calling protected routes.
- Replace fake_decode_token with real JWT verification before trusting this path in anything beyond the tutorial.
Example fix
// before
user = fake_decode_token(token)
if not user:
raise HTTPException(status_code=401, detail="Not authenticated", headers={"WWW-Authenticate": "Bearer"})
// after (real JWT)
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
user = get_user(db, payload.get("sub"))
if user is None:
raise HTTPException(status_code=401, detail="Not authenticated", headers={"WWW-Authenticate": "Bearer"}) Defensive patterns
Strategy: validation
Validate before calling
# Ensure a non-empty bearer token maps to a known user before the call
KNOWN_USERS = {"johndoe", "alice"}
def token_resolves(token: str) -> bool:
return bool(token) and token in KNOWN_USERS Type guard
from typing import TypeGuard
def is_known_token(token: str) -> TypeGuard[str]:
return isinstance(token, str) and token in {"johndoe", "alice"} Try / catch
import httpx
try:
r = httpx.get("/users/me", headers={"Authorization": f"Bearer {token}"})
except httpx.HTTPStatusError as e:
if e.response.status_code == 401:
# re-run the OAuth2 password flow to obtain a fresh token
token = login_and_get_token() Prevention
- Always obtain the token from POST /token rather than hand-typing one.
- In Swagger UI, use Authorize to complete the OAuth2 flow before calling protected routes.
- Replace the fake 'token==username' decode with real JWT verification before relying on it.
When it happens
Trigger: GET /users/me with Authorization: Bearer <value> where <value> is not a fake_users_db key (not 'johndoe'/'alice'), or 'Bearer ' with an empty token. Because this fake implementation treats the token AS the username, any non-username token fails.
Common situations: Client sent an opaque or stale token; developer confused that there is no real validation (token==username); a token minted before the user was deleted from the db.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
AI-assisted analysis of tiangolo/fastapi@3e8d1526d8 (2026-08-11).
Data as JSON: /api/errors/8d9d29279d5aba54.
Report an issue: GitHub.
Appendix: source
Thrown at docs_src/security/tutorial003_py310.py:59
def get_user(db, username: str):
if username in db:
user_dict = db[username]
return UserInDB(**user_dict)
def fake_decode_token(token):
# This doesn't provide any security at all
# Check the next version
user = get_user(fake_users_db, token)
return user
async def get_current_user(token: str = Depends(oauth2_scheme)):
user = fake_decode_token(token)
if not user:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Not authenticated",
headers={"WWW-Authenticate": "Bearer"},
)
return user
async def get_current_active_user(current_user: User = Depends(get_current_user)):
if current_user.disabled:
raise HTTPException(status_code=400, detail="Inactive user")
return current_user
@app.post("/token")
async def login(form_data: OAuth2PasswordRequestForm = Depends()):
user_dict = fake_users_db.get(form_data.username)
if not user_dict:
raise HTTPException(status_code=400, detail="Incorrect username or password")View on GitHub (pinned to 3e8d1526d8)