Używamy cookies, żeby zwiększyć Twoje doświadczenia na stronie
CodeWorlds

JWT Authentication - tokeny dostępu

Witaj, @name! Darwin z JWT authentication dla FastAPI! 🔐🎫

JWT (JSON Web Tokens) to standard tokenów dostępu - zamiast session cookies używamy stateless tokens!

Analogia Safari: JWT to jak elektroniczna przepustka safari - zawiera wszystkie info o turyście (ID, uprawnienia), jest podpisana cyfrowo (nie da się podrobić), i ma ważność (expires)! 🎫✅

Instalacja

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

Hashowanie haseł

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)

Tworzenie JWT tokenów

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    # Sprawdź użytkownika w bazie
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..."

Następna lekcja: Testing z pytest! 🧪

Przejdź do CodeWorlds