Skip to main content

JWT Authentication with FastAPI

Objective In this module, you will build a complete JWT-based authentication system using FastAPI. By the end of this module, you will be able to:
  • Create a User model using SQLAlchemy ORM.
  • Securely hash and verify user passwords.
  • Generate and validate JWT access tokens.
  • Implement user registration and login APIs.
  • Authenticate users using JWT.
  • Protect REST APIs using authentication dependencies.
  • Implement role-based authorization.

Architecture

Implementation Flow

  1. Create the Project Structure
  2. Configure the Database
  3. Create the User ORM Model
  4. Create the User Schemas
  5. Initialize the FastAPI Application
  6. Implement Authentication Utilities
  7. Implement the Register API
  8. Implement the Login API
  9. Implement the Current User Authentication Dependency
  10. Create Public and Protected APIs
  11. Test the Complete Authentication Flow

Step 1: Create the Project Structure

Objective Create the project structure and install the required libraries for implementing JWT authentication. Instructions Create a new FastAPI project and organize it using the following structure.
Install the required libraries using uv.
  • FastAPI
  • Uvicorn
  • SQLAlchemy
  • pwdlib
  • python-jose (with cryptography support)
Task Create the project structure and install the required dependencies for the JWT Authentication project.
Create the Project
Create a Virtual Environment
Activate the Virtual EnvironmentmacOS / Linux
Windows
Install the Required Libraries
Create the Project Structure
The project structure should look like this:
Verify Verify that:
  • The project has been created successfully.
  • The virtual environment has been activated.
  • All required libraries have been installed.
  • The app directory has been created.
  • All Python files have been created.
  • The project structure matches the required layout.
Commit Changes
Commit Changes Create a .gitignore file with the following content.
Initialize the Git repository and commit the project.

Step 2: Configure the Database

Objective Configure the SQLite database and implement the database session dependency. Instructions Create a database.py file and implement the following:
  • Configure the SQLite database.
  • Create the SQLAlchemy engine.
  • Create the Base class.
  • Implement the get_db() dependency for managing database sessions.
Task Configure the SQLite database and implement the database session dependency.
app/database.py
Verify Verify that:
  • The SQLite database URL has been configured.
  • The SQLAlchemy engine has been created.
  • The Base class has been implemented.
  • The get_db() dependency has been implemented.
Note: The users.db database file will be created automatically when the database tables are created in a later step.
Commit Changes

Step 3: Create the User ORM Model

Objective Create the User ORM model for storing user information in the SQLite database. Instructions Create a models.py file and implement the User ORM model. The model should contain the following fields:
  • id
  • name
  • email
  • password
  • role
Apply the following constraints:
  • id should be the primary key.
  • email should be unique.
  • name, email, and password should be mandatory.
  • role should default to "user".
Task Create the User ORM model.
app/models.py
Verify Verify that:
  • The User model has been created.
  • The model inherits from Base.
  • The table name is users.
  • The id field is the primary key.
  • The email field has a unique constraint.
  • The role field has a default value of "user".
Note: The database table will be created in the next step when the FastAPI application is initialized.
Commit Changes

Step 4: Create the User Schemas

Objective Create Pydantic schemas for validating API requests and formatting API responses. Instructions Create a schemas.py file and implement the following schemas:
  • UserCreate
  • UserResponse
The UserCreate schema should contain:
  • name
  • email
  • password
Apply the following validations:
  • name should have a minimum length of 3 characters.
  • name should have a maximum length of 50 characters.
  • email should be a valid email address.
  • password should have a minimum length of 8 characters.
The UserResponse schema should contain:
  • id
  • name
  • email
  • role
Configure the schema to read data directly from SQLAlchemy ORM objects. Task Create the User request and response schemas.
app/schemas.py
Verify Verify that:
  • The UserCreate schema has been created.
  • The UserResponse schema has been created.
  • The name field validates the minimum and maximum length.
  • The email field accepts only valid email addresses.
  • The password field validates the minimum length.
  • The UserResponse schema is configured to read data from ORM objects.
Commit Changes

Step 5: Initialize the FastAPI Application

