12 KiB
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
argon2. - Strict password validation (uppercase, lowercase, digit, special character).
- Password hashing with
- 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
APIRouterthat exposes all necessary endpoints under the/authprefix. - Automatic handling of custom exceptions into correct HTTP responses.
- A ready-to-use
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 myauth import create_auth_app_for_mongodb
# 1. Initialize FastAPI app
app = FastAPI()
# 2. Configure repositories for MongoDB
auth_app = create_auth_app_for_mongodb(mongodb_url="mongodb://localhost:27017",
jwt_secret="THIS_NEEDS_TO_BE_CHANGED")
# 3. Include the authentication routes
app.mount("/auth", auth_app)
@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 myauth import create_auth_app_for_postgresql
# 1. Initialize FastAPI app
app = FastAPI()
# 2. Configure repositories for PostgreSQL
auth_app = create_auth_app_for_postgresql(postgresql_url="mongodb://localhost:27017",
username="admin",
password="password",
jwt_secret="THIS_NEEDS_TO_BE_CHANGED")
# 3. Include the authentication routes
app.mount("/auth", auth_app)
@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 myauth import create_auth_app_for_sqlite
# 1. Initialize FastAPI app
app = FastAPI()
# 2. Configure repositories for MongoDB
auth_app = create_auth_app_for_sqlite(db_path="./UserDB", jwt_secret="THIS_NEEDS_TO_BE_CHANGED")
# 3. Include the authentication routes
app.mount("/auth", auth_app)
@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]"
from fastapi import FastAPI
from myauth import create_auth_app_for_sqlite
from myauth.emailing.smtp import SMTPEmailService
# 1. Initialize FastAPI app
app = FastAPI()
# 2. 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
)
# 3. Configure repositories for MongoDB
auth_app = create_auth_app_for_sqlite(db_path="./UserDB", jwt_secret="THIS_NEEDS_TO_BE_CHANGED",
email_service=email_service)
# 4. Include the authentication routes
app.mount("/auth", auth_app)
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.
from fastapi import FastAPI
from myauth import create_auth_app_for_sqlite
from myauth.emailing.base import EmailService
# 1. Initialize FastAPI app
app = FastAPI()
# 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_app = create_auth_app_for_sqlite(db_path="./UserDB", jwt_secret="THIS_NEEDS_TO_BE_CHANGED",
email_service=email_service)
# 4. Include the authentication routes
app.mount("/auth", auth_app)
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:
InvalidCredentialsError→ 401 UnauthorizedUserAlreadyExistsError→ 409 ConflictUserNotFoundError→ 404 Not FoundInvalidTokenError→ 401 UnauthorizedRevokedTokenError→ 401 UnauthorizedExpiredTokenError→ 401 UnauthorizedEmailNotVerifiedError→ 403 Forbidden (on login attempt)AccountDisabledError→ 403 Forbidden (on login attempt)
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.
- Access Token (JWT): 30 minutes validity, stateless, not stored in DB.
- Refresh Token (Opaque): 7 days validity, stored in DB.
- Password Reset Token (Random): 15 minutes validity, stored in DB.
- 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