Tutorial 2 — Multi-Tenant App with Auth
Tutorial 2: App Multitenancy con Auth
Section titled “Tutorial 2: App Multitenancy con Auth”This tutorial builds a multi-tenant SaaS app using three PyGo native packages working together: multitenancy, auth, and cache.
🎯 What You’ll Build
Section titled “🎯 What You’ll Build”A SaaS user management app where:
- Each tenant (company) has isolated data
- Users log in via email + password
- JWT tokens expire after 24 hours
- Roles:
user,admin,superadmin
📁 Project Structure
Section titled “📁 Project Structure”myapp/├── app/│ ├── web/│ │ └── saas.pgo ← Models, handlers, routes│ └── templates/│ └── dashboard.html ← Protected dashboard└── pygo.toml📝 Step 1: Define Models & Initialize Services
Section titled “📝 Step 1: Define Models & Initialize Services”Create app/web/saas.pgo:
# Import native packagesfrom multitenancy import Store as TenantStorefrom auth import Manager as AuthManagerfrom cache import NewMemoryCache
# Initialize services (runs on startup)tenants = TenantStore()auth = AuthManager(jwt_secret="CHANGE_ME_IN_PRODUCTION")cache = NewMemoryCache()
# Register a tenanttenants.register(id="acme", name="Acme Corp", plan="pro")
model User: id: UUID email: Email name: String role: String = "user" tenant_id: UUID created: DateTime
model Company: id: UUID name: String plan: Enum(free, pro, enterprise) created: DateTime
# Detect tenant from subdomain or headerhandler dashboard: dashboard(user_id: UUID) -> Dict: user = User.find(user_id) tenant = tenants.from_request(request) return { "user": user, "tenant": tenant, }
# Auth routeshandler login: login(email: Email, password: String) -> String: user = User.where(email=email).first() token = auth.generate_token(user) auth.create_session(response, user) return redirect("/dashboard")
handler logout: logout() -> String: auth.destroy_session(request, response) return redirect("/")
# Protected route (middleware checks session)route GET /dashboard -> auth.require_auth -> dashboardroute POST /login -> loginroute POST /logout -> auth.require_auth -> logout📝 Step 2: Create Protected Template
Section titled “📝 Step 2: Create Protected Template”Create app/templates/dashboard.html:
<!-- auth.require_auth middleware injects the user --><div x-data="{ user: {{ user | json }} }"> <h1>Welcome, <span x-text="user.name"></span>!</h1> <p>Tenant: <span x-text="tenant.name"></span></p> <p>Plan: <span x-text="tenant.plan"></span></p>
<form @submit.prevent="fetch('/logout', { method: 'POST' })"> <button>Logout</button> </form></div>📝 Step 3: Run and Test
Section titled “📝 Step 3: Run and Test”# Start the apppygo dev
# Login as a usercurl -X POST http://localhost:8080/login \ -H "Content-Type: application/x-www-form-urlencoded" \ -d "email=user@example.com&password=secret"
# Access protected route (needs session cookie)curl -b cookies.txt http://localhost:8080/dashboard🔧 Multi-Tenancy Strategies
Section titled “🔧 Multi-Tenancy Strategies”PyGo supports multiple multi-tenancy strategies — all built into the multitenancy package:
| Strategy | Isolation Level | When to Use |
|---|---|---|
| tenant_id (default) | Shared DB, row-level | Small to medium SaaS |
| schema | Shared DB, separate schemas | Medium SaaS, isolation needed |
| database | Separate DB per tenant | Enterprise, strict isolation |
# Choose strategy in pygo.toml[multitenancy]strategy = "schema" # or "database" or "tenant_id"🛡️ RBAC with Auth
Section titled “🛡️ RBAC with Auth”The auth package provides built-in role checks:
# Only admins can accessroute GET /admin -> auth.require_role("admin") -> admin_panel
# Superadmins bypass all checksroute GET /superadmin -> auth.require_role("superadmin") -> super_panel🌐 Multi-Tenant Routing
Section titled “🌐 Multi-Tenant Routing”Tenant detection happens automatically:
- Subdomain:
acme.myapp.pygo→ tenantacme - Header:
X-Tenant-ID: acme→ tenantacme - Path:
/acme/dashboard→ tenantacme
🚀 Next Steps
Section titled “🚀 Next Steps”- Tutorial 3: Background Jobs with Queue
- Auth API Reference
- pygo-tenancy-enterprise — for multi-database & white-label tenants