Authentication & Security
JWT, OAuth2, Password Hashing, and Role-Based Access Control (RBAC).
Authentication vs Authorization
These terms are often used interchangeably, but they serve completely different purposes in a secure system.
| Concept | Meaning | Analogy |
|---|---|---|
| Authentication | "Who are you?" | Identity Card / Passport |
| Authorization | "What can you access?" | Premises Access Card / Keys |
👉 Example: Logging in is Authentication. Once logged in, being allowed to access the /admin dashboard is Authorization.
🔑 Common Authentication Methods
JWT (JSON Web Token)
A compact, URL-safe means of representing claims to be transferred between two parties. It is the most common authentication method in modern FastAPI applications.
Header
Specifies algorithm (e.g., HS256) and token type.
Payload
Contains data claims (User ID, Role, Expire time).
Signature
The hashed content used to verify token integrity.
Why JWT?
📥 Installation
pip install python-jose[cryptography] passlib[bcrypt]Secure Password Hashing
The Golden Rule
Never store plain text passwords in your database. Always hash them using robust algorithms like Bcrypt.
password = "123456"
# Storing as-is in DBfrom passlib.context import CryptContext
pwd_context = CryptContext(schemes=["bcrypt"])
hash = pwd_context.hash("123456")def hash_password(password: str):
return pwd_context.hash(password)
def verify_password(plain, hashed):
return pwd_context.verify(plain, hashed)OAuth2 & JWT Implementation
1. OAuth2 Password Flow
FastAPI uses OAuth2PasswordBearer as a dependency to extract the token from the Header.
from fastapi.security import OAuth2PasswordBearer
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")2. Login & Token Creation
Converting validated credentials into a secure JWT.
from jose import jwt
from datetime import datetime, timedelta
SECRET_KEY = "your-complex-secret"
ALGORITHM = "HS256"
def create_access_token(data: dict):
to_encode = data.copy()
expire = datetime.utcnow() + timedelta(minutes=30)
to_encode.update({"exp": expire})
return jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
@app.post("/token")
async def login(form_data: OAuth2PasswordRequestForm = Depends()):
# 1. Fetch user from DB\n # 2. Verify password\n # 3. Create token\n token = create_access_token({"sub": user.id})
return {"access_token": token, "token_type": "bearer"}Protected Routes
The get_current_user dependency decodes the JWT and verifies the signature on every request to a protected endpoint.
def get_current_user(token: str = Depends(oauth2_scheme)):
try:
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
user_id: str = payload.get("sub")
if user_id is None:
raise HTTPException(status_code=401)
return user_id
except Exception:
raise HTTPException(status_code=401, detail="Invalid token")
@app.get("/users/me")
def read_me(current_user_id: str = Depends(get_current_user)):
return {"user_id": current_user_id}Full Auth Flow (Step-by-Step)
RBAC: Role-Based Access Control
Permission Checkers
Implementing authorization by checking user roles decoded from the JWT or fetched from DB.
def require_role(allowed_role: str):
def checker(user_role: str = Depends(get_user_role_from_db)):
if user_role != allowed_role:
raise HTTPException(status_code=403, detail="Forbidden")
return True
return checker
@app.get("/admin/metrics", dependencies=[Depends(require_role("admin"))])
def get_metrics():
return {"data": "Secure admin metrics"}Production Security Checklist
✅ Best Practices
- 🚀 Use HTTPS only
- 🔑 Store SECRET_KEY in .env
- ⏳ Set short token expiration
- 🔄 Implement Refresh Tokens
❌ Vulnerabilities
- 💣 Exposing passwords in logs
- 💣 Hardcoded secrets in code
- 💣 No rate limiting
- 💣 Insufficient role validation
Refresh Tokens (Advanced Concept)
⏳ Access Token
Short lifespan (e.g., 15-30 mins). Used for every API request to minimize damage if stolen.
🔄 Refresh Token
Long lifespan (e.g., 7-30 days). Used only to generate new access tokens without requiring re-login.
Security Middleware
Middlewares allow you to inject security headers globally for every HTTP request.
@app.middleware("http")
async def add_security_headers(request, call_next):
response = await call_next(request)
response.headers["X-Frame-Options"] = "DENY"
response.headers["X-XSS-Protection"] = "1; mode=block"
return responseInterview Preparation
🧠 Theory Questions
What is JWT?▼
Why use JWT over sessions?▼
Explain Authentication vs Authorization.▼
What is Hashing?▼
What is RBAC?▼
What is a Bearer Token?▼
What is OAuth2?▼
What happens if token expires?▼
💻 Coding Challenges
from passlib.context import CryptContext
context = CryptContext(schemes=["bcrypt"])
hashed = context.hash("mypassword")from jose import jwt
SECRET = "supersecret"
ALGO = "HS256"
def create_token(user_id: str):
return jwt.encode({"sub": user_id}, SECRET, algorithm=ALGO)@app.get("/secure")
def secure(user = Depends(get_current_user)):
return {"msg": "Secure"}@app.get("/admin")
def admin(user = Depends(require_role("admin"))):
return {"msg": "Admin"}🧩 Bonus: Security in ML APIs
For ML APIs, security isn't just about logs; it's about Model Misuse Prevention and Sensitive Inference Protection. Ensure model parameters or raw training data are never exposed in prediction outputs.
@app.post("/predict")
def predict(data: InputData, user = Depends(get_current_user)):
# Only authorized users can trigger inference
return model.predict(data)