Introduction
In most FastAPI authentication tutorials, you only learn how to create a login system using JWT access token. However this approach is incomplete and not suitable for real-world applications.
Access token expires quickly. Once they expire, the user is logged out and must log in again. This creates a poor user experience.
To solve this problem, modern web-applications use a refresh token system.
In this tutorial, you will learn how to:
- Understand the difference between access token and refresh token.
- Implement refresh token in FastAPI.
- Generate new access tokens without forcing users to log in again.
- Build a more secure and production ready authentication system.
We will use:
- FastAPI – To build APIs
- SQLAlchemy – For database queries
- PostgreSQL – Database to store refresh token and user data.
- JWT (JSON Web Tokens) – To securely access APIs.
By the end of this tutorial, you will have a complete authentication system used in real-world applications.
Prerequisites
Before starting this tutorial, make sure you have:
- Python 3.9 or later
- Basic knowledge of FastAPI
- Understanding of JWT authentication
- PostgreSQL installed and running
- A code editor such as VS Code
This tutorial is the advanced version of our previous tutorial in which we built basic JWT authentication in FastAPI. If you have not built a JWT authentication yet, you can read this tutorial:
How to Create a JWT Authentication System in FastAPI
Project Overview
Let's understand the flow of our project. We will build a system with the following flow.
Step 1 – User Login
- User provides email and password.
- Server returns access token (short expiry time) and refresh token (long expiry time).
Step 2 – Access Protected Routes
- Frontend sends access token with request.
- API validates the token and returns data.
Step 3 – Token Expiry
- Access token expires after the expiration time.
Step 4 – Refresh Token Flow
- Frontend sends refresh token to
/refreshendpoint. - The server verifies it and returns a new access token.
Step 5 – Secure Storage
- Refresh tokens are stored in the database.
- This allows:
- Logout Functionality
- Token revocation
- Better Security
What We Will Build
By the end, your FastAPI backend API will support:
- User Signup
- Login with JWT
- Access Token Authorization
- Refresh Token System
- Protected Routes
- Token Renewal Without Login
Modifying User Model to Store Refresh Token
To implement refresh tokens, we need a way to store them in the database.
Because unlike access tokens, refresh tokens must be controlled, revoked, and managed securely.
Step 1 – Update models.py
Add a new field inside the User model to store refresh token.
from sqlalchemy import Column, Integer, String
class User(Base):
__tablename__ = "users"
id = Column(Integer, primary_key=True, index=True)
email = Column(String, unique=True, index=True)
Lets understand why it is necessary to store refresh token in the database.
With database storage we can:
- Delete token on logout
- Replace token on login
- Detect misuse
In this tutorial we will store one refresh token per user. So logging in again replaces the old token. This is simple and beginner friendly. However, in a real production system multiple refresh tokens are stored for each device.
Step 4 – Database Migration
In our previous tutorial we did not create the column for refresh token in the database. In order to add the column we need to perform database migration.
However this process is advanced and requires migration tools knowledge. The simplest way is to delete the previous database and create a new one with the same name and a refresh token column.
Generating Access and Refresh Token
In this step, we will create two separate tokens.
- Access Token – short-lived ( used in APIs)
- Refresh Token – long-lived ( used to generate new access token)
Step 1 – Create Token Utility Functions
Update the file auth.py file, if you have been following the previous tutorial.
from datetime import datetime, timedelta
from jose import jwt
SECRET_KEY = "your_secret_key"
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30
REFRESH_TOKEN_EXPIRE_DAYS = 7Step 2 – Create Access Token Function
Add the function to generate access tokens.
def create_access_token(data: dict):
to_encode = data.copy()
expire = datetime.utcnow() + timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
to_encode.update({"exp": expire})
return jwt.encode(to_encode, SECRET_KEY, algorithm=Step 3 – Create Refresh Token Function
def create_refresh_token(data: dict):
to_encode = data.copy()
expire = datetime.utcnow() + timedelta(days=REFRESH_TOKEN_EXPIRE_DAYS)
to_encode.update({"exp": expire})
return jwt.encode(to_encode, SECRET_KEY, algorithm=As you can see, the access token will expire in minutes whereas the refresh token will expire in days.
Step 5 – Update Login Route
Now modify your /login endpoint inside the main.py file.
from fastapi import APIRouter, Depends
from sqlalchemy.orm import Session
@router.post("/login")
def login(user: UserLogin, db: Session = Depends(get_db)):
db_user = db.query(User).filter(User.email == user.email).first()
if
Creating the Refresh Token Endpoint
This endpoint allows users to get a new access token without logging in again.
Step 1 – Create /refresh Route
Open the main.py file and update it by adding the /refresh route.
@router.post("/refresh")
def refresh_token(refresh_token: str, db: Session = Depends(get_db)):
try:
payload = jwt.decode(refresh_token, SECRET_KEY, algorithms=[ALGORITHM])
email = payload.get("sub"
Let's understand how this endpoint works.
- Client sends refresh token.
- Server verifies JWT.
- Server checks database
- The server generates a new access token if the refresh token exists in the database.
Remember: Never allow refresh tokens to access protected routes.
Implementing Refresh Token Rotation
So far our system works, but it still has one weakness.
Conclusion
In this tutorial, you learned how to build a complete JWT authentication system with refresh token in FastAPI.
We started with basic authentication and then improved it step by step into a production-ready system.
In this tutorial we build:
- User login with JWT
- Access tokens for protected routes
- Refresh tokens for session management
- Secure token storage in the database
- Refresh endpoint to generate new access tokens