User System โ Registration, Login, JWT Authentication |
\\n\\nIn this chapter, you will learn FastAPI's JWT authentication scheme and understand the difference between it and Django/Flask Session authentication.
\\n\\n\\n\\n
JWT vs Session Authentication
\\n\\nDjango and Flask use Session authentication: the server stores the user state, and only the Session ID is stored in the Cookie.
\\n\\nFastAPI recommends JWT authentication: after logging in, the user obtains a Token, and subsequent requests can be authenticated by carrying the Token.
\\n\\n| Feature | \\nSession (Django/Flask) | \\nJWT (FastAPI) | \\n
|---|---|---|
| Storage Location | \\nServer-side (Memory/Database) | \\nClient-side (Cookie/LocalStorage) | \\n
| Scalability | \\nServer needs to share Session | \\nStateless, natively supports distributed systems | \\n
| Applicable Scenarios | \\nServer-side rendered websites | \\nAPI + SPA / Mobile | \\n
| Expiration Control | \\nCan be invalidated by the server at any time | \\nValid throughout the Token's lifetime | \\n
\\n\\n
Install Dependencies
(venv) $ pip install passlib python-jose python-multipart\\n\\n\\n- \\n
passlib๏ผPassword Hashing (Equivalent to werkzeug.security๏ผ \\n python-jose๏ผJWT generation and verification \\n python-multipart๏ผHandle form submission (OAuth2 login form) \\n
\\n\\n
Define User Model
\\n\\nExample
# File Path: models.py Add\\n\\nfrom passlib.context import CryptContext\\n\\npwd_context = CryptContext(schemes=, deprecated="auto")\\n\\nclass User(Base):\\n\\n __tablename__ ="users"\\n\\nid= Column(Integer, primary_key=True, index=True)\\n\\n username = Column(String(50), unique=True, nullable=False)\\n\\nemail= Column(String(120), unique=True, nullable=False)\\n\\n hashed_password = Column(String(256), nullable=False)\\n\\ndef set_password(self, password: str):\\n\\n"""Hash Password"""\\n\\nself.hashed_password= pwd_context.hash(password)\\n\\ndef verify_password(self, password: str) ->bool:\\n\\n"""Verify Password"""\\n\\nreturn pwd_context.verify(password,self.hashed_password)\\n\\n\\nGenerate and Run Migrations:
\\n\\n(venv) $ alembic revision --autogenerate -m "Add User model"(venv) $ alembic upgrade head\\n\\n\\n\\n\\n
JWT Utility Functions
\\n\\nExamples
\\n\\n# File path๏ผauth.py๏ผCreate New File)\\n\\nfrom datetime import datetime, timedelta\\n\\nfrom jose import JWTError, jwt\\n\\nfrom passlib.context import CryptContext\\n\\nfrom fastapi import Depends, HTTPException, status\\n\\nfrom fastapi.security import OAuth2PasswordBearer\\n\\n# Secret key๏ผproduction environment (read from environment variables)๏ผ\\n\\n SECRET_KEY ="your-secret-key-change-in-production"\\n\\n ALGORITHM ="HS256"\\n\\n ACCESS_TOKEN_EXPIRE_MINUTES =60 * 24# Token validity period๏ผ24 hours\\n\\n# OAuth2PasswordBearer tells FastAPI๏ผFromrequest header Authorization: Bearer <token> inextract JWT\\n\\n oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")\\n\\ndef create_access_token(data: dict) ->str:\\n\\n"""Generate JWT Token"""\\n\\n to_encode = data.copy()\\n\\n expire =datetime.utcnow() + timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)\\n\\n to_encode.update({"exp": expire})\\n\\nreturn jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)\\n\\ndef decode_access_token(token: str) ->dict | None:\\n\\n"""verify and decode JWT Token"""\\n\\ntry:\\n\\n payload = jwt.decode(token, SECRET_KEY, algorithms=)\\n\\nreturn payload\\n\\nexcept JWTError:\\n\\nreturn None\\n\\n\\n\\n\\n
Register routes
\\n\\nExamples
\\n\\n# File Path: routers/users.py\\n\\nfrom fastapi import APIRouter, Depends, HTTPException, Request, Form\\n\\nfrom fastapi.templating import Jinja2Templates\\n\\nfrom sqlalchemy.orm import Session\\n\\nfrom database import get_db\\n\\nfrom models import User\\n\\nfrom schemas import UserCreate, UserResponse\\n\\nfrom auth import create_access_token\\n\\nrouter = APIRouter(prefix="/users", tags=)\\n\\n templates = Jinja2Templates(directory="templates")\\n\\n@router.post("/register", response_model=UserResponse)\\n\\ndef register(\\n\\n username: str= Form(...),\\n\\nemail: str= Form(...),\\n\\n password: str= Form(...),\\n\\n db: Session = Depends(get_db)\\n\\n):\\n\\n"""User Registration"""\\n\\n# check if username and email already exist\\n\\nif db.query(User).filter(User.username== username).first():\\n\\nraise HTTPException(status_code=400, detail="Username already used")\\n\\nif db.query(User).filter(User.email==email).first():\\n\\nraise HTTPException(status_code=400, detail="Email Already Registered")\\n\\nuser= User(username=username,email=email)\\n\\nuser.set_password(password)\\n\\n db.add(user)\\n\\n db.commit()\\n\\n db.refresh(user)\\n\\nreturn user\\n\\n\\n\\n\\n
Login Route (Issue JWT)
\\n\\nExamples
\\n\\n# File Path: routers/users.py Append\\n\\nfrom fastapi.security import OAuth2PasswordRequestForm\\n\\nfrom auth import create_access_token\\n\\n@router.post("/token")\\n\\ndef login_for_access_token(\\n\\n form_data: OAuth2PasswordRequestForm = Depends(),\\n\\n db: Session = Depends(get_db)\\n\\n):\\n\\n"""\\n\\n Login and Get Token\\n\\n use OAuth2PasswordRequestForm๏ผfield names are username and password๏ผform format๏ผ\\n\\n """\\n\\nuser= db.query(User).filter(User.username== form_data.username).first()\\n\\nif not user or not user.verify_password(form_data.password):\\n\\nraise HTTPException(\\n\\n status_code=401,\\n\\n detail="Userusername or password error",\\n\\n headers={"WWW-Authenticate": "Bearer"}\\n\\n)\\n\\n# Issue JWT: Include Only user_id (Do Not Include Sensitive Information)\\n\\n access_token = create_access_token(data={"sub": str(user.id)})\\n\\nreturn{"access_token": access_token,"token_type": "bearer"}\\n\\n\\n\\n\\n\\nJWT Token in
\\nsubis the standard subject field, storing the user identifier. The token itself is not encrypted (it is only Base64 encoded), so you must never include sensitive information like passwords in it. If you need encryption, use JWE instead of JWT.
\\n\\n
Protecting Routes with JWT
\\n\\nExample
# Dependency Function to Get Current User\\n\\nfrom auth import oauth2_scheme, decode_access_token\\n\\ndef get_current_user(\\n\\ntoken: str= Depends(oauth2_scheme),\\n\\n db: Session = Depends(get_db)\\n\\n):\\n\\n"""From JWT Token inparse currentUser"""\\n\\n payload = decode_access_token(token)\\n\\nif payload is None:\\n\\nraise HTTPException(status_code=401, detail="Token Invalid or Expired")\\n\\nuser_id = payload.get("sub")\\n\\nuser= db.query(User).filter(User.id==int(user_id)).first()\\n\\nif user is None:\\n\\nraise HTTPException(status_code=401, detail="User does not exist")\\n\\nreturn user\\n\\n# inject into routes that require login\\n\\n@router.get("/me", response_model=UserResponse)\\n\\ndef read_current_user(current_user: User = Depends(get_current_user)):\\n\\n"""Get currentUserinformation๏ผrequires login๏ผ"""\\n\\nreturn current_user\\nFor SSR pages, JWTs are typically stored in cookies (rather than the Authorization header), and the token is extracted from the cookie in template routes for verification.
\\n\\n\\n\\n
Chapter Summary
\\n\\nIn this chapter, you implemented JWT authentication in FastAPI:passlib Hash Passwordใpython-jose Issue and Verify JWT,OAuth2PasswordBearer extract token,DependsImplement authentication dependency injection.
Unlike Django/Flask's Session authentication, JWT is a stateless solution that is better suited for front-end and back-end separation scenarios.
YouTip