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

  1. Send a token obtained from POST /token (here, literally the username 'johndoe').
  2. In Swagger UI, use Authorize and complete the OAuth2 password flow before calling protected routes.
  3. 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

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

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)