DEV Community

Cover image for Part 4: Security and Authentication in FastAPI
James
James

Posted on

Part 4: Security and Authentication in FastAPI

Part 4: Security and Authentication in FastAPI

In this part of our FastAPI series, we delve into implementing robust security measures to protect your API endpoints. We'll cover basic authentication schemes, token-based authentication using OAuth2, and setting up role-based access control (RBAC).

Why Focus on Security?

Security is paramount for any application that interacts with user data or interfaces with other systems. A secure API guards against unauthorized access and ensures that user data is handled safely. FastAPI provides several tools to help secure your API effectively.

OAuth2 with Password and Bearer Tokens

One of the most common and secure ways to handle authentication in APIs is through OAuth2 with Password (and hashing), which involves token-based credentials.

Setting Up Authentication

First, let's set up the authentication scheme in FastAPI. We'll use OAuth2 with password flow, which is well-suited for securing web and mobile applications.

from fastapi import FastAPI, Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from pydantic import BaseModel

app = FastAPI()

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")

def fake_hash_password(password: str):
    return "fakehashed" + password

class User(BaseModel):
    username: str
    email: Optional[str] = None
    full_name: Optional[str] = None
    disabled: Optional[bool] = None

class UserInDB(User):
    hashed_password: str

# Assuming we have a way to fetch user details
def get_user(db, username: str):
    if username == "johndoe":
        return UserInDB(username="johndoe", hashed_password=fake_hash_password("secret"))
    return None

# Authentication function
def authenticate_user(fake_db, username: str, password: str):
    user = get_user(fake_db, username)
    if not user or user.hashed_password != fake_hash_password(password):
        return False
    return user
Enter fullscreen mode Exit fullscreen mode

Creating a Token Endpoint

The token endpoint is where users will authenticate using their credentials to receive a token they can use for subsequent requests.

from fastapi import Security
from fastapi.security import OAuth2PasswordRequestForm
from jose import jwt, JWTError

SECRET_KEY = "a_very_secret_key"
ALGORITHM = "HS256"

@app.post("/token")
async def login(form_data: OAuth2PasswordRequestForm = Depends()):
    user = authenticate_user(fake_db, form_data.username, form_data.password)
    if not user:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Incorrect username or password",
            headers={"WWW-Authenticate": "Bearer"},
        )
    access_token = jwt.encode({"sub": user.username}, SECRET_KEY, algorithm=ALGORITHM)
    return {"access_token": access_token, "token_type": "bearer"}
Enter fullscreen mode Exit fullscreen mode

Role-Based Access Control (RBAC)

Role-based access control is a method to provide access based on the user role at an organization level. Let's implement a simple RBAC in FastAPI.

from fastapi import APIRouter, Depends, HTTPException

router = APIRouter()

async def get_current_user(token: str = Depends(oauth2_scheme)):
    credentials_exception = HTTPException(
        status_code=status.HTTP_401_UNAUTHORIZED,
        detail="Could not validate credentials",
        headers={"WWW-Authenticate": "Bearer"},
    )
    try:
        payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
        username: str = payload.get("sub")
        if username is None:
            raise credentials_exception
    except JWTError:
        raise credentials_exception
    user = get_user(fake_db, username=username)
    if user is None:
        raise credentials_exception
    return user

async def get_current_active_user(current_user: User = Depends(get_current_user)):
    if current_user.disabled:
        raise HTTPException(status_code=400, detail="Inactive user")
    return current_user

@router.get("/users/me", response_model=User)
async def read_users_me(current_user: User = Depends(get_current_active_user)):
    return current_user
Enter fullscreen mode Exit fullscreen mode

Conclusion

Implementing robust authentication and role-based access control is crucial for securing a FastAPI application. By following these examples, you can set up a secure API that uses OAuth2 for authentication and implements simple RBAC to manage user access based on roles.


Stay tuned for more advanced topics in this series, where we’ll explore testing strategies, integrate

Top comments (0)