349 lines
12 KiB
Markdown
349 lines
12 KiB
Markdown
# 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).
|
|
* **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.
|
|
|
|
```bash
|
|
# 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.
|
|
|
|
```Python
|
|
|
|
from fastapi import FastAPI
|
|
from myauth import create_app_router_for_mongoDB
|
|
|
|
# 1. Initialize FastAPI app
|
|
app = FastAPI()
|
|
|
|
# 2. Configure repositories for MongoDB
|
|
auth_router = create_app_router_for_mongoDB(mongodb_url="mongodb://localhost:27017",
|
|
jwt_secret="THIS_NEEDS_TO_BE_CHANGED")
|
|
|
|
# 3. Include the authentication routes
|
|
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.
|
|
|
|
```Python
|
|
|
|
from fastapi import FastAPI
|
|
from myauth import create_app_router_for_postgreSQL
|
|
|
|
# 1. Initialize FastAPI app
|
|
app = FastAPI()
|
|
|
|
# 2. Configure repositories for MongoDB
|
|
auth_router = create_app_router_for_mongoDB(postgresql_url="mongodb://localhost:27017",
|
|
username="admin",
|
|
password="password",
|
|
jwt_secret="THIS_NEEDS_TO_BE_CHANGED")
|
|
|
|
# 3. Include the authentication routes
|
|
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.
|
|
|
|
```Python
|
|
|
|
from fastapi import FastAPI
|
|
from myauth import create_app_router_for_sqlite
|
|
|
|
# 1. Initialize FastAPI app
|
|
app = FastAPI()
|
|
|
|
# 2. Configure repositories for MongoDB
|
|
auth_router = create_app_router_for_sqlite(db_path="./UserDB", jwt_secret="THIS_NEEDS_TO_BE_CHANGED")
|
|
|
|
# 3. Include the authentication routes
|
|
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]"
|
|
|
|
```Python
|
|
|
|
from fastapi import FastAPI
|
|
from myauth.emailing.smtp import SMTPEmailService
|
|
from myauth import create_app_router_for_sqlite
|
|
|
|
# 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_router = create_app_router_for_sqlite(db_path="./UserDB", jwt_secret="THIS_NEEDS_TO_BE_CHANGED",
|
|
email_service=email_service)
|
|
|
|
# 4. Include the authentication routes
|
|
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.
|
|
|
|
```Python
|
|
|
|
from fastapi import FastAPI
|
|
from myauth.emailing.base import EmailService
|
|
from myauth import create_app_router_for_sqlite
|
|
|
|
# 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_router = create_app_router_for_sqlite(db_path="./UserDB", jwt_secret="THIS_NEEDS_TO_BE_CHANGED",
|
|
email_service=email_service)
|
|
|
|
# 4. Include the authentication routes
|
|
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:
|
|
|
|
* `InvalidCredentialsError` → **401 Unauthorized**
|
|
* `UserAlreadyExistsError` → **409 Conflict**
|
|
* `UserNotFoundError` → **404 Not Found**
|
|
* `InvalidTokenError` → **401 Unauthorized**
|
|
* `RevokedTokenError` → **401 Unauthorized**
|
|
* `ExpiredTokenError` → **401 Unauthorized**
|
|
* `EmailNotVerifiedError` → **403 Forbidden (on login attempt)**
|
|
* `AccountDisabledError` → **403 Forbidden (on login attempt)**
|
|
|
|
## Appendix (Contributor & Development Details)
|
|
|
|
<details> <summary><b> Appendix A: Project Structure (src/my_auth)</b></summary>
|
|
|
|
```
|
|
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)
|
|
```
|
|
|
|
</details>
|
|
|
|
<details> <summary><b> Appendix B: Internal Data Models </b></summary>
|
|
|
|
### User Model
|
|
|
|
This is the internal Pydantic model used by the service.
|
|
|
|
```Python
|
|
|
|
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.
|
|
1. Refresh Token (Opaque): 7 days validity, stored in DB.
|
|
1. Password Reset Token (Random): 15 minutes validity, stored in DB.
|
|
1. Email Verification Token (JWT): Stateless, not stored in DB.
|
|
|
|
</details>
|
|
|
|
|
|
<details> <summary><b> Appendix C: Testing & Security </b></summary>
|
|
|
|
### Testing
|
|
|
|
The module is testable with `pytest`.
|
|
|
|
```Bash
|
|
|
|
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).
|
|
|
|
</details>
|
|
|
|
## License
|
|
|
|
MIT
|