Authentication of APIs in FastAPI

Exploring Username & Password Based Authentication and Token-Based Authorization

DeepyaC
Stackademic

--

Introduction:

Authentication is a fundamental aspects of securing APIs, and FastAPI provides a wide range of tools for implementing these mechanisms. In this blog, we’ll explore two common approaches for API authentication in FastAPI: Username and Password-Based Authentication and Token-Based Authorization.

Username and Password-Based Authentication

Username and password-based authentication is a straightforward way to secure your API. It requires users to provide valid credentials in the form of a username and password in the HTTP headers. This approach is suitable for scenarios with a limited number of users who need access to your API.

Implementation Example:

from fastapi import FastAPI, Depends, HTTPException
from fastapi.security import HTTPBasic, HTTPBasicCredentials

app = FastAPI()
security = HTTPBasic()

def verify_credentials(credentials: HTTPBasicCredentials = Depends(security)):
username = credentials.username
password = credentials.password

if not is_valid_credentials(username, password):
raise HTTPException(status_code=401, detail="Invalid credentials")

return username

def is_valid_credentials(username, password):
# Implement your custom verification logic here, e.g., by checking against a database
# Return True if credentials are valid, else return False
return True # Replace this with your actual verification logic

@app.get("/protected_resource")
async def get_protected_resource(username: str = Depends(verify_credentials)):
return {"message": f"Welcome, {username}!"}
  1. FastAPI executes the security object, typically an instance of HTTPBasic, upon a request to a route.
  2. HTTPBasic parses and extracts credentials from the "Authorization" header, expecting a base64-encoded "username:password" string.
  3. If the header is missing or invalid, it raises a 401 HTTPException, indicating the need for authentication.
  4. Valid credentials trigger a call to the verify_credentials function, which checks if username and password are valid. If valid, the username is returned, allowing the route handler to proceed. If not, an HTTPException with a 401 status and “Invalid credentials” message is raised.

Example Headers for Username and Password-Based Authentication:

To access the protected resource, you need to provide the “Authorization” header with the “Basic” authentication scheme. Here’s an example:

GET /protected_resource HTTP/1.1
Host: your-api-host.com
Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ=
  • “Basic” is the authentication scheme.
  • “dXNlcm5hbWU6cGFzc3dvcmQ=” is the base64-encoded string of “username:password.”

Pros:

  • Simple to implement.
  • Well-suited for scenarios with a limited number of users.
  • Credentials can be easily managed and stored.

Cons:

  • Credentials must be sent with every request, potentially exposing them if not secured properly.
  • Not suitable for large-scale applications or public APIs.

Token-Based Authorization

Token-based authorization is a widely-used method for securing APIs, where users must present a valid token as part of the “Authorization” header. This approach is highly versatile and can be suitable for various use cases.

Implementation Example:

from fastapi import FastAPI, Depends, HTTPException
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials

app = FastAPI()

# Implement your token verification logic here
def verify_jwt(credentials: HTTPAuthorizationCredentials) -> bool:

token = str(credentials.credentials)
# Implement your verification logic here, e.g., by checking against a database
# Or Send a request to an authentication service to verify the token
# url = settings.auth_service_url + "/verify"
# response = requests.post(url, json={"token": str(token)})
# Return True if the token is valid, else return 401 response
return True # Replace this with your actual verification logic
# if not valid
# raise HTTPException(status_code=401, detail="Invalid credentials")


class JWTBearer(HTTPBearer):
async def __call__(self, request: Request):
credentials: HTTPAuthorizationCredentials = await super(JWTBearer, self).__call__(request)
if credentials and verify_jwt(credentials):
return credentials.credentials


@app.get("/protected_resource")
async def get_protected_resource(Depends(JWTBearer())):
return {"message": f"Welcome!"}
  1. A FastAPI route, /protected_resource, is defined to handle GET requests. Access to this route is protected, requiring proper authorization.
  2. A custom JWTBearer class is created, extending FastAPI's built-in HTTPBearer class. This class is responsible for handling token-based authentication.
  3. The JWTBearer class's __call__ method is used to extract credentials from the "Authorization" header and invoke the verify_jwt function. If the token cannot be extracted due to improper headers, the __call__ method raises an HTTPException with a 403 status code.
  4. If verify_jwt returns True, the route handler proceeds. If it returns False, access is denied, ensuring that only authenticated users with valid tokens can access the protected route.

Example Headers for Token-Based Authorisation:

To access the protected resource, you need to provide the “Authorization” header with the “Bearer” authentication scheme. Here’s an example:

GET /protected_resource HTTP/1.1
Host: your-api-host.com
Authorization: Bearer your-access-token
  • “Bearer” is the authentication scheme.
  • “your-access-token” is the valid token you’ve obtained for authorization.

Pros:

  • Tokens are stateless and can be easily used for authentication and authorization.
  • Well-suited for large-scale applications and public APIs.
  • Provides flexibility for token generation, expiration, and revocation.

Cons:

  • Implementing token verification logic can be more complex.
  • Requires token management (generation, distribution, and revocation).
  • Might need a separate API for generating and verifying tokens.

Conclusion:

Authentication is a critical aspect of API security, and FastAPI offers numerous approaches to achieve it. In this blog, we’ve delved into two approaches: “Username and Password-Based Authentication” and “Token-Based Authorization.” The choice between them depends on your application’s specific requirements. By implementing the right authentication mechanism, you can ensure that your APIs are protected and accessible only to authorized users.

References:

Also, read about Implementing Python Logging Module with Aspect-Oriented Programming in this small blog

Thank you for taking your time to read my blog. I would greatly appreciate your feedback or even just a clap to show your support. It helps me feel motivated to write more. Happy Coding!

--

--