2025-10-18 22:43:59 +02:00
2025-10-18 22:42:22 +02:00
2025-10-18 22:42:22 +02:00
2025-10-18 22:42:22 +02:00
2025-10-18 12:26:55 +02:00
2025-10-18 22:42:22 +02:00
2025-10-18 12:26:55 +02:00
2025-10-18 22:43:59 +02:00
2025-10-18 22:42:22 +02:00
2025-10-18 22:42:22 +02:00
2025-10-18 22:42:22 +02:00

MyAuth Module

A reusable, modular authentication system for FastAPI applications, designed for deployment on an internal PyPI server.

Overview

MyAuth provides a complete, 'out-of-the-box' authentication solution for your FastAPI projects. It's designed to be installed as a dependency and configured in just a few lines, letting you focus on your business logic instead of user and token management.

This module handles registration, login/logout, email verification, password reset, and token management (JWT & Refresh) with pluggable database backends.

Features

  • Complete Authentication: User registration, email-based login, logout.
  • Token Management:
    • JWT Access Tokens (default: 30 min validity).
    • Opaque Refresh Tokens securely stored in the database (default: 7 days validity).
  • User Lifecycle:
    • Email verification (via JWT token).
    • Secure password reset (via secure token stored in DB).
    • Account activation / deactivation.
  • Security:
    • Password hashing with bcrypt (configurable rounds).
    • Strict password validation (uppercase, lowercase, digit, special character).
  • Flexible Architecture:
    • Pluggable Backends: Supports MongoDB, PostgreSQL, and SQLite out of the box.
    • Abstract Email Service: Use the built-in SMTP implementation or plug in your own (e.g., SendGrid, AWS SES).
  • FastAPI Integration:
    • A ready-to-use APIRouter that exposes all necessary endpoints under the /auth prefix.
    • Automatic handling of custom exceptions into correct HTTP responses.

Installation

Install the base module and choose your "extras" based on your infrastructure.

