285 lines
9.5 KiB
Markdown
285 lines
9.5 KiB
Markdown
# MyAuth Module
|
|
|
|
A reusable, modular authentication system for FastAPI applications with pluggable database backends.
|
|
|
|
## Overview
|
|
|
|
This module provides a complete authentication solution designed to be deployed on internal PyPI servers and reused across multiple projects. It handles user registration, login/logout, token management, email verification, and password reset functionality.
|
|
|
|
## Features
|
|
|
|
### Core Authentication
|
|
- ✅ User registration with email and username
|
|
- ✅ Login/logout with email-based authentication
|
|
- ✅ JWT-based access tokens (30 minutes validity)
|
|
- ✅ Opaque refresh tokens stored in database (7 days validity)
|
|
- ✅ Password hashing with configurable bcrypt rounds
|
|
|
|
### User Management
|
|
- ✅ Email verification with JWT tokens
|
|
- ✅ Password reset with secure random tokens (15 minutes validity)
|
|
- ✅ User roles management (flexible, no predefined roles)
|
|
- ✅ User settings storage (dict field)
|
|
- ✅ Account activation/deactivation
|
|
|
|
### Password Security
|
|
- ✅ Strict password validation (via Pydantic):
|
|
- Minimum 8 characters
|
|
- At least 1 uppercase letter
|
|
- At least 1 lowercase letter
|
|
- At least 1 digit
|
|
- At least 1 special character
|
|
|
|
### Architecture
|
|
- ✅ Abstract base classes for database persistence
|
|
- ✅ Multiple database implementations: MongoDB, SQLite, PostgreSQL
|
|
- ✅ Abstract email service interface with optional SMTP implementation
|
|
- ✅ Custom exceptions with FastAPI integration
|
|
- ✅ Synchronous implementation
|
|
- ✅ Ready-to-use FastAPI router with `/auth` prefix
|
|
|
|
## Project Structure
|
|
|
|
```
|
|
├──src
|
|
│ my_auth/
|
|
│ ├── __init__.py
|
|
│ ├── models/ # Pydantic models
|
|
│ │ ├── user.py # User model with roles and settings
|
|
│ │ ├── token.py # Token models (access, refresh, reset)
|
|
│ │ └── email_verification.py # Email verification models
|
|
│ ├── core/ # Business logic
|
|
│ │ ├── auth.py # Authentication service
|
|
│ │ ├── password.py # Password hashing/verification
|
|
│ │ └── token.py # Token generation/validation
|
|
│ ├── persistence/ # Database abstraction
|
|
│ │ ├── base.py # Abstract base classes
|
|
│ │ ├── mongodb.py # MongoDB implementation
|
|
│ │ ├── sqlite.py # SQLite implementation
|
|
│ │ └── postgresql.py # PostgreSQL implementation
|
|
│ ├── api/ # FastAPI routes
|
|
│ │ └── routes.py # All authentication endpoints
|
|
│ ├── email/ # Email service
|
|
│ │ ├── base.py # Abstract interface
|
|
│ │ └── smtp.py # SMTP implementation (optional)
|
|
│ ├── exceptions.py # Custom exceptions
|
|
│ └── config.py # Configuration classes
|
|
├── tests
|
|
```
|
|
|
|
## User Model
|
|
|
|
```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
|
|
|
|
### Token Types
|
|
The module uses a unified tokens collection with a discriminator field:
|
|
|
|
1. **Access Token (JWT)**: 30 minutes validity, stateless
|
|
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, no DB storage
|
|
|
|
### Token Storage
|
|
All tokens requiring storage (refresh and password reset) are kept in a single `tokens` collection/table with a `token_type` discriminator field.
|
|
|
|
## API Endpoints
|
|
|
|
The module exposes a pre-configured FastAPI router with the following endpoints:
|
|
|
|
```
|
|
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
|
|
```
|
|
|
|
## Installation
|
|
|
|
### Dependencies
|
|
|
|
**Core dependencies:**
|
|
```bash
|
|
pip install fastapi pydantic pydantic-settings python-jose[cryptography] passlib[bcrypt] python-multipart
|
|
```
|
|
|
|
**Database-specific dependencies:**
|
|
- MongoDB: `pip install pymongo`
|
|
- SQLite: Built-in (no additional dependency)
|
|
- PostgreSQL: `pip install psycopg2-binary`
|
|
|
|
**Optional email dependency:**
|
|
- SMTP: `pip install secure-smtplib`
|
|
|
|
## Usage
|
|
|
|
### Basic Setup
|
|
|
|
```python
|
|
from fastapi import FastAPI
|
|
from auth_module import AuthService
|
|
from auth_module.persistence.mongodb import MongoUserRepository, MongoTokenRepository
|
|
from auth_module.api import auth_router
|
|
|
|
# Initialize repositories
|
|
user_repo = MongoUserRepository(connection_string="mongodb://localhost:27017/mydb")
|
|
token_repo = MongoTokenRepository(connection_string="mongodb://localhost:27017/mydb")
|
|
|
|
# Initialize auth service
|
|
auth_service = AuthService(
|
|
user_repository=user_repo,
|
|
token_repository=token_repo,
|
|
jwt_secret="your-secret-key-here",
|
|
access_token_expire_minutes=30,
|
|
refresh_token_expire_days=7,
|
|
password_reset_token_expire_minutes=15,
|
|
password_hash_rounds=12
|
|
)
|
|
|
|
# Create FastAPI app and include auth router
|
|
app = FastAPI()
|
|
app.include_router(auth_router) # Mounts at /auth prefix
|
|
```
|
|
|
|
### Using Different Databases
|
|
|
|
#### SQLite
|
|
```python
|
|
from auth_module.persistence.sqlite import SQLiteUserRepository, SQLiteTokenRepository
|
|
|
|
user_repo = SQLiteUserRepository(db_path="./auth.db")
|
|
token_repo = SQLiteTokenRepository(db_path="./auth.db")
|
|
```
|
|
|
|
#### PostgreSQL
|
|
```python
|
|
from auth_module.persistence.postgresql import PostgreSQLUserRepository, PostgreSQLTokenRepository
|
|
|
|
user_repo = PostgreSQLUserRepository(
|
|
host="localhost",
|
|
port=5432,
|
|
database="mydb",
|
|
user="postgres",
|
|
password="secret"
|
|
)
|
|
token_repo = PostgreSQLTokenRepository(...)
|
|
```
|
|
|
|
### Email Service Configuration
|
|
|
|
```python
|
|
from auth_module.email.smtp import SMTPEmailService
|
|
|
|
email_service = SMTPEmailService(
|
|
host="smtp.gmail.com",
|
|
port=587,
|
|
username="your-email@gmail.com",
|
|
password="your-app-password",
|
|
use_tls=True
|
|
)
|
|
|
|
auth_service = AuthService(
|
|
user_repository=user_repo,
|
|
token_repository=token_repo,
|
|
email_service=email_service, # Optional
|
|
...
|
|
)
|
|
```
|
|
|
|
### Custom Email Service
|
|
|
|
Implement your own email service by extending the abstract base class:
|
|
|
|
```python
|
|
from auth_module.email.base import EmailService
|
|
|
|
class CustomEmailService(EmailService):
|
|
def send_verification_email(self, email: str, token: str) -> None:
|
|
# Your implementation (SendGrid, AWS SES, etc.)
|
|
pass
|
|
|
|
def send_password_reset_email(self, email: str, token: str) -> None:
|
|
# Your implementation
|
|
pass
|
|
```
|
|
|
|
## Error Handling
|
|
|
|
The module uses custom exceptions that are automatically converted to appropriate HTTP responses:
|
|
|
|
- `InvalidCredentialsError` → 401 Unauthorized
|
|
- `UserAlreadyExistsError` → 409 Conflict
|
|
- `UserNotFoundError` → 404 Not Found
|
|
- `InvalidTokenError` → 401 Unauthorized
|
|
- `RevokedTokenError` → 401 Unauthorized
|
|
- `ExpiredTokenError` → 401 Unauthorized
|
|
- `EmailNotVerifiedError` → 403 Forbidden
|
|
- `AccountDisabledError` → 403 Forbidden
|
|
|
|
## Configuration Options
|
|
|
|
```python
|
|
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
|
|
)
|
|
```
|
|
|
|
## Testing
|
|
|
|
The module is fully testable with pytest. Test fixtures are provided for each database implementation.
|
|
|
|
```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
|
|
- Password reset tokens are single-use and expire after 15 minutes
|
|
- Email verification tokens are stateless JWT
|
|
- Rate limiting should be implemented at the application level
|
|
- HTTPS should be enforced by the application
|
|
|
|
## Future Enhancements (Not Included)
|
|
|
|
- Multi-factor authentication (2FA/MFA)
|
|
- Rate limiting on login attempts
|
|
- OAuth2 provider integration
|
|
- Session management (multiple device tracking)
|
|
- Account lockout after failed attempts
|
|
|
|
## License
|
|
|
|
[Your License Here]
|
|
|
|
## Contributing
|
|
|
|
[Your Contributing Guidelines Here] |