[{"data":1,"prerenderedAt":88},["ShallowReactive",2],{"qa-\u002Ffastapi\u002Fsecurity\u002Foauth2":3},{"page":4,"siblings":79,"blog":70},{"id":5,"title":6,"body":7,"description":11,"difficulty":14,"extension":15,"framework":16,"frameworkSlug":17,"meta":18,"navigation":19,"order":20,"path":21,"questions":22,"questionsCount":69,"related":70,"seo":71,"seoDescription":72,"stem":73,"subtopic":74,"topic":75,"topicSlug":76,"updated":77,"__hash__":78},"qa\u002Ffastapi\u002Fsecurity\u002Foauth2.md","Oauth2",{"type":8,"value":9,"toc":10},"minimark",[],{"title":11,"searchDepth":12,"depth":12,"links":13},"",2,[],"hard","md","FastAPI","fastapi",{},true,1,"\u002Ffastapi\u002Fsecurity\u002Foauth2",[23,28,32,36,40,44,49,53,57,61,65],{"id":24,"difficulty":25,"q":26,"a":27},"oauth2-password-bearer","medium","What is `OAuth2PasswordBearer` in FastAPI and what does it do?","`OAuth2PasswordBearer` is a callable security dependency that:\n1. Extracts the Bearer token from the `Authorization: Bearer \u003Ctoken>` header.\n2. Returns the token string to your handler.\n3. Registers the OAuth2 password flow in the OpenAPI schema so Swagger UI shows\n   an \"Authorize\" dialog.\n\n```python\nfrom fastapi.security import OAuth2PasswordBearer\nfrom fastapi import Depends\n\noauth2_scheme = OAuth2PasswordBearer(tokenUrl=\"\u002Ftoken\")\n\n@app.get(\"\u002Fme\")\nasync def current_user(token: str = Depends(oauth2_scheme)):\n    # token is the raw Bearer string — validate it yourself\n    payload = decode_jwt(token)\n    return payload\n```\n\n`OAuth2PasswordBearer` only extracts the token — it does NOT validate it.\nValidation is your responsibility.\n\nRule of thumb: use `OAuth2PasswordBearer` so Swagger UI gets the Authorize button;\ndo JWT decoding\u002Fverification in a separate `get_current_user` dependency.\n",{"id":29,"difficulty":25,"q":30,"a":31},"token-endpoint","How do you implement the `\u002Ftoken` endpoint for the OAuth2 password flow in FastAPI?","Accept `OAuth2PasswordRequestForm` (username + password as form data) and return\na token response:\n\n```python\nfrom fastapi import Depends\nfrom fastapi.security import OAuth2PasswordRequestForm\nfrom jose import jwt\n\n@app.post(\"\u002Ftoken\")\nasync def login(form: OAuth2PasswordRequestForm = Depends()):\n    user = await authenticate_user(form.username, form.password)\n    if not user:\n        raise HTTPException(\n            status_code=400, detail=\"Incorrect username or password\"\n        )\n    access_token = jwt.encode(\n        {\"sub\": str(user.id), \"exp\": datetime.utcnow() + timedelta(minutes=30)},\n        settings.secret_key, algorithm=\"HS256\"\n    )\n    return {\"access_token\": access_token, \"token_type\": \"bearer\"}\n```\n\nThe response must have `access_token` and `token_type` fields — Swagger UI\nreads these to store the token.\n\nRule of thumb: never return the user's password (even hashed) from `\u002Ftoken`;\nreturn only the token and its type.\n",{"id":33,"difficulty":25,"q":34,"a":35},"password-hashing","How do you hash and verify passwords securely in FastAPI?","Use `passlib` with `bcrypt`:\n\n```python\nfrom passlib.context import CryptContext\n\npwd_context = CryptContext(schemes=[\"bcrypt\"], deprecated=\"auto\")\n\ndef hash_password(plain: str) -> str:\n    return pwd_context.hash(plain)\n\ndef verify_password(plain: str, hashed: str) -> bool:\n    return pwd_context.verify(plain, hashed)\n\nasync def authenticate_user(username: str, password: str):\n    user = await db.get_user_by_username(username)\n    if not user or not verify_password(password, user.password_hash):\n        return None\n    return user\n```\n\nNever store plain-text passwords. `bcrypt` automatically includes a salt and\nis resistant to GPU-accelerated brute-force attacks.\n\nRule of thumb: use `passlib.CryptContext` with `bcrypt` — it handles salting,\nalgorithm upgrades (`deprecated=\"auto\"`) and constant-time comparison.\n",{"id":37,"difficulty":25,"q":38,"a":39},"get-current-user-dep","Show the standard pattern for a `get_current_user` dependency in FastAPI.","```python\nfrom fastapi import Depends, HTTPException\nfrom fastapi.security import OAuth2PasswordBearer\nfrom jose import jwt, JWTError\nfrom pydantic import BaseModel\n\noauth2_scheme = OAuth2PasswordBearer(tokenUrl=\"\u002Ftoken\")\n\nclass TokenData(BaseModel):\n    user_id: int\n\nasync def get_current_user(token: str = Depends(oauth2_scheme)):\n    credentials_exception = HTTPException(\n        status_code=401,\n        detail=\"Could not validate credentials\",\n        headers={\"WWW-Authenticate\": \"Bearer\"},\n    )\n    try:\n        payload = jwt.decode(token, settings.secret_key, algorithms=[\"HS256\"])\n        user_id: int = payload.get(\"sub\")\n        if user_id is None:\n            raise credentials_exception\n    except JWTError:\n        raise credentials_exception\n    user = await db.get_user(user_id)\n    if not user:\n        raise credentials_exception\n    return user\n\n@app.get(\"\u002Fme\")\nasync def me(user: User = Depends(get_current_user)):\n    return user\n```\n\nRule of thumb: always include `headers={\"WWW-Authenticate\": \"Bearer\"}` on 401\nresponses — RFC 7235 requires it and some clients depend on it.\n",{"id":41,"difficulty":14,"q":42,"a":43},"oauth2-scopes","How do you implement OAuth2 scopes in FastAPI?","Define scopes in `OAuth2PasswordBearer`, encode them in the token, and verify\nthem with `SecurityScopes`:\n\n```python\nfrom fastapi import Security, SecurityScopes\nfrom fastapi.security import OAuth2PasswordBearer\n\noauth2_scheme = OAuth2PasswordBearer(\n    tokenUrl=\"\u002Ftoken\",\n    scopes={\"read\": \"Read items\", \"write\": \"Create and update items\"},\n)\n\nasync def get_current_user(\n    security_scopes: SecurityScopes,\n    token: str = Depends(oauth2_scheme),\n):\n    payload = jwt.decode(token, settings.secret_key, algorithms=[\"HS256\"])\n    token_scopes = payload.get(\"scopes\", [])\n    for scope in security_scopes.scopes:\n        if scope not in token_scopes:\n            raise HTTPException(\n                403,\n                detail=f\"Scope '{scope}' required\",\n                headers={\"WWW-Authenticate\": f'Bearer scope=\"{security_scopes.scope_str}\"'},\n            )\n    return payload\n\n@app.get(\"\u002Fitems\")\nasync def list_items(user = Security(get_current_user, scopes=[\"read\"])):\n    ...\n```\n\nUse `Security()` (not `Depends()`) to pass scope requirements.\n\nRule of thumb: encode granted scopes in the JWT at login time; verify required\nscopes in `get_current_user` — keep scope logic in the auth dependency, not handlers.\n",{"id":45,"difficulty":46,"q":47,"a":48},"inactive-user","easy","How do you block inactive or disabled users from accessing protected routes?","Add an `is_active` check in `get_current_user` or a separate `require_active_user`\ndependency:\n\n```python\nasync def get_current_active_user(\n    user: User = Depends(get_current_user),\n) -> User:\n    if not user.is_active:\n        raise HTTPException(status_code=400, detail=\"Inactive user\")\n    return user\n\n@app.get(\"\u002Forders\")\nasync def orders(user: User = Depends(get_current_active_user)):\n    ...\n```\n\nSeparating \"can we trust the token?\" (`get_current_user`) from \"is the user\nallowed?\" (`get_current_active_user`) makes the dependency graph cleaner and\neach function independently testable.\n\nRule of thumb: chain fine-grained permission checks as separate dependencies —\ndon't put all guard logic in one giant `get_current_user` function.\n",{"id":50,"difficulty":14,"q":51,"a":52},"refresh-token","How would you implement refresh tokens in a FastAPI application?","Issue two tokens at login: a short-lived access token and a long-lived refresh token.\nStore the refresh token hash in the DB. Provide a `\u002Frefresh` endpoint:\n\n```python\n@app.post(\"\u002Frefresh\")\nasync def refresh_token(refresh_token: str = Body(...)):\n    # 1. Verify it's a valid JWT\n    try:\n        payload = jwt.decode(refresh_token, REFRESH_SECRET, algorithms=[\"HS256\"])\n    except JWTError:\n        raise HTTPException(401, \"Invalid refresh token\")\n\n    # 2. Check it exists and isn't revoked in the DB\n    stored = await db.get_refresh_token(payload[\"jti\"])\n    if not stored or stored.revoked:\n        raise HTTPException(401, \"Refresh token revoked\")\n\n    # 3. Issue new access token (optionally rotate refresh token)\n    new_access = create_access_token(payload[\"sub\"])\n    return {\"access_token\": new_access, \"token_type\": \"bearer\"}\n```\n\nKey design decisions: rotate refresh tokens on each use (reduces replay risk),\nstore a `jti` (JWT ID) for revocation, use a different signing secret for refresh tokens.\n\nRule of thumb: keep access tokens short (15-30 min); keep refresh tokens\nlong (7-30 days) but revocable via a DB lookup.\n",{"id":54,"difficulty":14,"q":55,"a":56},"logout-token-revocation","How do you implement logout \u002F token revocation for JWT-based auth in FastAPI?","JWTs are stateless — you can't \"un-sign\" one. Revocation requires a server-side\nstore:\n\n**Option A — Revocation list (deny list)**:\n```python\n@app.post(\"\u002Flogout\")\nasync def logout(token: str = Depends(oauth2_scheme)):\n    payload = jwt.decode(token, SECRET_KEY, algorithms=[\"HS256\"])\n    jti = payload[\"jti\"]\n    exp = payload[\"exp\"]\n    await redis.setex(f\"revoked:{jti}\", exp - int(time.time()), \"1\")\n    return {\"detail\": \"Logged out\"}\n\n# In get_current_user, after decoding:\nif await redis.exists(f\"revoked:{payload['jti']}\"):\n    raise HTTPException(401, \"Token revoked\")\n```\n\n**Option B — Short expiry + refresh rotation** (no revocation list needed):\nAccess token expires in 5 minutes; compromised tokens are useless quickly.\n\nRule of thumb: for low-security APIs, use short-lived tokens; for high-security\napps (banking, health), maintain a revocation list in Redis keyed by JWT ID.\n",{"id":58,"difficulty":46,"q":59,"a":60},"oauth2-form-vs-json","Why does the OAuth2 `\u002Ftoken` endpoint accept form data instead of JSON?","The **OAuth2 specification (RFC 6749)** requires the token endpoint to accept\n`application\u002Fx-www-form-urlencoded` data — not JSON. This is for historical\ncompatibility with web browsers and existing OAuth2 clients.\n\nFastAPI's `OAuth2PasswordRequestForm` reads:\n- `username` (form field)\n- `password` (form field)\n- `scope` (optional, space-separated)\n- `grant_type` (must be `\"password\"`)\n\n```python\n@app.post(\"\u002Ftoken\")\nasync def login(form: OAuth2PasswordRequestForm = Depends()):\n    # form.username, form.password, form.scopes\n    ...\n```\n\nSwagger UI's \"Authorize\" dialog sends this form automatically.\n\nRule of thumb: never change the `\u002Ftoken` endpoint to JSON — it breaks OAuth2\nlibrary compatibility; keep all other API endpoints as JSON.\n",{"id":62,"difficulty":14,"q":63,"a":64},"cookie-auth","How do you implement cookie-based authentication in FastAPI?","Use `APIKeyCookie` from `fastapi.security` or manually read the cookie:\n\n```python\nfrom fastapi import Cookie, Response\nfrom fastapi.security import APIKeyCookie\n\ncookie_scheme = APIKeyCookie(name=\"session_id\")\n\n@app.post(\"\u002Flogin\")\nasync def login(response: Response, form: OAuth2PasswordRequestForm = Depends()):\n    user = await authenticate_user(form.username, form.password)\n    if not user:\n        raise HTTPException(400, \"Invalid credentials\")\n    token = create_session_token(user.id)\n    response.set_cookie(\n        key=\"session_id\",\n        value=token,\n        httponly=True,    # no JS access\n        secure=True,      # HTTPS only\n        samesite=\"lax\",\n    )\n    return {\"status\": \"logged in\"}\n\n@app.get(\"\u002Fme\")\nasync def me(session: str = Depends(cookie_scheme)):\n    user_id = validate_session(session)\n    return await db.get_user(user_id)\n```\n\nRule of thumb: set `httponly=True`, `secure=True`, and `samesite=\"lax\"` on\nauth cookies — these three flags prevent XSS theft, HTTP sniffing, and CSRF.\n",{"id":66,"difficulty":14,"q":67,"a":68},"two-factor-auth","How would you add TOTP two-factor authentication to a FastAPI app?","Use the `pyotp` library to generate and verify TOTP codes:\n\n```python\nimport pyotp\n\n@app.post(\"\u002F2fa\u002Fsetup\")\nasync def setup_2fa(user: User = Depends(get_current_user)):\n    secret = pyotp.random_base32()\n    await db.save_totp_secret(user.id, secret)\n    totp = pyotp.TOTP(secret)\n    return {\"provisioning_uri\": totp.provisioning_uri(user.email, issuer_name=\"MyApp\")}\n\n@app.post(\"\u002F2fa\u002Fverify\")\nasync def verify_2fa(code: str = Body(...), user: User = Depends(get_current_user)):\n    secret = await db.get_totp_secret(user.id)\n    if not pyotp.TOTP(secret).verify(code):\n        raise HTTPException(401, \"Invalid 2FA code\")\n    # issue the final access token\n    return {\"access_token\": create_access_token(user.id), \"token_type\": \"bearer\"}\n```\n\nThe flow: login → partial token → `\u002F2fa\u002Fverify` with code → full access token.\n\nRule of thumb: issue a scoped \"pending 2FA\" token after password verification;\nonly issue a full-access token after TOTP is confirmed.\n",11,null,{"description":11},"FastAPI OAuth2 interview questions — OAuth2PasswordBearer, password flow, token endpoint, scopes and securing routes with OAuth2.","fastapi\u002Fsecurity\u002Foauth2","OAuth2","Security & Auth","security","2026-06-20","VfoQ1II42taw05rn_T0KjOE4Ue0T3JvJjExIxKJz_hg",[80,81,84],{"subtopic":74,"path":21,"order":20},{"subtopic":82,"path":83,"order":12},"JWT Tokens","\u002Ffastapi\u002Fsecurity\u002Fjwt",{"subtopic":85,"path":86,"order":87},"API Keys","\u002Ffastapi\u002Fsecurity\u002Fapi-keys",3,1782244113015]