# 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. ```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 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. ```Python 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. ```Python 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]" ```Python # ... (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. ```Python # ... (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: * `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)** ## Configuration Options All options are passed during the `AuthService` initialization: ```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 ) ``` ## 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. ```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.
Appendix C: Testing & Security ### 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).
## License MIT