We use cookies to enhance your experience on the site
CodeWorlds

JWT Authentication - access tokens

Welcome, @name! Darwin here with JWT authentication for FastAPI! 🔐🎫

JWT (JSON Web Tokens) is a standard for access tokens - instead of session cookies we use stateless tokens!

Safari Analogy: JWT is like an electronic safari pass - it contains all the tourist's info (ID, permissions), is digitally signed (cannot be forged), and has an expiration date! 🎫✅

Installation

1pip install python-jose[cryptography] passlib[bcrypt] python-multipart

Password hashing

1from passlib.context import CryptContext
2
3pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
4
5def hash_password(password: str) -> str:
6    return pwd_context.hash(password)
7
8def verify_password(plain_password: str, hashed_password: str) -> bool:
9    return pwd_context.verify(plain_password, hashed_password)

Creating JWT tokens

1from jose import JWTError, jwt
2from datetime import datetime, timedelta
3
4SECRET_KEY = "your-secret-key-keep-it-secret"
5ALGORITHM = "HS256"
6ACCESS_TOKEN_EXPIRE_MINUTES = 30
7
8def create_access_token(data: dict):
9    to_encode = data.copy()
10    expire = datetime.utcnow() + timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
11    to_encode.update({"exp": expire})
12    encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
13    return encoded_jwt
14
15def decode_token(token: str):
16    try:
17        payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
18        return payload
19    except JWTError:
20        return None

Login endpoint

1from fastapi import FastAPI, Depends, HTTPException, status
2from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
3
4app = FastAPI()
5
6oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
7
8@app.post("/token")
9async def login(form_data: OAuth2PasswordRequestForm = Depends()):
10    # Check user in the database
11    user = authenticate_user(form_data.username, form_data.password)
12
13    if not user:
14        raise HTTPException(
15            status_code=status.HTTP_401_UNAUTHORIZED,
16            detail="Incorrect username or password"
17        )
18
19    access_token = create_access_token(data={"sub": user.username})
20    return {"access_token": access_token, "token_type": "bearer"}
21
22async def get_current_user(token: str = Depends(oauth2_scheme)):
23    payload = decode_token(token)
24    if not payload:
25        raise HTTPException(status_code=401, detail="Invalid token")
26
27    username = payload.get("sub")
28    return {"username": username}
29
30@app.get("/protected")
31async def protected_route(current_user: dict = Depends(get_current_user)):
32    return {"message": f"Hello {current_user['username']}!"}

Test:

1# Login
2curl -X POST http://localhost:8000/token \
3  -d "username=darwin&password=secret123"
4
5# Response: {"access_token":"eyJ...", "token_type":"bearer"}
6
7# Protected route
8curl http://localhost:8000/protected \
9  -H "Authorization: Bearer eyJ..."

Next lesson: Testing with pytest! 🧪

Go to CodeWorlds