Python Authentication — JWT, OAuth & Session Management
Authentication verifies user identity. This tutorial covers JWT tokens, OAuth2 flows, password hashing, and session management.
Learning Objectives
- Generate and verify JWT tokens
- Hash passwords securely with bcrypt
- Implement OAuth2 authentication
- Build session-based authentication
Authentication vs Authorization
Authentication: "Who are you?"
→ Login, verify identity
Authorization: "What can you do?"
→ Permissions, roles, access control
Example:
Alice logs in (authentication)
Alice can view her profile (authorization)
Alice cannot delete other users' profiles (no authorization)
Password Hashing with bcrypt
import bcrypt
# NEVER store passwords in plain text!
# BAD: password = "my_secret" (stored as-is)
# GOOD: password = bcrypt.hashpw(...) (hashed)
def hash_password(password: str) -> str:
"""Hash a password for storage."""
salt = bcrypt.gensalt(rounds=12) # 12 rounds = slow but secure
hashed = bcrypt.hashpw(password.encode('utf-8'), salt)
return hashed.decode('utf-8')
def verify_password(password: str, hashed: str) -> bool:
"""Verify a password against its hash."""
return bcrypt.checkpw(
password.encode('utf-8'),
hashed.encode('utf-8')
)
# Usage
password = "my_secret_password"
hashed = hash_password(password)
print(f"Hash: {hashed}") # $2b$12$...
# Verify
print(verify_password("my_secret_password", hashed)) # True
print(verify_password("wrong_password", hashed)) # False
JWT (JSON Web Tokens)
JWT is a stateless authentication mechanism. The token contains all needed information — no server-side session storage required.
import jwt
from datetime import datetime, timedelta
SECRET_KEY = "your-super-secret-key" # Store in environment!
# Generate token
def create_token(user_id: int, role: str = "user") -> str:
payload = {
"user_id": user_id,
"role": role,
"exp": datetime.utcnow() + timedelta(hours=24),
"iat": datetime.utcnow() # Issued at
}
return jwt.encode(payload, SECRET_KEY, algorithm="HS256")
# Verify token
def verify_token(token: str) -> dict:
try:
payload = jwt.decode(token, SECRET_KEY, algorithms=["HS256"])
return payload
except jwt.ExpiredSignatureError:
raise ValueError("Token expired")
except jwt.InvalidTokenError:
raise ValueError("Invalid token")
# Usage
token = create_token(user_id=123, role="admin")
print(f"Token: {token[:50]}...")
payload = verify_token(token)
print(f"User ID: {payload['user_id']}")
FastAPI Authentication Example
from fastapi import FastAPI, Depends, HTTPException
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
app = FastAPI()
security = HTTPBearer()
def get_current_user(credentials: HTTPAuthorizationCredentials = Depends(security)):
try:
payload = verify_token(credentials.credentials)
return payload
except ValueError:
raise HTTPException(status_code=401, detail="Invalid token")
@app.get("/protected")
def protected_route(user: dict = Depends(get_current_user)):
return {"message": f"Hello, user {user['user_id']}"}
Key Takeaways
- NEVER store passwords in plain text
- Use bcrypt for password hashing (slow = secure)
- JWT is stateless — good for APIs and microservices
- Always set token expiration
- Store secrets in environment variables, not code
- Use HTTPS for all authentication
- Implement rate limiting to prevent brute force attacks
- Consider using OAuth2 for third-party authentication