Skip to main content

Step 1 - User Registration & Basic Authentication

Build a simple authentication system using FastAPI, SQLite, and HTTP Basic Authentication. Users can register with a username and password. Passwords are securely hashed before being stored. Public endpoints are accessible by everyone, while protected endpoints require valid username and password credentials.

What We Will Build

  • Configure a SQLite database.
  • Create a User model.s
  • Register new users.
  • Hash passwords using bcrypt.
  • Verify user credentials.
  • Implement HTTP Basic Authentication.
  • Create public and protected API endpoints.

APIs

Authentication

Protected endpoints use HTTP Basic Authentication. The client sends the username and password in the Authorization header:
The application verifies the credentials against the hashed password stored in the database before granting access.

Task 1 - Create the Project

Goal

Create a new FastAPI project for implementing authentication using SQLite and HTTP Basic Authentication.

Create the Project

Create the Project Structure

Create the required files. Windows
macOS / Linux
Add the following code to app.py.
Start the development server.
Open your browser and navigate to: At the end of this task, you should have:
  • A FastAPI project initialized using uv.
  • Required dependencies installed.
  • The basic project structure created.
  • The application running successfully.

Task 2 - Create the User Model

Goal

Create the User model to store user credentials and update the application to create the database tables automatically.

Update models.py

Update app.py

Import the User model before creating the database tables.

Why Import User?

Although the User model is not referenced directly, importing it registers the model with SQLModel.metadata. When SQLModel.metadata.create_all(engine) is executed, SQLModel creates the user table in the database.

Run the Application

Verify

After starting the application, a new SQLite database named auth.db should be created with a user table containing the following columns:

Expected Outcome

At the end of this task, you should have:
  • A User model.
  • A RegisterRequest model.
  • A user table created automatically in the SQLite database.
  • A FastAPI application ready for implementing user registration.

Task 3 - Implement Password Hashing

Goal

Implement password hashing and verification using bcrypt.

Update auth.py

Run the File

Expected Output

Why Hash Passwords?

Passwords should never be stored as plain text. Instead, only their hashed values are stored in the database. During authentication, the entered password is hashed and compared with the stored hash.

Expected Outcome

At the end of this task, you should have:
  • A reusable password hashing function.
  • A reusable password verification function.
  • A simple way to test the implementation independently.

Task 4 - Implement User Registration

Goal

Create an API to register new users by storing their username and hashed password in the database.

Update app.py

Run the Application

Test the API

Open the Swagger UI.
Register a new user.

Verify

Open the user table in your SQLite database. The password should be stored as a hashed value similar to:
instead of

Expected Outcome

At the end of this task, you should have:
  • A working user registration API.
  • Duplicate username validation.
  • Passwords stored securely using hashing.

Task 5 - Implement HTTP Basic Authentication

Goal

Authenticate users using HTTP Basic Authentication by verifying their username and password.

Update auth.py

Add the following code.

Nested Dependencies

The authenticate_user() function is a dependency that itself depends on two other dependencies.
  • security extracts the username and password from the Authorization header.
  • get_session() provides a database session.
This is known as nested dependency injection.

Why Does It Work?

Although authenticate_user() is not a route handler, it is executed by FastAPI because it is declared as a dependency using Depends(). FastAPI automatically resolves all nested dependencies before calling the function. Later, we’ll use it like this:
The dependency resolution flow is:

Important

Depends() works only when FastAPI executes the function. This works because FastAPI calls authenticate_user() as a dependency.
However, calling the function directly does not resolve its dependencies.
In this case, credentials and session would simply be Depends objects instead of actual values.

Expected Outcome

At the end of this task, you should have:
  • A reusable authentication dependency.
  • Username verification.
  • Password verification.
  • Proper 401 Unauthorized responses for invalid credentials.
  • An understanding of nested dependency injection in FastAPI.

Task 6 - Create Public and Protected Endpoints

Goal

Create public and protected endpoints to understand how authentication controls access to API resources.

Update app.py

Add the following endpoints.

How It Works

The /public endpoint is accessible to everyone. The /protected endpoint depends on the authenticate_user() dependency. Before executing the endpoint, FastAPI:
  1. Extracts the username and password from the Authorization header.
  2. Verifies the credentials.
  3. Returns the authenticated user.
  4. Executes the endpoint only if authentication succeeds.

Run the Application

Test the APIs

Open the Swagger UI.

Public Endpoint

Call:
This endpoint is accessible without authentication.

Protected Endpoint

Call:
Click Authorize in Swagger UI and enter the registered username and password. If the credentials are valid, the response will be:
If the credentials are invalid, the response will be:

Authentication Flow

Expected Outcome

At the end of this task, you should have:
  • A public endpoint accessible to everyone.
  • A protected endpoint requiring valid credentials.
  • A working implementation of HTTP Basic Authentication.