Tutorial

FastAPI Auth: The Decision Framework — Choosing the Right Approach

Eight authentication and authorization methods, one decision tree. Here is the comparison matrix, production combination patterns, and pre-launch security checklist that turns auth knowledge into shipping decisions.

Tin Dang avatar
Tin Dang
A decision tree branching into multiple paths with checkmarks and labels representing different authentication strategies

You have spent seven posts learning how authentication and authorization work — passwords, JWTs, API keys, OAuth 2.0, OpenID Connect, RBAC, ABAC, and ReBAC. Now a product manager walks up and asks: “We’re building a B2B SaaS with a REST API. What should we use for auth?”

The answer is not one method. It is a combination — different methods for different actors, different contexts, and different security requirements. This post gives you the decision framework to answer that question for any system.

The Master Comparison

Authentication Methods

MethodIdentity CarrierStateful?LifetimeRevocationComplexityBest For
Password + SessionSession cookieYes (Redis/DB)Hours–daysInstant (delete session)LowServer-rendered web apps
JWTBearer tokenNo (self-contained)MinutesDelayed (expiry or blocklist)MediumSPAs, mobile, microservices
API KeyHeader/query paramYes (DB lookup)Weeks–monthsInstant (deactivate key)LowMachine-to-machine, partner APIs
OAuth 2.0Access tokenDependsMinutes–hoursDepends on providerHighDelegated access, “Login with X”
OpenID ConnectID token (JWT)NoMinutesN/A (identity snapshot)HighSSO, enterprise identity
mTLSClient certificateNoMonths–yearsCRL/OCSPVery HighService mesh, zero-trust

Authorization Models

ModelDecision BasisGranularityQuery CostScaling ChallengeBest For
RBACUser → Role → PermissionCoarse1–2 queriesRole explosionAdmin panels, internal tools
ABACAttributes + PoliciesFinePolicy eval + attribute fetchingPolicy complexityCompliance, classification
ReBACUser → Relationship → ResourcePer-resourceGraph traversalGraph sizeCollaborative platforms

The Decision Tree

Authorization Decision

Production Combination Patterns

No production system uses a single auth method. Here are the four most common patterns:

Pattern 1: The Startup SaaS

Auth stack: Password + JWT + RBAC

  • Users register with email/password
  • Login returns JWT (access + refresh)
  • RBAC with 3–5 roles: owner, admin, member, viewer
  • Add Google/GitHub SSO when customers ask (OIDC)

When to evolve: When enterprise customers need their own IdP → add SAML/OIDC SSO.

Pattern 2: The API Platform

Auth stack: API Keys + OAuth 2.0 + Rate Limiting

  • First-party integrations use scoped API keys
  • Third-party apps use OAuth 2.0 (authorization code + PKCE)
  • All access through an API gateway with per-key rate limiting
  • Webhooks signed with HMAC for outbound events

When to evolve: When partners need user-level delegation → add OAuth scopes per user.

Pattern 3: The Enterprise B2B

Auth stack: OIDC SSO + RBAC + ABAC

  • Each customer connects their IdP via OIDC (or SAML bridge)
  • Roles provisioned via SCIM or mapped from IdP groups
  • ABAC for compliance: data classification, regional access, time-based policies
  • Tenant isolation enforced at the database level (row-level security)

When to evolve: When customers need per-document sharing → add ReBAC layer.

Pattern 4: The Microservice Platform

Auth stack: JWT + mTLS + Service Mesh

  • User-facing JWT validated at the API gateway
  • JWT forwarded to downstream services (RS256 — services verify with public key)
  • Service-to-service: mTLS for transport security + JWT for user context
  • Each service checks its own scope (Depends(require_scope("billing:write")))

Migration Paths

Real systems evolve. Here are the common transitions:

FromToTriggerMigration Strategy
Password only+ OAuth/OIDC SSOEnterprise customersAdd OAuth alongside passwords — link accounts by email
is_admin booleanRBACNeed >2 permission levelsAdd roles table, backfill existing users, gate routes with dependencies
RBAC+ ABACCompliance requirementsAdd policy engine, keep RBAC for coarse checks, ABAC for fine-grained
Session cookiesJWTSPA or mobile clientAdd JWT endpoints, keep sessions for web — both can coexist
API keys only+ OAuth 2.0Third-party marketplaceAdd OAuth for new integrations, grandfather existing API keys
Self-managed authAuth-as-a-ServiceTeam capacityMigrate to Auth0/Clerk/WorkOS, keep custom auth for edge cases

The “Add, Don’t Replace” Principle

Auth migrations are safest when you add the new method alongside the old one:

