All Python templates

FastAPI JWT Authentication

JWT auth with refresh tokens, password hashing, and role-based access.

DevZone Tools1,560 copiesUpdated Feb 8, 2026FastAPIPython
# CLAUDE.md — FastAPI JWT Authentication

## Tokens

- Two tokens: **access token** (short-lived, ~15 min) + **refresh token** (longer, ~7–30 days).
- Sign with HS256 if you have one service. Switch to RS256 if you have multiple services that need to verify without sharing the signing key.
- Library: **python-jose** or **PyJWT**. Either is fine — pick one.
- Claims: `sub` (user id), `iat`, `exp`, `jti` (for revocation), and a `type` claim distinguishing access vs refresh.

## Storage on the client

- Web: HTTP-only, Secure, SameSite=Lax cookies for the refresh token. Access token in memory only.
- Mobile / native: secure storage (Keychain, EncryptedSharedPreferences). Never plain disk.
- Don't put tokens in `localStorage`. XSS reads it.

## Password hashing

- **bcrypt** via `passlib` or **argon2** via `argon2-cffi`. Argon2 if starting fresh.
- Cost factor: bcrypt 12+, argon2 default. Re-evaluate yearly.
- Never log passwords. Filter them out of error reporting.

## FastAPI integration

- Auth dependency:
  ```python
  async def get_current_user(token: str = Depends(oauth2_scheme), db: AsyncSession = Depends(get_db)) -> User:
      try:
          payload = jwt.decode(token, settings.SECRET_KEY, algorithms=["HS256"])
      except JWTError:
          raise HTTPException(401, "Invalid token")
      user = await db.get(User, payload["sub"])
      if not user:
          raise HTTPException(401, "Invalid token")
      return user
  ```
- For optional auth, write a separate dependency that returns `Optional[User]`. Don't make `get_current_user` accept `None`.

## Refresh flow

- Refresh endpoint: takes the refresh token, issues new access + refresh, **rotates** the refresh token.
- Track refresh tokens in a `refresh_tokens` table with `jti`, `user_id`, `expires_at`, `revoked_at`. Lookup on each refresh.
- On suspicious activity (token reuse), revoke the entire user session family.

## Roles & permissions

- Role on the user record, claim copied to the access token. Don't re-fetch the user just for role checks.
- Per-endpoint authorization: factory dependencies like `require_role("admin")`.
- For object-level permissions, check ownership inside the service — JWT only proves identity, not authorization.

## Logout

- Stateless JWTs can't truly be invalidated. For real logout:
  - Delete the refresh-token row (the user can't refresh again)
  - Optionally maintain a revocation list keyed by `jti` for unexpired access tokens
  - Or shorten access-token TTL so revocation is "soon enough"

## Don't

- Don't use long-lived access tokens to avoid refresh logic. The refresh flow exists for revocation.
- Don't put sensitive data (email, role, plan) you'd hate to leak in the JWT payload — anyone can read it.
- Don't trust `iss` and `aud` without configuring them. Set them in encode and verify in decode.
- Don't roll your own crypto. Use library-supported algorithms only.

Other Python templates