4-how-to-secure-api-endpoints-with-oauth-20-in-fastapi.html

How to Secure API Endpoints with OAuth 2.0 in FastAPI

In the modern web development landscape, securing API endpoints is paramount, especially when sensitive information is involved. One of the most robust ways to ensure API security is through OAuth 2.0, a protocol for authorization that allows applications to securely access user data without sharing passwords. In this article, we'll explore how to implement OAuth 2.0 in FastAPI, a popular Python web framework, with detailed code examples and actionable insights.

Understanding OAuth 2.0

Before diving into the code, it's essential to understand what OAuth 2.0 is and how it works. OAuth 2.0 is an authorization framework that enables third-party services to exchange access tokens for resource access. Instead of sharing credentials, users can grant limited access to their resources, ensuring better security.

Key Concepts

  • Resource Owner: Typically, the user who owns the data.
  • Client: The application requesting access to the resource.
  • Authorization Server: The server that verifies the resource owner's identity and issues access tokens.
  • Resource Server: The server that holds the protected resources and accepts access tokens for authentication.

Use Cases for OAuth 2.0

OAuth 2.0 is widely used in various scenarios, including:

  • Social Logins: Allowing users to sign in using their Google, Facebook, or GitHub accounts.
  • API Access: Providing third-party applications with limited access to user data.
  • Microservices: Securing communication between different services in a microservices architecture.

Setting Up FastAPI with OAuth 2.0

FastAPI is an excellent choice for building APIs due to its speed and ease of use. Here’s how to secure API endpoints using OAuth 2.0 in FastAPI.

Step 1: Install Required Packages

First, ensure you have FastAPI and the required libraries installed. You can do this using pip:

pip install fastapi uvicorn python-multipart python-jose[cryptography] passlib[bcrypt]

Step 2: Create a FastAPI Application

Let's start by creating a simple FastAPI application. In your project directory, create a file named main.py and add the following code:

from fastapi import FastAPI

app = FastAPI()

@app.get("/")
async def root():
    return {"message": "Welcome to the FastAPI OAuth 2.0 example!"}

Step 3: Implement OAuth 2.0

Now, let’s implement the OAuth 2.0 flow. We will create an authorization server and a resource server within the same FastAPI application.

Setting Up the OAuth2 Scheme

Define the OAuth2 scheme using the OAuth2PasswordBearer class:

from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")

Create User Authentication

Next, create a function to verify user credentials. In a real application, you would typically query a database:

from fastapi import Depends, HTTPException
from passlib.context import CryptContext

# Dummy database
fake_users_db = {
    "johndoe": {
        "username": "johndoe",
        "full_name": "John Doe",
        "email": "johndoe@example.com",
        "hashed_password": "$2b$12$KIX5VwK8lGF1XWc5Y7DEhO6j/6Vx5dG1U0E5lZc0Zc7tD8Qp2zqrm",  # hashed "secret"
        "disabled": False,
    }
}

pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")

def verify_password(plain_password, hashed_password):
    return pwd_context.verify(plain_password, hashed_password)

def get_user(db, username: str):
    if username in db:
        user_dict = db[username]
        return user_dict
    return None

def authenticate_user(db, username: str, password: str):
    user = get_user(db, username)
    if not user or not verify_password(password, user['hashed_password']):
        return False
    return user

Step 4: Token Creation

Now, let's implement token creation using JSON Web Tokens (JWT):

from datetime import datetime, timedelta
from jose import JWTError, jwt

SECRET_KEY = "your_secret_key"
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30

def create_access_token(data: dict, expires_delta: 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

Step 5: Create the Token Endpoint

Add a route to generate the access token:

@app.post("/token")
async def login(form_data: OAuth2PasswordRequestForm = Depends()):
    user = authenticate_user(fake_users_db, form_data.username, form_data.password)
    if not user:
        raise HTTPException(status_code=400, detail="Incorrect username or password")
    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"}

Step 6: Protect API Endpoints

To secure an endpoint, use the oauth2_scheme to obtain the access token:

@app.get("/users/me")
async def read_users_me(token: str = Depends(oauth2_scheme)):
    # Here you would decode the token and retrieve user information
    return {"token": token}

Conclusion

Securing API endpoints using OAuth 2.0 in FastAPI can significantly enhance your application's security. By implementing proper authorization flows, you ensure that only authorized users can access sensitive data. Remember to always store your secret keys securely and consider using environment variables for production applications.

With the steps outlined in this article, you should now have a solid foundation to implement OAuth 2.0 in your FastAPI applications. Happy coding!

SR
Syed
Rizwan

About the Author

Syed Rizwan is a Machine Learning Engineer with 5 years of experience in AI, IoT, and Industrial Automation.