101 Guide for Python User Authentication System
January 6, 2025

Securing user data is non-negotiable today, and for this, python user authentication is essential to protect your application from unauthorized access and potential threats.
At Times TX, we know building a secure system isn’t easy. Without the right tools, security issues can slip through, putting your users' data at risk.
That’s why we focus on a seamless Python authentication system with FastAPI and PostgreSQL. This approach secures user data and ensures smooth operation as your app grows.
This guide will help you create a secure Python authentication system using FastAPI and PostgreSQL, utilizing libraries that simplify the process for Python beginners.
But what exactly does user authentication mean, and why is it so crucial? Let’s first look at the basics, methods, and why it matters today.
What Exactly Does User Authentication Mean, and Why Is It So Crucial?
User authentication is the process of verifying the person attempting to log into the system. It helps verify a person’s identity before they gain access to data or services, ensuring their total safety and security.
Authentication is crucial because it prevents unauthorized access, keeping your users' data safe. Without it, anyone could access private information, leading to security risks.
FastAPI is a fast and simple web framework that makes it easy to create secure APIs in Python. PostgreSQL, a trusted open-source database, helps you store user data safely.
When used together, they create a solid foundation for building a secure and scalable authentication system.
Libraries Used in Setting Up the Python User Authentication System
FastAPI: For creating the API.
Uvicorn: The ASGI server implementation is used to run FastAPI applications.
Psycopg: PostgreSQL adapter for Python.
SQLModel: Combines the best parts of SQLAlchemy and Pydantic.
Asyncpg: Asyncio PostgreSQL driver.
SQLAlchemy (asyncio): SQL toolkit and Object-Relational Mapping (ORM) library.
Passlib (bcrypt): Library for hashing passwords.
Python-jose (cryptography): For JWT token creation and verification.
Python-multipart: For handling form data.
Emails: Library for sending emails.
Pyotp: For generating and verifying One-Time Passwords (OTPs).
Alembic: Database migrations tool for SQLAlchemy.
Python User Authentication System Development Process
Setting Up the Project Boilerplate
Step 1: Initial Setup
First, ensure you have Python 3.7+ installed. Create a new directory for your project and set up a virtual environment.
mkdir fastapi-auth-system
cd fastapi-auth-system
python3 -m venv env
source env/bin/activate
Next, install the necessary libraries:
pip install fastapi uvicorn psycopg2-binary asyncpg sqlmodel sqlalchemy alembic passlib python-jose python-multipart emails pyotp
Step 2: Project Structure
Organize your project as follows:
fastapi-auth-system/
├── alembic/
│ ├── versions/
│ └── env.py
├── app/
│ ├── __init__.py
│ ├── main.py
│ ├── config.py
│ ├── models.py
│ ├── crud.py
│ ├── schemas.py
│ ├── auth.py
│ ├── deps.py
│ └── routers/
│ ├── __init__.py
│ └── users.py
├── .env
└── alembic.ini
Step 3: Configuration
Store your secrets in the .env file. This file should not be shared or committed to version control.
Use environment variables to manage sensitive information like database connection strings and secret keys.
Use export with a variable name to load the variable in the environment. Here’s a sample .env file.
DATABASE_URL=postgresql+asyncpg://username:password@localhost/dbname
SECRET_KEY=your_secret_key
ALGORITHM=HS256
ACCESS_TOKEN_EXPIRE_MINUTES=30
You can create a secret key using this command in the terminal (MAC)
openssl rand -hex 32
0c123d40865eef256c6394bcb4457ac2953f1a40b83cd48b04a85de3ef821f51
You can look it up on the web for other OS.
NEXT
In app/config.py, load these environment variables using Pydantic's BaseSettings.
Step 4: Alembic Setup
Configure Alembic for database migrations. This allows you to update your database schema over time without losing data. In alembic/env.py, set up the target metadata.
from sqlalchemy import engine_from_config, pool
from logging.config import fileConfig
from alembic import context
from app.models import SQLModel, Base
config = context.config
fileConfig(config.config_file_name)
target_metadata = Base.metadata
def run_migrations_offline():
url = config.get_main_option("sqlalchemy.url")
context.configure(url=url, target_metadata=target_metadata, literal_binds=True)
with context.begin_transaction():
context.run_migrations()
def run_migrations_online():
connectable = engine_from_config(
config.get_section(config.config_ini_section), prefix="sqlalchemy.", poolclass=pool.NullPool)
with connectable.connect() as connection:
context.configure(connection=connection, target_metadata=target_metadata)
with context.begin_transaction():
context.run_migrations()
if context.is_offline_mode():
run_migrations_offline()
else:
run_migrations_online()
Development
Step 1: Database Models
Define your database models in app/models.py. This example includes a simple User model.
from sqlmodel import SQLModel, Field
from typing import Optional
class User(SQLModel, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
email: str
hashed_password: str
is_active: bool = True
Step 2: CRUD Operations
In app/crud.py, create basic CRUD (Create, Read, Update, Delete) operations. These functions interact with the database to perform operations on the User model.
from sqlmodel import Session, select
from app.models import User
from app.schemas import UserCreate
from app.auth import get_password_hash
def get_user_by_email(session: Session, email: str) -> User:
return session.exec(select(User).where(User.email == email)).first()
def create_user(session: Session, user: UserCreate) -> User:
db_user = User(email=user.email, hashed_password=get_password_hash(user.password))
session.add(db_user)
session.commit()
session.refresh(db_user)
return db_user
Step 3: Authentication
Handle authentication logic in app/auth.py. This includes password hashing and creation of JWT tokens.
from passlib.context import CryptContext
from jose import JWTError, jwt
from datetime import datetime, timedelta
from app.config import settings
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
def get_password_hash(password: str) -> str:
return pwd_context.hash(password)
def verify_password(plain_password: str, hashed_password: str) -> bool:
return pwd_context.verify(plain_password, hashed_password)
def create_access_token(data: dict, expires_delta: timedelta = None):
to_encode = data.copy()
expire = datetime.utcnow() + (expires_delta if expires_delta else timedelta(minutes=settings.access_token_expire_minutes))
to_encode.update({"exp": expire})
return jwt.encode(to_encode, settings.secret_key, algorithm=settings.algorithm)
Step 4: Routers
Define the user router in app/routers/users.py. This is where you create routes for user-related operations like registration.
from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.orm import Session
from app.crud import create_user, get_user_by_email
from app.schemas import UserCreate, UserResponse
from app.deps import get_db
router = APIRouter()
@router.post("/", response_model=UserResponse)
def create_new_user(user: UserCreate, db: Session = Depends(get_db)):
db_user = get_user_by_email(db, email=user.email)
if db_user:
raise HTTPException(status_code=400, detail="Email already registered")
return create_user(db, user=user)
Step 5: Main Application
Set up the main application in app/main.py. This file includes the application initialization and route inclusion.
from fastapi import FastAPI
from app.routers import users
from app.database import engine, SQLModel
app = FastAPI()
@app.on_event("startup")
async def on_startup():
async with engine.begin() as conn:
await conn.run_sync(SQLModel.metadata.create_all)
app.include_router(users.router, prefix="/users", tags=["users"])
Step 6: Running the App and Database Migrations
Before running the app, apply the database migrations to create the necessary tables.
alembic revision --autogenerate -m "create users table"
alembic upgrade head
To run the FastAPI application, use Uvicorn asgi server.
uvicorn app.main:app --reload
This command starts the server in development mode, enabling automatic reloads when you change the code.
Step 7: Making API Calls
Once the server is running, you can test the API endpoints. To do this, make requests using tools like curl, Postman, or your browser.
- Register a New User
curl -X POST "http://127.0.0.1:8000/users/" -H "accept: application/json" -H "Content-Type: application/json" -d '{"email": "user@example.com", "password": "password"}'
- Response
{
"id": 1,
"email": "user@example.com",
"is_active": true
}
This is a fundamental example but a solid start for building a secure and scalable authentication system.
Challenges and Solutions
Security Concerns
Challenge: Keeping user data safe and making sure the authentication process is secure is a big challenge. With increasing threats, you need to be extra careful.
Solution: Protect user passwords with a strong password system like bcrypt. It’s one of the best ways to store them securely.
It would help if you also used tokens (JWT) for added protection. Enforcing HTTPS ensures that data travels safely over the Internet, preventing hackers from grabbing sensitive information.
To keep things secure over time, make your tokens expire after a certain period and set up a system to refresh them. Adding rate limiting can also protect against misuse, making it harder for anyone to abuse your system.
Scalability Issues
Challenge: As more users sign up, the number of authentication requests grows. Managing this increase while maintaining fast, smooth access can be challenging.
Solution: To handle more users, you can use asynchronous operations with FastAPI and PostgreSQL. This helps your system respond faster, even when traffic is high.
For more extensive systems, adding load balancing and database replication can help share the load and keep things running smoothly.
Another helpful tip is caching mechanisms can significantly reduce the load on your database, ensuring fast access to frequently used data and keeping your system running efficiently.
Wrapping Up
And there you have it. This guide gives you the essential steps to build a secure Python user authentication system, but remember, proper security is an ongoing process.
Regularly monitoring, testing, and updating your Python authentication system will keep it performing at its best. By following these practices, you’ll create a reliable foundation that grows with your application’s needs.
Want to improve your system? Times TX specializes in secure, scalable software solutions. Contact us today to discuss how we can support your project’s success.