1. Environment Preparation and Dependency Installation¶
To implement user login authentication with FastAPI and JWT, we first need to install the required Python libraries. Open your terminal and run the following command:
pip install fastapi uvicorn python-jose[cryptography] passlib[bcrypt] python-multipart
- FastAPI: A high-performance web framework for building APIs
- uvicorn: An ASGI server for running FastAPI applications
- python-jose: Used for generating and verifying JWT tokens
- passlib: For password hashing (secure password storage)
- bcrypt: A dependency of passlib for password encryption algorithms
- python-multipart: Handles form data (commonly used in login interfaces)
2. Core Concept Understanding¶
- JWT (JSON Web Token): A stateless authentication method where the server generates an encrypted token based on user information. Clients can prove their identity by including this token in each request.
- Dependency: A mechanism in FastAPI for reusing logic (e.g., token verification). It automatically processes requests and returns results.
- Password Hashing: Passwords are not stored in plain text. Instead, they are converted into irreversible hash values using algorithms like bcrypt. Verification is done by comparing hash values.
3. Code Implementation Steps¶
3.1 Import Required Libraries¶
from datetime import datetime, timedelta
from typing import Optional
from fastapi import Depends, FastAPI, HTTPException, status
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from jose import JWTError, jwt
from passlib.context import CryptContext
from pydantic import BaseModel
3.2 Configure JWT Parameters¶
# Configure JWT parameters (use environment variables in production)
SECRET_KEY = "your-secret-key-keep-it-safe-and-long-enough" # Recommend 16+ random characters
ALGORITHM = "HS256" # Encryption algorithm (HS256 = HMAC-SHA256)
ACCESS_TOKEN_EXPIRE_MINUTES = 30 # Token validity: 30 minutes
3.3 Simulate User Database¶
Use a list to simulate database storage (replace with real databases like SQLite/PostgreSQL in production):
# Simulated user data (replace with actual database queries in production)
fake_users_db = {
"johndoe": {
"username": "johndoe",
"hashed_password": "hashed_secret_password", # Will be generated using bcrypt later
"disabled": False,
}
}
3.4 Password Hashing Tool (passlib)¶
Use the bcrypt algorithm to hash passwords:
# Initialize password hashing context
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
def get_password_hash(password: str) -> str:
"""Hash a password for storage"""
return pwd_context.hash(password)
def verify_password(plain_password: str, hashed_password: str) -> bool:
"""Verify a stored password against its hash"""
return pwd_context.verify(plain_password, hashed_password)
3.5 JWT Utility Functions (Generate and Verify Tokens)¶
def create_access_token(data: dict, expires_delta: Optional[timedelta] = None) -> str:
"""Generate a JWT token"""
to_encode = data.copy()
if expires_delta:
expire = datetime.utcnow() + expires_delta
else:
expire = datetime.utcnow() + timedelta(minutes=15)
to_encode.update({"exp": expire})
encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
return encoded_jwt
def verify_token(token: str, credentials_exception) -> dict:
"""Verify token validity"""
try:
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
username: str = payload.get("sub") # Extract username from token (sub = subject)
if username is None:
raise credentials_exception
except JWTError:
raise credentials_exception
return {"username": username}
3.6 OAuth2 Dependency (Extract Token)¶
FastAPI provides OAuth2PasswordBearer to automatically retrieve tokens from request headers:
# OAuth2 scheme: Automatically extract Bearer Token from request headers
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
3.7 Define User Models¶
Use Pydantic to define request/response data structures (ensure data format correctness):
class User(BaseModel):
username: str
disabled: Optional[bool] = None
class UserInDB(User):
hashed_password: str
3.8 Login Endpoint (Get Token)¶
Accept username/password, verify, and return a token:
# Login endpoint: Path is /token, accepts form parameters (username/password)
@app.post("/token")
async def login_for_access_token(form_data: OAuth2PasswordRequestForm = Depends(), db=fake_users_db):
# 1. Retrieve user from database (replace with actual query logic)
user = db.get(form_data.username)
if not user:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Incorrect username or password",
headers={"WWW-Authenticate": "Bearer"},
)
# 2. Verify password
if not verify_password(form_data.password, user["hashed_password"]):
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Incorrect username or password",
headers={"WWW-Authenticate": "Bearer"},
)
# 3. Generate Token
access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
access_token = create_access_token(
data={"sub": user["username"]}, expires_delta=access_token_expires
)
return {"access_token": access_token, "token_type": "bearer"}
3.9 Protected Endpoint (Verify Token)¶
Use dependencies to verify the token and return user information:
# Dependency: Verify token and return user info
async def get_current_user(
token: str = Depends(oauth2_scheme),
credentials_exception=HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid authentication credentials",
headers={"WWW-Authenticate": "Bearer"},
),
):
user = verify_token(token, credentials_exception)
return User(username=user["username"])
# Example of a protected route
@app.get("/users/me")
async def read_users_me(current_user: User = Depends(get_current_user)):
return {"user": current_user}
3.10 Main Program Entry¶
app = FastAPI()
# Initialize password hashing (called during user registration in production; pre-hash for demo)
if __name__ == "__main__":
# Manually generate hashed password for test user (should be called in registration API)
test_password = "testpassword"
fake_users_db["johndoe"]["hashed_password"] = get_password_hash(test_password)
4. Running and Testing¶
- Start the service: In the directory containing your code file (e.g.,
main.py), run:
uvicorn main:app --reload
- Access Swagger UI: Open a browser and visit
http://localhost:8000/docsto test the login endpoint via the interactive interface. - Testing Flow:
- Call thePOST /tokenendpoint withusername=johndoeandpassword=testpasswordto obtain anaccess_token.
- Use the token to call theGET /users/meendpoint. AddAuthorization: Bearer <token>in the request headers to receive user information.
5. Key Knowledge Summary¶
- Dependency: Reuse token verification logic to automatically return user information or block invalid requests.
- JWT Security: Protect the
SECRET_KEY(use environment variables in production) to prevent token expiration or leaks. - Password Storage: Use
passlibhashing algorithms to store passwords (never store plain text) to mitigate data breach risks.
With the above steps, you have implemented a basic user login authentication system based on FastAPI and JWT. Subsequent extensions could include features like refresh tokens and multi-user role permissions.