101 Guide for Python User Authentication System

January 6, 2025

python-user-authentication-system

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.


web developmentSoftware
author

TimesTX

Times TX is a digital transformation leader empowering SMBs and consultants with innovative tech solutions. We publish actionable insights, trends, and strategies to inspire growth and excellence in the digital age.