Objective Initialize the FastAPI application, create the database tables, and implement a simple Home endpoint. Instructions Open the main.py file and implement the following:
  • Create a FastAPI application.
  • Import the database engine.
  • Import the User model.
  • Create the database tables.
  • Implement a Home endpoint.
Task Initialize the FastAPI application and create the database tables.
app/main.py
Verify Start the FastAPI application.
Verify that:
  • The application starts successfully.
  • A users.db database file is created.
  • A users table is created in the database.
  • The Home endpoint is accessible.
Open the following URL in your browser:
Expected Response
You can also open the Swagger UI.
Verify that the Home endpoint appears in the API documentation. Commit Changes

Step 6: Implement Authentication Utilities

Objective Implement reusable helper functions for password hashing, password verification, JWT generation, and JWT validation. These helper functions will be used by the Register API, Login API, and Protected APIs. Instructions Create an auth.py file and implement the following:
  • Configure the Secret Key.
  • Configure the JWT Signing Algorithm.
  • Configure the Token Expiration Time.
  • Implement the hash_password() function.
  • Implement the verify_password() function.
  • Implement the create_access_token() function.
  • Implement the verify_access_token() function.
Task Implement the authentication utility functions required for JWT authentication.
app/auth.py
Verify Verify that:
  • The Secret Key has been configured.
  • The JWT signing algorithm has been configured.
  • The token expiration time has been configured.
  • The password hashing utility has been implemented.
  • The password verification utility has been implemented.
  • The JWT generation utility has been implemented.
  • The JWT validation utility has been implemented.
Note: These helper functions will be used in the upcoming steps to implement user registration, login, and protected REST APIs.
Commit Changes

Step 7: Implement the Register API

Objective Implement the Register API to create a new user account. Before storing the user in the database, securely hash the password using the authentication utility. Instructions Open the main.py file and implement the Register API. The API should perform the following:
  • Accept user registration details.
  • Validate the request using the UserCreate schema.
  • Check whether the email already exists.
  • Hash the password.
  • Create a new user.
  • Save the user to the database.
  • Return the created user.
Task Implement the Register API.
Update app/main.py
Verify Start the FastAPI application.
Open the Swagger UI.
Test the Register API POST /register Request Body
Verify that:
  • The user is created successfully.
  • A 201 Created response is returned.
  • The password stored in the database is hashed.
  • The response does not include the password.
  • Registering the same email again returns 409 Conflict.
Open the users.db database using DB Browser for SQLite and verify that the password column contains a hashed value instead of the original password. Commit Changes

Step 8: Create the Authentication Schemas

Objective Create the Pydantic schemas required for user authentication. Instructions Open the schemas.py file and implement the following schemas:
  • LoginRequest
  • TokenResponse
The LoginRequest schema should contain:
  • email
  • password
The TokenResponse schema should contain:
  • access_token
  • token_type
Task Create the authentication schemas.
Update app/schemas.py
Verify Verify that:
  • The LoginRequest schema has been created.
  • The TokenResponse schema has been created.
  • The email field accepts valid email addresses.
  • The password field uses the shared Password validation.
Commit Changes

Step 9: Implement the Login API

Objective Implement the Login API to authenticate a user and generate a JWT access token. Instructions Open the main.py file and implement the Login API. Implementation Steps Step 1: Import the required authentication utilities and schemas. Step 2: Create the Login API endpoint. Step 3: Retrieve the user using the email address. Step 4: Verify that the user exists. Step 5: Verify the entered password. Step 6: Generate a JWT access token. Step 7: Return the generated JWT to the client. Task Implement the Login API.
Update app/main.py
Verify Start the FastAPI application.
Open the Swagger UI.
Test the Login API Invoke the POST /login endpoint. Request Body
Verify that:
  • The user is authenticated successfully.
  • A JWT access token is returned.
  • The response contains the token type as "bearer".
  • An invalid email returns 401 Unauthorized.
  • An incorrect password returns 401 Unauthorized.
Note: Copy the generated JWT access token. It will be used in the next step to access the protected APIs.
Commit Changes

