Setting Up a Secure API with FastAPI and JWT Authentication
In today’s digital landscape, securing your APIs is not just a best practice; it’s a necessity. With the rise of web applications and mobile services, ensuring that your API endpoints are protected from unauthorized access is paramount. FastAPI, a modern and fast web framework for building APIs with Python, is an excellent tool for this purpose. Coupled with JWT (JSON Web Tokens) for authentication, you can create a secure API that is both efficient and easy to implement.
Understanding FastAPI and JWT
What is FastAPI?
FastAPI is a web framework built on top of Starlette and Pydantic, designed to create APIs quickly and efficiently. It offers numerous features like automatic validation, serialization, and interactive API documentation, all while maintaining high performance.
What is JWT?
JSON Web Token (JWT) is an open standard used for securely transmitting information between parties as a JSON object. It is compact, URL-safe, and can be verified and trusted because it is digitally signed. JWTs are widely used for authentication and information exchange in web applications.
Use Cases for JWT Authentication
- User Authentication: Verify user identity during login and provide secure access to resources.
- Session Management: Maintain user sessions without the need for server-side storage.
- Microservices Communication: Secure communication between different services in a microservices architecture.
Prerequisites
Before we start coding, ensure you have the following installed:
- Python 3.6 or higher
- FastAPI
- Uvicorn (ASGI server)
- PyJWT (for handling JWTs)
- Passlib (for password hashing)
You can install the required packages using pip:
pip install fastapi uvicorn pyjwt passlib[bcrypt]
Step-by-Step Guide to Setting Up FastAPI with JWT Authentication
Step 1: Create a Basic FastAPI Application
Begin by creating a new directory for your project and inside it, create a new Python file named main.py
.
# main.py
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
def read_root():
return {"message": "Welcome to FastAPI with JWT Authentication"}
Run your FastAPI application using Uvicorn:
uvicorn main:app --reload
Visit http://127.0.0.1:8000
in your browser to see your running API.
Step 2: Implement User Registration and Password Hashing
To handle user authentication, you need a way to register users and securely store their passwords. Use Passlib to hash passwords.
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from passlib.context import CryptContext
app = FastAPI()
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
# User model
class User(BaseModel):
username: str
password: str
users_db = {}
def hash_password(password: str):
return pwd_context.hash(password)
@app.post("/register")
def register(user: User):
if user.username in users_db:
raise HTTPException(status_code=400, detail="Username already registered")
users_db[user.username] = hash_password(user.password)
return {"message": "User registered successfully"}
Step 3: Create JWT Token Generation
Next, you’ll need to create a function that generates JWT tokens for authenticated users.
import jwt
from datetime import datetime, timedelta
SECRET_KEY = "your_secret_key"
ALGORITHM = "HS256"
def create_jwt_token(username: str):
expiration = datetime.utcnow() + timedelta(hours=1)
token = jwt.encode({"sub": username, "exp": expiration}, SECRET_KEY, algorithm=ALGORITHM)
return token
Step 4: Implement User Login and JWT Issuance
Now, let’s implement the user login endpoint that verifies credentials and issues a JWT token.
@app.post("/login")
def login(user: User):
if user.username not in users_db or not pwd_context.verify(user.password, users_db[user.username]):
raise HTTPException(status_code=401, detail="Invalid credentials")
token = create_jwt_token(user.username)
return {"access_token": token, "token_type": "bearer"}
Step 5: Protecting Routes with JWT Authentication
To secure your API endpoints, you’ll need to create a dependency that checks for a valid JWT token.
from fastapi import Depends, Security
from fastapi.security import OAuth2PasswordBearer
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="login")
def get_current_user(token: str = Depends(oauth2_scheme)):
try:
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
username: str = payload.get("sub")
if username is None:
raise HTTPException(status_code=401, detail="Invalid authentication credentials")
return username
except jwt.PyJWTError:
raise HTTPException(status_code=401, detail="Invalid authentication credentials")
Step 6: Creating Protected Endpoints
You can now create endpoints that require authentication.
@app.get("/users/me", response_model=User)
def read_users_me(current_user: str = Depends(get_current_user)):
return {"username": current_user}
Conclusion
By following these steps, you've successfully set up a secure API using FastAPI and JWT authentication. With this foundation, you can expand your application to include more features, such as user roles, refresh tokens, and more sophisticated permission checks.
Key Takeaways
- FastAPI simplifies API development with its built-in features.
- JWT provides a secure way to handle user authentication.
- Properly hashing passwords is crucial for security.
As you advance in your FastAPI journey, remember to keep security at the forefront of your development process. Happy coding!