# Support both session AND JWT auth during migration
async def get_current_user(
request: Request,
token: str | None = Depends(oauth2_scheme_optional),
session_id: str | None = Cookie(None),
db: AsyncSession = Depends(get_db),
) -> User:
# Try JWT first
if token:
return await validate_jwt_user(token, db)
# Fall back to session
if session_id:
return await validate_session_user(session_id, db)
raise HTTPException(status_code=401, detail="Not authenticated")

Old clients keep using sessions. New clients use JWT. Both work. Migrate gradually.

Pre-Launch Security Checklist

Before shipping any auth implementation to production, verify every item:

Transport Layer

  • TLS/HTTPS enforced on all endpoints (HSTS header set)
  • No credentials in URL query parameters (use headers or body)
  • No credentials in server logs (redact Authorization headers)
  • CORS configured — Access-Control-Allow-Origin is not *

Password Authentication

  • Passwords hashed with Argon2id or bcrypt (never SHA/MD5)
  • Minimum password length enforced (NIST: 8 characters)
  • Maximum password length enforced (prevent DoS: 128 characters)
  • Failed login attempts tracked — account lockout after 5 failures
  • IP-based rate limiting on login endpoints
  • Registration does not leak whether email exists (or it is an acceptable tradeoff)

Token Security

  • JWTs signed with explicit algorithm list — algorithms=["HS256"], never "none"
  • Access tokens expire in ≤15 minutes
  • Refresh tokens stored in HttpOnly cookies with SameSite=Strict
  • Refresh token rotation implemented — old tokens invalidated
  • Token type checked — refresh tokens cannot be used as access tokens
  • JWT secret key is ≥256 bits of entropy

API Keys

  • Stored as SHA-256 hashes — plaintext never persisted
  • Shown to user exactly once at creation
  • Scoped with least-privilege permissions
  • Prefixed for identification (sk_, pk_, cdk_)
  • Expiry dates set (or reviewed quarterly)
  • Per-key rate limiting enforced

OAuth / OIDC

  • state parameter validated on callback (CSRF protection)
  • nonce validated in ID tokens (replay protection)
  • Client secret never exposed to frontend code
  • PKCE used for SPAs and mobile (no implicit grant)
  • Scopes are minimal — request only what you need
  • ID token aud claim matches your client ID

Authorization

  • Every endpoint has an explicit authorization check (no “default allow”)
  • BOLA prevention: resource queries filter by tenant/owner
  • Admin endpoints are not accessible by guessing URLs
  • Authorization failures return 403, not 404 (unless hiding resource existence is desired)
  • Permissions checked in dependencies, not in route handler bodies

Audit

  • All authentication events logged (login, logout, failure, token refresh)
  • All authorization failures logged with user ID and resource ID
  • Logs do not contain passwords, tokens, or API keys
  • Logs are append-only (not editable by application code)

Quick Reference: Which Auth for What

ScenarioAuthenticationAuthorizationNotes
Blog with admin panelPassword + sessionis_admin booleanKeep it simple
SPA with user accountsPassword + JWTRBAC (3–5 roles)Add SSO when customers ask
Mobile appOAuth PKCE + JWTRBACNever store client secrets in app
Public APIAPI keys (partners) + OAuth (third-party)ScopesRate limit per key
Enterprise SaaSOIDC SSORBAC + ABACMap IdP groups to roles
Collaborative platformOIDC SSO + password fallbackReBACPer-resource sharing like Google Docs
MicroservicesJWT (user) + mTLS (service)Scoped claimsRS256 for distributed validation
Internal toolsOIDC (corporate IdP)RBACSingle source of identity

Series Recap

Over eight posts we have built:

  1. A mental model that separates authentication from authorization and maps the four-layer security stack
  2. Password authentication with Argon2id hashing, session cookies, CSRF protection, and brute-force mitigation
  3. JWT authentication with access/refresh tokens, rotation, and three revocation strategies
  4. API key authentication with hashed storage, scoping, zero-downtime rotation, and per-key rate limiting
  5. OAuth 2.0 with the authorization code flow, PKCE for SPAs, and client credentials for machines
  6. OpenID Connect with Google SSO, ID token validation, and an OIDC-vs-SAML comparison
  7. Authorization patterns — RBAC, ABAC, and ReBAC — implemented as FastAPI dependencies
  8. A decision framework with comparison matrices, production patterns, migration paths, and a security checklist

Auth is not a feature you bolt on. It is a design decision that shapes your architecture. Choose the simplest combination that meets your requirements today, and design your dependencies so you can add layers as your requirements grow.

The best auth system is the one your team understands well enough to operate correctly in production — at 2 AM, under pressure, when something goes wrong.

0