FastAPI Day 7: Implementing Clear JWT Authentication with Dependency Injection
This tutorial walks through building a full registration and login system in FastAPI, covering bcrypt password hashing, JWT token creation with expiration, dependency-injected authentication helpers, protected endpoints, and a suite of passing tests, demonstrating a clear and practical approach to backend authentication.
1. Registration: Store Passwords as Hashes
Passwords must never be stored in plain text. The article uses bcrypt to hash passwords.
import bcrypt
def get_password_hash(password: str) -> str:
return bcrypt.hashpw(password.encode(), bcrypt.gensalt()).decode()
def verify_password(plain: str, hashed: str) -> bool:
return bcrypt.checkpw(plain.encode(), hashed.encode())The registration endpoint checks for duplicate email, hashes the password, stores the user record, and returns a public user object.
@app.post("/auth/register", status_code=201)
def register(user: UserCreate):
if user.email in USERS_DB:
raise HTTPException(status_code=400, detail="Email already registered")
hashed = get_password_hash(user.password)
USERS_DB[user.email] = {"email": user.email, "hashed_password": hashed, ...}
return UserPublic(email=user.email, full_name=user.full_name)2. Login: Generate JWT Token
When a user logs in, the password is verified and a JWT token with an expiration time is created.
import jwt
from datetime import datetime, timedelta, timezone
SECRET_KEY = "change-this-in-production"
ALGORITHM = "HS256"
def create_access_token(data: dict, expires_delta: timedelta | None = None):
to_encode = data.copy()
expire = datetime.now(timezone.utc) + (expires_delta or timedelta(minutes=30))
to_encode.update({"exp": expire})
return jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)The login endpoint accepts form data, validates credentials, and returns the access token.
@app.post("/auth/login")
def login(username: str = Form(), password: str = Form()):
if username not in USERS_DB:
raise HTTPException(status_code=401, detail="Incorrect credentials")
user = USERS_DB[username]
if not verify_password(password, user["hashed_password"]):
raise HTTPException(status_code=401, detail="Incorrect credentials")
access_token = create_access_token(data={"sub": username})
return {"access_token": access_token, "token_type": "bearer"}3. Protected Routes: Dependency‑Injected Authentication Chain
Endpoints that require authentication add a single dependency:
def get_current_user_from_token(token: Annotated[str, Depends(verify_token)]):
try:
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
email = payload.get("sub")
if email is None:
raise HTTPException(status_code=401, detail="Invalid token")
except jwt.ExpiredSignatureError:
raise HTTPException(status_code=401, detail="Token expired")
except jwt.InvalidTokenError:
raise HTTPException(status_code=401, detail="Invalid token")
return email
@app.get("/auth/me")
def get_me(current_email: Annotated[str, Depends(get_current_user_from_token)]):
user = USERS_DB[current_email]
return UserPublic(email=user["email"], full_name=user["full_name"])The verify_token dependency (introduced on Day 5) parses the Authorization header; get_current_user_from_token only decodes and validates the token.
4. What Was Completed on Day 7
bcrypt password hashing (store hash, verify on login)
JWT token generation with expiration
POST /auth/register endpoint
POST /auth/login endpoint (form‑encoded)
GET /auth/me protected endpoint using JWT
All five new test cases pass, bringing the total to 47
5. Next Steps
Day 8 will cover database integration with SQLModel and project structuring into routers, models, and schemas.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Tech Ocean
Focused on AI programming, sharing ready-to-use development efficiency solutions.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
