0% found this document useful (0 votes)
4 views10 pages

Building Generative AI Services With FastAPI51

FastAPI provides HTTP security mechanisms, including basic authentication and JWT authentication, to protect endpoints. Basic authentication can be implemented using FastAPI's dependency injection system, while JWTs offer a more secure alternative by storing authentication details within a token. To implement JWT authentication, necessary dependencies must be installed, and database tables for users and tokens need to be created, along with defining SQLAlchemy models and Pydantic schemas for data validation.

Uploaded by

xiaowang198808
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
4 views10 pages

Building Generative AI Services With FastAPI51

FastAPI provides HTTP security mechanisms, including basic authentication and JWT authentication, to protect endpoints. Basic authentication can be implemented using FastAPI's dependency injection system, while JWTs offer a more secure alternative by storing authentication details within a token. To implement JWT authentication, necessary dependencies must be installed, and database tables for users and tokens need to be created, along with defining SQLAlchemy models and Pydantic schemas for data validation.

Uploaded by

xiaowang198808
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

FastAPI has implemented several HTTP security

mechanisms including HTTP​ sic that can leverage the


Ba⁠
FastAPI’s dependency injection system.

Use the secrets built-in library to compare the provided


username and password with the server’s values. Using
secrets.compare_digest() ensures the duration of
checking operations remain consistent no matter what the
3
inputs are to avoid timing attacks.
Note that secrets.compare_digest() can only accept
byte or string inputs containing ASCII characters (i.e.,
English-only characters). To handle other characters, you
will need to encode the inputs with UTF-8 to bytes first
before performing the credential checks.
Return a standardized authorization HTTPException
compliant with security standards that browsers
understand so that they show the login prompt again to
the user. The exception message must be generic to avoid
leaking any sensitive information, such as the existence of
a user account, to attackers.

Using the HTTPBasic with Depends() returns the


HTTPBasicCredentials object that contains the
provided username.
Injecting a security dependency to any FastAPI endpoint will
protect it with the implemented authentication. You can
experience this yourself now by visiting the /docs page and
sending a request to the /users/me endpoint.

The endpoint will show a lock icon in front of it, and you should
see a sign-in alert when making a request, asking you to
provide credentials, as you can see in Figure 8-2.

Figure 8-2. Basic authentication in FastAPI


Well done! In 25 lines of code, you managed to implement a
basic form of authentication to protect an endpoint. You can
now use basic authentication in your own prototypes and
development servers.

Bear in mind, you should avoid adopting the basic


authentication mechanism in production-grade GenAI services.
A better and more secure alternative for public-facing services
is JWT authentication. It eliminates the need for server-side
sessions by storing all authentication details within a token. It
also maintains data integrity and works across different
domains with a widely accepted standard.

JSON Web Tokens (JWT) Authentication

Now that you’re more familiar with basic concepts of


authentication, let’s implement a more complex but secure JWT
authentication layer to your FastAPI service. As part of this,
you’ll also refactor your existing endpoints to combine them
under a separate resource API router to group, name, tag, and
protect multiple endpoints at once.
What is JWT?

JWTs are a URL-safe and compact way of asserting claims


between applications via tokens.

These tokens consist of three parts:

Headers

Specify the token type and signing algorithm in addition


to the datetime and the issuing authority.

Payload

Specify the body of the token representing the claims on


the resource alongside additional metadata.

Signature

The function that creates the token will also sign it using
the encoded payload, encoded headers, a secret, and the
signing algorithm.

TIP

The base64 encoding algorithm is often used to encode and decode data for
compactness and URL safety.
Figure 8-3 shows what a typical JWT looks like.

Figure 8-3. JWT components (Source: [Link])

JWTs are secure, compact, and convenient since they can hold
all the information needed to perform user authentication,
avoiding the need for multiple database round-trips. In
addition, due to their compactness, you can transfer them
across the network using the HTTP POST body, headers, or URL
parameters.
Getting started with JWT authentication

To get started with implementing the JWT authentication


mechanism in FastAPI, you need to install the passlib and
python-jose dependencies:

$ pip install passlib python-jose

With the dependencies installed, you will then need tables in


the database to store the generated users and associated token
data. For data persistence, let’s migrate the database to create
the users and tokens tables, as shown in Figure 8-4.

Figure 8-4. Entity relationship diagram of users and tokens tables


If you look at Figure 8-4, you will spot that the tokens table
has a one-to-many relationship with the users table. You can
use the token records to track successful login attempts for each
user and to revoke access if needed.

Next, let’s define the required SQLAlchemy models and


Pydantic schemas for database queries and data validation, as
shown in Examples 8-2 and 8-3.

Example 8-2. Declare user SQLAlchemy ORM models

# [Link]

import uuid
from datetime import UTC, datetime
from sqlalchemy import Index, String
from [Link] import DeclarativeBase, Mappe

class Base(DeclarativeBase):
pass

class User(Base):
__tablename__ = "users"

id: Mapped[[Link]] = mapped_column(primary


email: Mapped[str] = mapped_column(String(len
hashed_password: Mapped[str] = mapped_column
is_active: Mapped[bool] = mapped_column(defau
role: Mapped[str] = mapped_column(default="US
created_at: Mapped[datetime] = mapped_column
updated_at: Mapped[datetime] = mapped_column
default=[Link](UTC), onupdate=datet
)

__table_args__ = (Index("ix_users_email", "em

You will be using the ORM models at the data access layer while
the Pydantic schemas will validate incoming and outgoing
authentication data at the endpoint layer.

Example 8-3. Declare user Pydantic schemas with username


and password field validators

# [Link]

from datetime import datetime


from typing import Annotated
from pydantic import (UUID4, AfterValidator, Base
validate_call)

@validate_call
def validate_username(value: str) -> str:
if not [Link]():
raise ValueError("Username must be alphan
return value

@validate_call
def validate_password(value: str) -> str:
validations = [
(
lambda v: any([Link]() for cha
"Password must contain at least one d
),
(
lambda v: any([Link]() for cha
"Password must contain at least one u
),
(
lambda v: any([Link]() for cha
"Password must contain at least one l
),
]
for condition, error_message in validations:
if not condition(value):
raise ValueError(error_message)
return value

ValidUsername = Annotated[
str, Field(min_length=3, max_length=20), Afte
]
ValidPassword = Annotated[
str, Field(min_length=8, max_length=64), Afte
]

class UserBase(BaseModel):
model_config = ConfigDict(from_attributes=Tru

username: ValidUsername
is_active: bool = True
role: str = "USER"

class UserCreate(UserBase):
password: ValidPassword

class UserInDB(UserBase):
hashed_password: str

class UserOut(UserBase):
id: UUID4
created_at: datetime
updated_at: datetime

Validate both username and password to enforce higher


security requirements.

Allow Pydantic to read SQLAlchemy ORM model


attributes instead of having to manually populate
Pydantic schemas from SQLAlchemy models.

You might also like