Step 10: Implement the Current User Authentication Dependency

Objective Implement a dependency that authenticates the current user using a JWT access token. The dependency will:
  • Read the Bearer token from the request.
  • Validate the JWT access token.
  • Extract the user information from the JWT payload.
  • Retrieve the authenticated user from the database.
  • Return the authenticated user.
Instructions Open the auth.py file and implement the current user authentication dependency. Implementation Steps Step 1: Import the required FastAPI security classes. Step 2: Create an HTTPBearer security instance. Step 3: Create a reusable database session dependency. Step 4: Implement the get_current_user() dependency. Step 5: Read the Bearer token from the request. Step 6: Validate the JWT access token. Step 7: Retrieve the authenticated user from the database. Step 8: Return the authenticated user. Task Implement the current user authentication dependency.
Update app/auth.py
Verify Start the FastAPI application.
Open the Swagger UI.
Verify that:
  • The application starts successfully.
  • The Authorize button appears in Swagger UI.
  • A Bearer token can be entered using the Authorize dialog.
  • The get_current_user() dependency is implemented successfully.
Note: The get_current_user() dependency will be used in the next step to protect REST APIs.
Commit Changes

Step 11: Create Public and Protected APIs

Objective Create public and protected REST APIs to demonstrate JWT-based authentication. The Public API should be accessible without authentication, while the Protected API should only be accessible to authenticated users. Instructions Open the main.py file and implement the Public and Protected APIs. Implementation Steps Step 1: Import the get_current_user() dependency. Step 2: Create a reusable dependency for the authenticated user. Step 3: Implement a Public API. Step 4: Implement a Protected API. Step 5: Access the authenticated user’s information inside the Protected API. Task Create the Public and Protected APIs.
Update app/main.py
Verify Start the FastAPI application.
Open the Swagger UI.
Test the Public API Invoke the GET /public endpoint. Verify that:
  • The endpoint is accessible without authentication.
  • A successful response is returned.
Expected Response
Test the Protected API
  1. Register a new user.
  2. Login using the registered user’s credentials.
  3. Copy the generated JWT access token.
  4. Click the Authorize button in Swagger UI.
  5. Enter the JWT token in the following format:
  1. Invoke the GET /profile endpoint.
Verify that:
  • The authenticated user’s details are returned.
  • Accessing the endpoint without a token returns 401 Unauthorized.
  • Accessing the endpoint with an invalid or expired token returns 401 Unauthorized.
Expected Response
Commit Changes

Step 12: Test the Complete Authentication Flow

Objective Test the complete JWT authentication workflow by registering a user, logging in, obtaining a JWT access token, and accessing both public and protected APIs. Instructions Run the FastAPI application and verify that the authentication system works as expected.

Test Scenarios

Step 1: Start the Application Run the application.
Open the Swagger UI.

Step 2: Register a New User Invoke the POST /register endpoint. Request Body
Verify that:
  • The user is registered successfully.
  • A 201 Created response is returned.
  • The password is stored as a hashed value in the database.

Step 3: Login Invoke the POST /login endpoint. Request Body
Verify that:
  • Login is successful.
  • A JWT access token is returned.
Example Response
Copy the generated JWT access token.
Step 4: Access the Public API Invoke the GET /public endpoint. Verify that:
  • The endpoint is accessible without authentication.
Expected Response

Step 5: Access the Protected API Click the Authorize button in Swagger UI. Enter the JWT access token.
Invoke the GET /profile endpoint. Verify that:
  • The authenticated user’s details are returned.
Example Response

Step 6: Verify Unauthorized Access Verify the following scenarios:
  • Access /profile without a JWT token.
  • Access /profile with an invalid JWT token.
  • Access /profile with an expired JWT token.
Verify that each request returns:

Expected Authentication Flow

Verify Verify that:
  • User registration works successfully.
  • Login returns a valid JWT access token.
  • Passwords are stored as hashed values.
  • Public APIs are accessible without authentication.
  • Protected APIs require a valid JWT.
  • Invalid or expired tokens return 401 Unauthorized.
Commit Changes