Introduction to JWT Authentication
JSON Web Token (JWT) is a stateless authentication mechanism used to securely exchange user information between a client and a server. After a successful login, the server generates a signed token and sends it to the client. The client includes this token in theAuthorization header of subsequent requests, allowing the server to authenticate the user without storing any session information.
Unlike Session-Based Authentication, the server does not maintain authentication state. Instead, all the information required to authenticate the user is contained within the JWT.
For a deeper understanding of JWT, including how tokens are encoded and decoded, visit the official JWT website:
:contentReference[oaicite:0]
What is a JWT?
A JWT is a compact, URL-safe string consisting of three parts separated by dots (.).
Structure of a JWT
1. Header
The header contains metadata about the token. Typical information includes:- Token type (
JWT) - Signing algorithm (for example,
HS256)
2. Payload
The payload contains information (called claims) about the authenticated user. Typical claims include:- User ID
- Username
- Role
- Permissions
- Issued time (
iat) - Expiration time (
exp)
Note: The payload is Base64URL encoded, not encrypted. Anyone possessing the token can decode and read its contents. Therefore, never store sensitive information such as passwords or secret keys inside a JWT.
3. Signature
The signature is generated using:- Header
- Payload
- Secret key
- Signing algorithm
- The token has not been modified.
- The token was generated by a trusted server.
Complete JWT Authentication & Authorization Flow
Authorization Header
The client sends the JWT using theAuthorization header.
Why is JWT Stateless?
Unlike Session-Based Authentication, the server does not store authentication sessions.Advantages of JWT Authentication
- Users authenticate only once by logging in.
- Username and password are not sent with every request.
- No server-side session storage is required.
- Stateless authentication improves scalability.
- Works well with REST APIs, mobile applications, and microservices.
- Tokens are digitally signed, allowing the server to detect tampering.
- JWT can carry useful user information (claims), such as:
- User ID
- Username
- Roles
- Permissions
- Roles and permissions can be used directly for authorization checks.
- Works seamlessly with Single Page Applications (SPA) and third-party clients.
- Reduces database lookups when appropriate because user claims travel with the token.
Limitations
- Tokens cannot be easily revoked before they expire.
- The payload is encoded, not encrypted.
- Sensitive information should never be stored in the payload.
- A JWT is larger than a session ID and is sent with every request.
- Short-lived access tokens are recommended for better security.
- If user roles or permissions change, applications often retrieve the latest user information from the database instead of relying solely on the claims inside the token.
Typical Use Cases
- REST APIs
- Mobile applications
- Single Page Applications (SPA)
- Microservices
- Cloud-native applications
Comparison
What We Will Implement
In this section, we will implement:- User registration.
- Password hashing.
- User login.
- JWT generation.
- JWT validation.
- Authentication dependency.
- Public and protected endpoints.
- Role-based authorization in the next section.
Task 1 - Create the Project
Goal
Create a new FastAPI project for implementing JWT Authentication using SQLite.Create the Project
Installed Packages
Create the Project Structure
Update database.py
Update app.py
Run the Application
Verify
Open the following URLs:Expected Outcome
At the end of this task, you should have:- A new FastAPI project.
- All required dependencies installed.
- A configured SQLite database.
- A running FastAPI application.
- The project ready for implementing JWT authentication.
Task 2 - Create the User Model
Goal
Create theUser model and request models required for implementing JWT authentication.
Update models.py
Run the Application
Verify
After restarting the application, a new SQLite database namedjwt_auth.db should be created with a user table.
Note
Unlike Session-Based Authentication, JWT Authentication does not require aSession table. The server does not store authentication state. Instead, all authentication information is carried inside the JWT, making the application stateless.
Expected Outcome
At the end of this task, you should have:- A
Usermodel. - Request models for registration and login.
- A
usertable created automatically in the SQLite database.
Task 3 - Implement Password Hashing
Goal
Implement password hashing and password verification usingbcrypt.
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 verified against 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 secure mechanism for storing user passwords.
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
Import the required modules.
Run the Application
Test the API
Open the Swagger UI.Verify
Open theuser table in the SQLite database.
The password should be stored as a hashed value similar to:
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 Login and Generate JWT
Goal
Authenticate users using their username and password. Upon successful authentication, generate a JWT and return it to the client.Update auth.py
Import the required modules.
Update app.py
Import the required modules.
How It Works
- Receive the username and password.
- Retrieve the user from the database.
- Verify the password.
- Create the JWT payload.
- Add an expiration time.
- Sign the JWT using the secret key.
- Return the JWT to the client.
Run the Application
Test the API
Open the Swagger UI.Expected Response
- Header
- Payload
- Signature
Expected Outcome
At the end of this task, you should have:- A working login API.
- Username and password verification.
- JWT generation.
- Signed access tokens with an expiration time.
Task 6 - Validate JWT and Authenticate Users
Goal
Validate the JWT received in theAuthorization header and return the authenticated user.
Update auth.py
Import the required modules.
Nested Dependencies
Theauthenticate_user() function is itself a dependency and depends on two other dependencies.
HTTPBearer()extracts the Bearer token from theAuthorizationheader.get_session()provides a database session.
How It Works
- Read the Bearer token from the
Authorizationheader. - Verify the JWT signature.
- Verify the token has not expired.
- Extract the user ID from the token.
- Retrieve the user from the database.
- Return the authenticated user.
- Return 401 Unauthorized if the token is invalid, expired, or the user does not exist.
Authorization Header
The client must send the JWT as a Bearer token.Expected Outcome
At the end of this task, you should have:- A reusable JWT authentication dependency.
- JWT signature validation.
- Token expiration validation.
- Retrieval of the authenticated user.
- Proper
401 Unauthorizedresponses for invalid or expired tokens.
Task 7 - Create Public and Protected Endpoints
Goal
Create public and protected endpoints using JWT authentication.Update app.py
Import the authentication dependency.
How It Works
The/public endpoint is accessible to everyone.
The /protected endpoint depends on the authenticate_user() dependency. Before executing the endpoint, FastAPI:
- Reads the JWT from the
Authorizationheader. - Validates the JWT signature.
- Checks whether the token has expired.
- Retrieves the authenticated user.
- Executes the endpoint only if authentication succeeds.
Run the Application
Test the APIs
Open the Swagger UI.Step 1 - Register
Register a new user.Step 2 - Login
Login to receive a JWT.access_token.