# Base installation (core logic, no DB or email drivers)
pip install myauth --index-url [http://your-pypiserver.com/](http://your-pypiserver.com/)

# --- Installation with Extras (Recommended) ---

# To use with MongoDB
pip install "myauth[mongodb]" --index-url [http://your-pypiserver.com/](http://your-pypiserver.com/)

# To use with PostgreSQL
pip install "myauth[postgresql]" --index-url [http://your-pypiserver.com/](http://your-pypiserver.com/)

# To use the built-in SMTP email service
pip install "myauth[email]" --index-url [http://your-pypiserver.com/](http://your-pypiserver.com/)

# To combine extras (e.g., PostgreSQL + Email)
pip install "myauth[postgresql,email]" --index-url [http://your-pypiserver.com/](http://your-pypiserver.com/)

Core Dependencies

The core module requires: fastapi, pydantic, pydantic-settings, python-jose[cryptography], passlib[bcrypt], python-multipart

Quick Start

To get started, choose one of the database options below. Each example is complete and self-contained: it sets up the FastAPI app, the authentication service, and the related routes.

Copy-paste the example that matches your infrastructure into your main.py.

Option 1: Quick Start with MongoDB

This example configures myauth to use MongoDB as its backend.


from fastapi import FastAPI
from my_auth import AuthService
from my_auth.api import auth_router
from my_auth.persistence.mongodb import MongoUserRepository, MongoTokenRepository

# 1. Initialize FastAPI app
app = FastAPI()

# 2. Configure repositories for MongoDB
# Make sure your connection string is correct
user_repo = MongoUserRepository(connection_string="mongodb://localhost:27017/myappdb")
token_repo = MongoTokenRepository(connection_string="mongodb://localhost:27017/myappdb")

# 3. Configure the Authentication Service
# IMPORTANT: Change this to a long, random, secret string
auth_service = AuthService(
  user_repository=user_repo,
  token_repository=token_repo,
  jwt_secret="YOUR_SUPER_LONG_AND_SECURE_JWT_SECRET_HERE"
  # email_service will be added in the next step
)

# 4. Include the authentication routes
# Endpoints like /auth/login, /auth/register are now active
app.include_router(auth_router)


@app.get("/")
def read_root():
  return {"message": "Application running with MyAuth (MongoDB)"}

Option 2: Quick Start with PostgreSQL

This example configures myauth to use PostgreSQL as its backend.


from fastapi import FastAPI
from my_auth import AuthService
from my_auth.api import auth_router
from my_auth.persistence.postgresql import PostgreSQLUserRepository, PostgreSQLTokenRepository

# 1. Initialize FastAPI app
app = FastAPI()

# 2. Configure repositories for PostgreSQL
# Update with your database credentials
db_config = {
    "host": "localhost",
    "port": 5432,
    "database": "mydb",
    "user": "postgres",
    "password": "secret"
}
user_repo = PostgreSQLUserRepository(**db_config)
token_repo = PostgreSQLTokenRepository(**db_config)

# 3. Configure the Authentication Service
# IMPORTANT: Change this to a long, random, secret string
auth_service = AuthService(
  user_repository=user_repo,
  token_repository=token_repo,
  jwt_secret="YOUR_SUPER_LONG_AND_SECURE_JWT_SECRET_HERE"
  # email_service will be added in the next step
)

# 4. Include the authentication routes
# Endpoints like /auth/login, /auth/register are now active
app.include_router(auth_router)


@app.get("/")
def read_root():
  return {"message": "Application running with MyAuth (PostgreSQL)"}

Option 3: Quick Start with SQLite

This example configures myauth to use SQLite, which is ideal for development or small applications.


from fastapi import FastAPI
from my_auth import AuthService
from my_auth.api import auth_router
from my_auth.persistence.sqlite import SQLiteUserRepository, SQLiteTokenRepository

# 1. Initialize FastAPI app
app = FastAPI()

# 2. Configure repositories for SQLite
# This will create/use a file named 'auth.db' in the current directory
db_path = "./auth.db"
user_repo = SQLiteUserRepository(db_path=db_path)
token_repo = SQLiteTokenRepository(db_path=db_path)

# 3. Configure the Authentication Service
# IMPORTANT: Change this to a long, random, secret string
auth_service = AuthService(
  user_repository=user_repo,
  token_repository=token_repo,
  jwt_secret="YOUR_SUPER_LONG_AND_SECURE_JWT_SECRET_HERE"
  # email_service will be added in the next step
)

# 4. Include the authentication routes
# Endpoints like /auth/login, /auth/register are now active
app.include_router(auth_router)


@app.get("/")
def read_root():
  return {"message": "Application running with MyAuth (SQLite)"}

Next Step: Configure the Email Service

For email verification (/auth/verify-email) and password resets (/auth/password-reset) to work, you must provide an email service to the AuthService.

Simply modify the AuthService initialization from the Quick Start step you chose.

Option 1: Use the Built-in SMTP Service

This is the simplest option if you have an SMTP server (like Gmail, SendGrid SMTP, etc.). Remember to install the extra: pip install "myauth[email]"


# ... (keep your app and repository config from the Quick Start)

from my_auth.email.smtp import SMTPEmailService

# 1. Configure the email service
email_service = SMTPEmailService(
  host="smtp.gmail.com",
  port=587,
  username="your-email@gmail.com",
  password="your-app-password",  # Use an 'App Password' for Gmail
  use_tls=True
)

# 2. Pass the email service to AuthService
auth_service = AuthService(
  user_repository=user_repo,  # From Quick Start
  token_repository=token_repo,  # From Quick Start
  email_service=email_service,  # Add this line
  jwt_secret="YOUR_SUPER_LONG_AND_SECURE_JWT_SECRET_HERE"
)

# ... (keep 'app.include_router(auth_router)')

Option 2: Create a Custom Email Service

If you use a third-party service (like AWS SES, Mailgun) that requires an API, you can implement your own.


# ... (keep your app and repository config from the Quick Start)

from my_auth.email.base import EmailService


# 1. Implement your custom email service
class CustomEmailService(EmailService):
  def __init__(self, api_key: str):
    # self.api_key = api_key
    # self.client = ThirdPartyClient(api_key=api_key)
    print("Custom Email Service Initialized")
  
  def send_verification_email(self, email: str, token: str) -> None:
    # Your custom logic to send an email via an API
    # verification_link = f"http://localhost:8000/verify?token={token}"
    print(f"Sending VERIFICATION to {email} with token {token}")
    pass
  
  def send_password_reset_email(self, email: str, token: str) -> None:
    # Your custom logic to send an email via an API
    # reset_link = f"http://localhost:8000/reset-password?token={token}"
    print(f"Sending PASSWORD RESET to {email} with token {token}")
    pass


# 2. Initialize your custom service
email_service = CustomEmailService(api_key="YOUR_API_KEY_HERE")

# 3. Pass your custom service to AuthService
auth_service = AuthService(
  user_repository=user_repo,  # From Quick Start
  token_repository=token_repo,  # From Quick Start
  email_service=email_service,  # Add this line
  jwt_secret="YOUR_SUPER_LONG_AND_SECURE_JWT_SECRET_HERE"
)

# ... (keep 'app.include_router(auth_router)')

API Endpoints Reference

The auth_router exposes the following endpoints under the /auth prefix:

POST   /auth/register                # User registration
POST   /auth/login                   # Login (email + password)
POST   /auth/logout                  # Logout (revokes refresh token)
POST   /auth/refresh                 # Refresh access token
POST   /auth/password-reset-request  # Request password reset
POST   /auth/password-reset          # Reset password with token
POST   /auth/verify-email-request    # Request email verification
POST   /auth/verify-email            # Verify email with token
GET    /auth/me                      # Get current user info

Error Handling

The module uses custom exceptions that are automatically converted to the appropriate HTTP responses by FastAPI:

  • InvalidCredentialsError401 Unauthorized
  • UserAlreadyExistsError409 Conflict
  • UserNotFoundError404 Not Found
  • InvalidTokenError401 Unauthorized
  • RevokedTokenError401 Unauthorized
  • ExpiredTokenError401 Unauthorized
  • EmailNotVerifiedError403 Forbidden (on login attempt)
  • AccountDisabledError403 Forbidden (on login attempt)

Configuration Options

All options are passed during the AuthService initialization:


AuthService(
  user_repository: UserRepository,  # Required
token_repository: TokenRepository,  # Required
jwt_secret: str,  # Required
jwt_algorithm: str = "HS256",  # Optional
access_token_expire_minutes: int = 30,  # Optional
refresh_token_expire_days: int = 7,  # Optional
password_reset_token_expire_minutes: int = 15,  # Optional
password_hash_rounds: int = 12,  # Optional (bcrypt cost)
email_service: EmailService = None  # Optional
)

Appendix (Contributor & Development Details)

Appendix A: Project Structure (src/my_auth)
my_auth/
├── __init__.py
├── models/                    # Pydantic models (User, Token...)
│   ├── user.py
│   ├── token.py
│   └── email_verification.py
├── core/                      # Business logic (Auth, Password, Token services)
│   ├── auth.py
│   ├── password.py
│   └── token.py
├── persistence/               # Database abstraction
│   ├── base.py               # Abstract base classes
│   ├── mongodb.py
│   ├── sqlite.py
│   └── postgresql.py
├── api/                       # FastAPI routes
│   └── routes.py
├── email/                     # Email service
│   ├── base.py               # Abstract interface
│   └── smtp.py
├── exceptions.py              # Custom HTTP exceptions
└── config.py                  # Configuration classes (if any)
Appendix B: Internal Data Models

User Model

This is the internal Pydantic model used by the service.


class User:
  id: str  # Unique identifier
  email: str  # Unique, required
  username: str  # Required, non-unique
  hashed_password: str  # Bcrypt hashed
  roles: list[str]  # Free-form roles, no defaults
  user_settings: dict  # Custom user settings
  is_verified: bool  # Email verification status
  is_active: bool  # Account active status
  created_at: datetime
  updated_at: datetime

Token Management

The module uses a unified tokens collection/table with a token_type discriminator field for all persistent tokens.

  1. Access Token (JWT): 30 minutes validity, stateless, not stored in DB.
  2. Refresh Token (Opaque): 7 days validity, stored in DB.
  3. Password Reset Token (Random): 15 minutes validity, stored in DB.
  4. Email Verification Token (JWT): Stateless, not stored in DB.
Appendix C: Testing & Security

Testing

The module is testable with pytest.


pytest tests/

Security Considerations

  • Passwords are hashed using bcrypt with configurable rounds.
  • JWT tokens are signed with HS256 (configurable).
  • Refresh tokens are opaque and stored securely in the database.
  • Password reset tokens are single-use, opaque, and expire quickly (15 min).
  • Rate Limiting is not included and should be implemented at the application level (e.g., using slowapi).
  • HTTPS should be enforced by the production web server (e.g., Nginx, Traefik).

License

MIT

Description
No description provided
Readme 217 KiB
Languages
Python 100%