# Authentication & Role-Based Access Control (RBAC) Complete guide to the authentication and authorization system powering the multi-tenant platform. ## Overview The platform uses a JWT-based authentication system combined with role-based access control (RBAC) to secure all interfaces: - **Admin** interface - **Store** dashboard - **Storefront** - **REST API** endpoints ## Authentication System ### Technology Stack - **JWT (JSON Web Tokens)**: Stateless authentication - **bcrypt**: Secure password hashing - **Jose**: JWT encoding/decoding library - **FastAPI Security**: OAuth2 password bearer flow ### Authentication Flow ```mermaid sequenceDiagram participant Client participant API participant AuthManager participant Database Client->>API: POST /api/v1/auth/login
{username, password} API->>AuthManager: authenticate_user() AuthManager->>Database: Query user by username/email Database-->>AuthManager: User record AuthManager->>AuthManager: verify_password() AuthManager-->>API: User object API->>AuthManager: create_access_token() AuthManager-->>API: JWT token API-->>Client: {access_token, token_type, expires_in} Note over Client: Store token Client->>API: GET /api/v1/resource
Authorization: Bearer API->>AuthManager: verify_token() AuthManager->>AuthManager: Decode JWT AuthManager->>Database: Query user by ID Database-->>AuthManager: User object AuthManager-->>API: Current user API->>API: Process request API-->>Client: Resource data ``` ## User Roles The platform uses a **4-value role enum** on the `User` model to distinguish user types: ``` User.role: "super_admin" | "platform_admin" | "merchant_owner" | "store_member" ``` ### Customer Role (Separate Model) **Access**: Public storefront and own account space **Capabilities**: - Browse store storefronts - Place orders - Manage their own account and order history - View order status - Update profile information - Can register directly from storefront frontend **Account Creation**: Self-registration via storefront frontend (email verification required) **Authentication**: Standard JWT authentication (separate `Customer` model, not `User`) ### Merchant Owner (`role="merchant_owner"`) **Access**: Full access to owned store dashboards **Characteristics**: - Has **ALL store permissions** automatically (no role record needed) - Ownership determined by `Merchant.owner_user_id` - `User.is_merchant_owner` property returns `True` - `User.is_store_user` property returns `True` - `User.is_owner_of(store_id)` checks ownership **Capabilities**: - Manage products and inventory - Process orders - View analytics and reports - Configure storefront settings - Manage team members (invite, remove, update roles) - Access store-specific APIs **Account Creation**: Created automatically when admin creates a store ### Store Member (`role="store_member"`) **Access**: Store area based on assigned role permissions **Characteristics**: - Permissions come from `StoreUser.role_id -> Role.permissions` - `User.is_store_user` property returns `True` - Must be invited by merchant owner via email **Capabilities**: Limited based on assigned role (Manager, Staff, Support, Viewer, Marketing, or custom) **Account Creation**: Invited by merchant owner via email **Permissions System**: Team members can have granular permissions for different areas (up to 75 permissions) ### Super Admin (`role="super_admin"`) **Access**: Full platform administration across all platforms **Characteristics**: - `User.is_super_admin` property returns `True` (computed: `role == "super_admin"`) - `User.is_admin` property returns `True` - Can access all platforms without restriction - Cannot access store portal (blocked by middleware) **Capabilities**: - Manage all stores across all platforms - Create/manage store accounts - Access system settings - View all data across the platform - Manage users of all types - Access audit logs - Platform-wide analytics **Account Creation**: Created by existing super admins on the backend ### Platform Admin (`role="platform_admin"`) **Access**: Platform administration scoped to assigned platforms **Characteristics**: - `User.is_platform_admin` property returns `True` (computed: `role == "platform_admin"`) - `User.is_admin` property returns `True` - Scoped to specific platforms via `AdminPlatform` association - Cannot access store portal (blocked by middleware) **Capabilities**: - Manage stores within assigned platforms - Access platform-scoped settings and analytics - View data within assigned platforms **Account Creation**: Created by super admins ## Application Areas & Access Control The platform has three distinct areas with different access requirements: | Area | URL Pattern | Access | Purpose | |------|-------------|--------|---------| | **Admin** | `/admin/*` or `admin.platform.com` | Super admins and platform admins (`is_admin`) | Platform administration and store management | | **Store** | `/store/*` | Merchant owners and store members (`is_store_user`) | Store dashboard and storefront management | | **Storefront** | `/storefront/*`, custom domains, subdomains | Customers and public | Public-facing eCommerce storefront | | **API** | `/api/*` | All authenticated users (role-based) | REST API for all operations | ## Account Registration Flow ### Admin Accounts (Super Admin & Platform Admin) - ❌ Cannot register from frontend - ✅ **Super Admins** (`role="super_admin"`): Created by existing super admins - ✅ **Platform Admins** (`role="platform_admin"`): Created by super admins - Used for: Platform administration ### Store Accounts (Merchant Owner & Store Member) - ❌ Cannot register from frontend - ✅ **Merchant Owners** (`role="merchant_owner"`): Automatically created when admin creates a new store - ✅ **Store Members** (`role="store_member"`): Invited by merchant owner via email invitation - Activation: Upon clicking email verification link ### Customer Accounts - ✅ Can register directly on store storefront - Activation: Upon clicking registration email link - Used for: Shopping and order management ## Role Enforcement Methods The `AuthManager` class provides several methods for role-based access control: ### require_admin() Restricts access to admin users only. **Usage**: ```python from fastapi import Depends from models.database.user import User from middleware.auth import auth_manager @app.get("/admin/dashboard") async def admin_dashboard( current_user: User = Depends(auth_manager.require_admin) ): return {"message": "Admin access"} ``` **Raises**: `AdminRequiredException` if user is not admin ### require_store() Allows access to store users and admins. **Usage**: ```python @app.get("/store/products") async def store_products( current_user: User = Depends(auth_manager.require_store) ): return {"products": [...]} ``` **Raises**: `InsufficientPermissionsException` if user is not store or admin ### require_customer() Allows access to customer users and admins. **Usage**: ```python @app.get("/storefront/orders") async def storefront_orders( current_user: User = Depends(auth_manager.require_customer) ): return {"orders": [...]} ``` **Raises**: `InsufficientPermissionsException` if user is not customer or admin ### require_role() Custom role enforcement for specific roles. This method returns a decorator factory that creates role-checking decorators for any role name. **Method Signature**: ```python def require_role(self, required_role: str) -> Callable ``` **Parameters**: - `required_role` (str): The exact role name required (e.g., `"super_admin"`, `"platform_admin"`, `"merchant_owner"`, `"store_member"`) **Returns**: A decorator function that: 1. Accepts a function as input 2. Returns a wrapper that validates the current user's role 3. Raises `HTTPException(403)` if the role doesn't match **Usage Example**: ```python from fastapi import Depends, APIRouter from middleware.auth import auth_manager from models.database.user import User router = APIRouter() @router.get("/owner-only") @auth_manager.require_role("merchant_owner") async def owner_endpoint(current_user: User): """Only users with role='merchant_owner' can access this.""" return {"message": "Merchant owner access granted"} # Can also be used with other specific roles @router.get("/super-admin-only") @auth_manager.require_role("super_admin") async def super_admin_endpoint(current_user: User): return {"data": "super admin content"} ``` **Error Response**: ```json { "detail": "Required role 'merchant_owner' not found. Current role: 'store_member'" } ``` **Note**: For standard access patterns, prefer using the dedicated methods (`require_admin()`, `require_store()`, `require_customer()`) or the computed properties (`is_admin`, `is_store_user`) as they provide better error handling and custom exceptions. ### create_default_admin_user() Creates a default super admin user if one doesn't already exist. This is typically used during initial application setup or database seeding. **Method Signature**: ```python def create_default_admin_user(self, db: Session) -> User ``` **Parameters**: - `db` (Session): SQLAlchemy database session **Returns**: `User` object (either the existing admin user or the newly created one) **Behavior**: 1. Checks if a user with username "admin" already exists 2. If not found, creates a new super admin user with: - Username: `admin` - Email: `admin@example.com` - Password: `admin123` (hashed with bcrypt) - Role: `super_admin` - Status: Active 3. If found, returns the existing user without modification **Usage Example**: ```python from app.core.database import SessionLocal from middleware.auth import auth_manager # During application startup or database initialization db = SessionLocal() try: admin_user = auth_manager.create_default_admin_user(db) print(f"Admin user ready: {admin_user.username}") finally: db.close() ``` **Security Warning**: ⚠️ The default credentials (`admin` / `admin123`) should be changed immediately after first login in production environments. Consider using environment variables for initial admin credentials: ```python # Example: Custom admin creation with env variables import os def create_admin_from_env(db: Session): admin_username = os.getenv("ADMIN_USERNAME", "admin") admin_password = os.getenv("ADMIN_PASSWORD", "admin123") admin_email = os.getenv("ADMIN_EMAIL", "admin@example.com") # Check if admin exists admin = db.query(User).filter(User.username == admin_username).first() if not admin: admin = User( username=admin_username, email=admin_email, hashed_password=auth_manager.hash_password(admin_password), role="super_admin", is_active=True ) db.add(admin) db.commit() return admin ``` **Typical Use Cases**: - Initial database setup scripts - Application bootstrap/initialization - Development environment setup - Testing fixtures ## JWT Token Structure ### Token Payload ```json { "sub": "123", // User ID (JWT standard claim) "username": "testuser", // Username for display "email": "user@example.com", // User email "role": "merchant_owner", // User role (4-value enum) "exp": 1700000000, // Expiration timestamp (JWT standard) "iat": 1699999000 // Issued at timestamp (JWT standard) } ``` ### Token Configuration | Variable | Description | Default | |----------|-------------|---------| | `JWT_SECRET_KEY` | Secret key for JWT signing | Development key (change in production!) | | `JWT_EXPIRE_MINUTES` | Token expiration time in minutes | 30 | **Environment Configuration**: ```bash # .env JWT_SECRET_KEY=your-super-secret-key-change-in-production JWT_EXPIRE_MINUTES=30 ``` ## Permission Hierarchy ```mermaid graph TD A[Super Admin
role=super_admin] --> B[Full Platform Access] A --> C[All Platforms] AA[Platform Admin
role=platform_admin] --> D2[Scoped Platform Access] AA --> D3[Assigned Platforms Only] D[Merchant Owner
role=merchant_owner] --> E[Store Dashboard] D --> F[Team Management] D --> G[Storefront Settings] D --> H[All Store Permissions - 75] I[Store Member
role=store_member] --> E I --> J[Role-Based Permissions] K[Customer
separate model] --> L[Storefront Access] K --> M[Own Orders] K --> N[Own Profile] ``` **Admin Override**: Admin users (`is_admin`: super admins and platform admins) have access to the admin portal. They cannot access the store portal directly -- these are separate security boundaries enforced by middleware. **Note**: `is_super_admin` is no longer a database column. It is a computed property: `User.role == "super_admin"`. JWT tokens no longer include an `is_super_admin` claim; derive it from the `role` claim instead. ## Security Features ### Password Security **Hashing**: - Algorithm: bcrypt - Automatic salt generation - Configurable work factor **Example**: ```python from middleware.auth import auth_manager # Hash password hashed = auth_manager.hash_password("user_password") # Verify password is_valid = auth_manager.verify_password("user_password", hashed) ``` ### Token Security **Features**: - Signed with secret key (prevents tampering) - Includes expiration time - Stateless (no server-side session storage) - Short-lived (30 minutes default) **Best Practices**: - Use HTTPS in production - Store tokens securely on client - Implement token refresh mechanism - Clear tokens on logout ### Protection Against Common Attacks **SQL Injection**: - ✅ SQLAlchemy ORM with parameterized queries - ✅ Input validation with Pydantic **XSS (Cross-Site Scripting)**: - ✅ Jinja2 auto-escaping - ✅ Content Security Policy headers **CSRF (Cross-Site Request Forgery)**: - ✅ JWT tokens in Authorization header (not cookies) - ✅ SameSite cookie attribute for session cookies **Brute Force**: - ✅ Rate limiting on auth endpoints - ✅ Account lockout after failed attempts (future) ## Authentication Endpoints ### Register User ```http POST /api/v1/auth/register Content-Type: application/json { "email": "user@example.com", "username": "testuser", "password": "securepassword123" } ``` **Response**: ```json { "id": 123, "username": "testuser", "email": "user@example.com", "role": "customer", "is_active": true } ``` ### Login ```http POST /api/v1/auth/login Content-Type: application/json { "username": "testuser", "password": "securepassword123" } ``` **Response**: ```json { "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...", "token_type": "bearer", "expires_in": 1800 } ``` ### Using Authentication Include the JWT token in the Authorization header: ```http GET /api/v1/resource Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9... ``` ## Error Handling ### Authentication Errors | Error | Status Code | Description | |-------|-------------|-------------| | `InvalidCredentialsException` | 401 | Username/password incorrect | | `InvalidTokenException` | 401 | JWT token invalid or malformed | | `TokenExpiredException` | 401 | JWT token has expired | | `UserNotActiveException` | 403 | User account is inactive | ### Authorization Errors | Error | Status Code | Description | |-------|-------------|-------------| | `AdminRequiredException` | 403 | Endpoint requires admin role | | `InsufficientPermissionsException` | 403 | User lacks required permissions | ## Testing Authentication ### Unit Tests ```python import pytest from middleware.auth import AuthManager def test_password_hashing(): auth_manager = AuthManager() password = "test_password" hashed = auth_manager.hash_password(password) assert auth_manager.verify_password(password, hashed) assert not auth_manager.verify_password("wrong_password", hashed) def test_create_token(): auth_manager = AuthManager() user = create_test_user(role="merchant_owner") token_data = auth_manager.create_access_token(user) assert "access_token" in token_data assert "token_type" in token_data assert token_data["token_type"] == "bearer" ``` ### Integration Tests ```python def test_login_flow(client): # Register user response = client.post("/api/v1/auth/register", json={ "username": "testuser", "email": "test@example.com", "password": "password123" }) assert response.status_code == 200 # Login response = client.post("/api/v1/auth/login", json={ "username": "testuser", "password": "password123" }) assert response.status_code == 200 token = response.json()["access_token"] # Access protected endpoint response = client.get( "/api/v1/profile", headers={"Authorization": f"Bearer {token}"} ) assert response.status_code == 200 ``` ## Best Practices ### For Developers 1. **Always hash passwords**: Never store plain text passwords 2. **Use dependency injection**: Leverage FastAPI's `Depends` for auth 3. **Validate tokens**: Always validate tokens on protected endpoints 4. **Check permissions**: Verify user has required role/permissions 5. **Log auth events**: Track login, logout, failed attempts ### For Operations 1. **Strong secret keys**: Use long, random JWT secret keys 2. **HTTPS only**: Never send tokens over HTTP 3. **Token expiration**: Keep token lifetimes short 4. **Rotate secrets**: Periodically rotate JWT secret keys 5. **Monitor auth logs**: Watch for suspicious activity ### For Security 1. **Rate limiting**: Limit auth endpoint requests 2. **Account lockout**: Implement after N failed attempts 3. **Email verification**: Require email confirmation 4. **Password policies**: Enforce strong password requirements 5. **2FA support**: Consider adding two-factor authentication ## Examples ### Protecting an Endpoint ```python from fastapi import Depends, APIRouter from sqlalchemy.orm import Session from middleware.auth import auth_manager from app.core.database import get_db from models.database.user import User router = APIRouter() @router.get("/stores") async def get_stores( current_user: User = Depends(auth_manager.require_admin), db: Session = Depends(get_db) ): """Only admins can list all stores.""" stores = db.query(Store).all() return {"stores": stores} ``` ### Multi-Role Access ```python @router.get("/dashboard") async def dashboard( current_user: User = Depends(auth_manager.get_current_user), db: Session = Depends(get_db) ): """Accessible by all authenticated users, but returns different data.""" if current_user.is_admin: # Admin (super_admin or platform_admin) sees platform data data = get_admin_dashboard(db) elif current_user.is_store_user: # Store user (merchant_owner or store_member) sees their store data data = get_store_dashboard(db, current_user.id) else: # Customer sees their orders data = get_customer_dashboard(db, current_user.id) return data ``` ## Related Documentation - [Middleware Stack](middleware.md) - System-wide request processing - [Error Handling](../api/error-handling.md) - Exception handling - [Backend API Reference](../backend/middleware-reference.md) - Technical AuthManager docs - [Testing Guide](../testing/testing-guide.md) - Testing authentication ## Technical Reference For detailed API documentation of authentication classes and methods: - [AuthManager API Reference](../backend/middleware-reference.md#authentication-authorization)