Authentication is the process of verifying the identity of a user or system trying to access an API. In FastAPI, authentication mechanisms are used to ensure that only authorized users can access certain endpoints.
Authorization is the process of determining what actions an authenticated user is allowed to perform. Once a user is authenticated, authorization rules are applied to decide if the user has the necessary permissions to access a particular resource or perform a specific operation.
HTTPS is a secure version of HTTP that uses SSL/TLS encryption to protect the data transmitted between the client and the server. Using HTTPS is essential for protecting sensitive information such as user credentials and API keys from being intercepted by malicious actors.
Basic authentication is a simple authentication scheme where the client sends a username and password in the Authorization
header of the HTTP request. Here is an example of implementing basic authentication in FastAPI:
from fastapi import Depends, FastAPI
from fastapi.security import HTTPBasic, HTTPBasicCredentials
app = FastAPI()
security = HTTPBasic()
def get_current_username(credentials: HTTPBasicCredentials = Depends(security)):
# Here you can add logic to verify the credentials against a database
if credentials.username == "admin" and credentials.password == "password":
return credentials.username
return None
@app.get("/protected")
def protected_route(username: str = Depends(get_current_username)):
if username:
return {"message": f"Hello, {username}! You are authenticated."}
return {"message": "Unauthorized"}
OAuth2 is a widely used authentication and authorization framework. FastAPI provides built-in support for OAuth2 with password and bearer tokens. Here is an example:
from fastapi import Depends, FastAPI
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from jose import JWTError, jwt
from passlib.context import CryptContext
from datetime import datetime, timedelta
from typing import Optional
app = FastAPI()
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
# Secret key for signing the JWT tokens
SECRET_KEY = "your-secret-key"
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30
# Mock user database
fake_users_db = {
"johndoe": {
"username": "johndoe",
"hashed_password": pwd_context.hash("secret"),
}
}
def verify_password(plain_password, hashed_password):
return pwd_context.verify(plain_password, hashed_password)
def get_user(username: str):
if username in fake_users_db:
return fake_users_db[username]
return None
def authenticate_user(username: str, password: str):
user = get_user(username)
if not user:
return False
if not verify_password(password, user["hashed_password"]):
return False
return user
def create_access_token(data: dict, expires_delta: Optional[timedelta] = None):
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
@app.post("/token")
def login(form_data: OAuth2PasswordRequestForm = Depends()):
user = authenticate_user(form_data.username, form_data.password)
if not user:
return {"message": "Invalid credentials"}
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"}
@app.get("/protected")
def protected_route(token: str = Depends(oauth2_scheme)):
try:
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
username: str = payload.get("sub")
if username is None:
return {"message": "Invalid token"}
return {"message": f"Hello, {username}! You are authenticated."}
except JWTError:
return {"message": "Invalid token"}
API keys are a simple way to authenticate requests to an API. The client includes the API key in the request, usually in the Authorization
header or as a query parameter. Here is an example of using API keys in FastAPI:
from fastapi import Depends, FastAPI
from fastapi.security.api_key import APIKeyHeader
app = FastAPI()
API_KEY = "your-api-key"
api_key_header = APIKeyHeader(name="X-API-Key", auto_error=False)
def get_api_key(api_key: str = Depends(api_key_header)):
if api_key == API_KEY:
return api_key
return None
@app.get("/protected")
def protected_route(api_key: str = Depends(get_api_key)):
if api_key:
return {"message": "You are authenticated with the API key."}
return {"message": "Unauthorized"}
Input validation is the process of checking the data received from the client to ensure that it is in the correct format and within the expected range. FastAPI uses Pydantic for input validation, which makes it easy to define the data models and validate the input automatically. Here is an example:
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
price: float
@app.post("/items/")
def create_item(item: Item):
return item
Rate limiting is a technique used to control the number of requests a client can make to an API within a certain time period. This helps prevent abuse of the API and ensures that the server resources are not overloaded. There are several third-party libraries available for implementing rate limiting in FastAPI, such as slowapi
.
Proper error handling is essential for providing a good user experience and preventing sensitive information from being leaked. FastAPI allows you to define custom error handlers to handle different types of errors gracefully. Here is an example:
from fastapi import FastAPI, HTTPException
app = FastAPI()
@app.get("/error")
def trigger_error():
raise HTTPException(status_code=404, detail="Resource not found")
Regularly updating your dependencies, including FastAPI and its related libraries, is crucial for security. Developers often release security patches to fix vulnerabilities, so keeping your dependencies up to date helps protect your API from known security threats.
Follow secure coding practices such as avoiding hardcoding sensitive information, sanitizing user input, and using proper encryption techniques. This helps prevent common security vulnerabilities such as SQL injection, cross-site scripting (XSS), and buffer overflows.
Conduct regular security audits of your API to identify and fix any security vulnerabilities. You can use tools such as OWASP ZAP and Nmap to perform automated security scans, and also consider hiring a professional security firm to conduct a more comprehensive audit.
FastAPI provides a wide range of security features that make it easy for developers to protect their APIs. By understanding the fundamental concepts, using the appropriate authentication and authorization mechanisms, following common practices, and implementing best practices, you can ensure that your APIs are secure and protected from unauthorized access and other security threats.