OAuth2 Password Flow — Login That Issues Tokens
IntermediateOAuth2PasswordRequestForm receives username/password at /token, the app returns a bearer token, and OAuth2PasswordBearer extracts it on every protected call — the login skeleton behind the Swagger Authorize button.
Overview
APIs authenticate with tokens, not server-side sessions: the client logs in once at a token endpoint, receives an access token, and sends it as Authorization: Bearer <token> on every request — stateless, so any instance behind the load balancer can verify it. FastAPI ships the two halves of this contract: OAuth2PasswordRequestForm parses the standard form-encoded login body, and OAuth2PasswordBearer is a dependency that pulls the bearer token off incoming requests (returning 401 with the correct WWW-Authenticate header when absent). Declaring them also lights up the Authorize button in /docs, so the whole team can log in and test protected endpoints from the browser. This chapter wires the skeleton with a fake token; the next two chapters supply the real hashing and real JWTs.
The /token Endpoint + the Bearer Extractor
tokenUrl tells the docs where login lives. The form is form-encoded (username/password fields — the OAuth2 spec), not JSON. Wrong credentials return 401, never "wrong password" specifics.
from fastapi import Depends, FastAPI, HTTPException, status
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
app = FastAPI()
# Extracts "Authorization: Bearer xyz" → "xyz"; 401 if header missing
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
FAKE_DB = {"asha@nitk.edu.in": {"name": "Asha", "hashed": "bcrypt$..."}}
@app.post("/token")
def login(form: OAuth2PasswordRequestForm = Depends()):
user = FAKE_DB.get(form.username) # form fields: username, password
password_ok = user is not None # verify_password() → next chapter
if not user or not password_ok:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="incorrect username or password", # deliberately vague:
headers={"WWW-Authenticate": "Bearer"}, # never reveal WHICH was wrong
)
return {
"access_token": "fake-token-" + form.username, # real JWT → next chapter
"token_type": "bearer", # spec-required shape
}
@app.get("/profile")
def profile(token: str = Depends(oauth2_scheme)):
return {"your_token": token} # decoding it comes next
# The wire format:
# POST /token Content-Type: application/x-www-form-urlencoded
# username=asha%40nitk.edu.in&password=s3cret
# → {"access_token": "...", "token_type": "bearer"}
# GET /profile Authorization: Bearer <access_token>Why Tokens (Not Sessions) — and What /docs Gives You
Stateless verification is what lets APIs scale horizontally and serve mobile + SPA + server clients uniformly. And because the scheme is declared, Swagger UI handles the whole login dance for manual testing.
# Sessions vs tokens for APIs:
#
# Cookie sessions Bearer tokens
# ─────────────────── ─────────────────────
# state lives server-side state lives IN the token (signed)
# sticky sessions / shared store any instance verifies independently
# browser-centric (cookies) browsers, apps, curl, services alike
# CSRF protection needed no cookies → no classic CSRF
#
# (Server-rendered websites still legitimately use cookie sessions —
# YesStudy-style SPAs + mobile apps are why APIs standardised on bearer.)
# What declaring OAuth2PasswordBearer buys you in /docs:
# 1. A padlock icon on every protected endpoint
# 2. The "Authorize" button → posts to tokenUrl ("token")
# 3. Swagger stores the token and attaches Authorization: Bearer
# to every "Try it out" call — the whole team tests auth flows
# without curl or Postman.
# Two rules already visible in this skeleton:
# - 401 responses include WWW-Authenticate: Bearer (the HTTP spec says so;
# FastAPI's helpers do it — keep it when raising manually)
# - Login errors stay vague. "User not found" vs "wrong password"
# tells an attacker which emails are registered (user enumeration).Key Points to Remember
- 1OAuth2PasswordRequestForm parses the form-encoded login; response is {access_token, token_type}
- 2OAuth2PasswordBearer extracts the bearer token and 401s when it is missing
- 3Tokens are stateless — any instance verifies; no sticky sessions, no CSRF cookies
- 4Login failures stay vague (no user enumeration) and carry WWW-Authenticate: Bearer
Interview Questions
Sign in to ask AriaWhy do APIs prefer bearer tokens over cookie sessions? When are sessions still right?
What is user enumeration and how does your login endpoint avoid it?
Walk through what happens in Swagger UI when you click Authorize.
Ask Aria about OAuth2 Password Flow — Login That Issues Tokens
Your personal AI tutor — ask anything about this concept
Revision Status
Personal Notes
Sign in to save personal notes for this topic.
Discussion
Sign in to join the discussion.