Multi-tenant Laravel application dengan API key-based tenant identification. Setiap tenant memiliki database terpisah untuk isolasi data yang sempurna.
- ✅ Database Isolation - Setiap tenant memiliki database terpisah
- ✅ API Key Authentication - Identifikasi tenant via
X-Tenant-API-Keyheader - ✅ Dynamic Database Configuration - Konfigurasi database per-tenant (host, port, username, password)
- ✅ Encrypted Credentials - Password database dienkripsi menggunakan Laravel encryption
- ✅ Laravel Sanctum - Token-based authentication untuk API
- ✅ Role-Based Access Control - Support role admin/user
- ✅ Tenant Token Validation - Mencegah cross-tenant token usage
- ✅ Custom Error Messages - Pesan error yang informatif
- ✅ Automated Backup - Scheduled daily & weekly backup
- ✅ Manual Backup - Via Artisan command atau REST API
- ✅ Compression Support - Gzip compression untuk menghemat storage
- ✅ Auto Cleanup - Hapus backup lama otomatis
- ✅ Restore Capability - Restore database dari backup
- ✅ Tenant Management API - CRUD tenant via REST API
- ✅ Health Check - Monitor koneksi database tenant
- ✅ Backup Statistics - Monitor status backup semua tenant
┌─────────────────────┐
│ Central Database │
│ │
│ - tenants │
│ - domains │
├─────────────────────┤
│ │
│ Tenant Metadata & │
│ API Keys Storage │
└─────────────────────┘
│
│ Manages
▼
┌──────────────────────────────────────┐
│ Tenant Databases │
├─────────────┬─────────────┬──────────┤
│ Tenant A │ Tenant B │ Tenant C │
│ │ │ │
│ - users │ - users │ - users │
│ - products │ - products │ - products│
│ - orders │ - orders │ - orders │
│ - tokens │ - tokens │ - tokens │
└─────────────┴─────────────┴──────────┘
┌──────────────┐
│ Client │
└──────┬───────┘
│ X-Tenant-API-Key: tk_abc123...
│ Authorization: Bearer token...
▼
┌──────────────────────────────────────┐
│ Middleware: InitializeTenancyByApiKey│
│ 1. Validasi API Key │
│ 2. Find Tenant │
│ 3. Initialize Tenancy │
│ 4. Set Database Connection │
└──────┬───────────────────────────────┘
│
▼
┌──────────────────────────────────────┐
│ Middleware: auth:sanctum │
│ 1. Verify Bearer Token │
│ 2. Load User dari Tenant Database │
└──────┬───────────────────────────────┘
│
▼
┌──────────────────────────────────────┐
│ Middleware: ValidateTenantToken │
│ 1. Pastikan token dari tenant yang │
│ sama dengan API key │
└──────┬───────────────────────────────┘
│
▼
┌──────────────────────────────────────┐
│ Controller Action │
│ Data dari Tenant Database │
└───────────────────────────────────────┘
- PHP 8.4+
- Composer
- MySQL 8.0+
- Laravel Herd (atau PHP development server lainnya)
-
Clone Repository
git clone https://github.com/JunedSetiawan/laravel-multi-tenant-try.git cd laravel-multi-tenant-try -
Install Dependencies
composer install
-
Environment Configuration
cp .env.example .env php artisan key:generate
-
Database Configuration (
.env)# Central Database DB_CONNECTION=mysql DB_HOST=127.0.0.1 DB_PORT=3306 DB_DATABASE=central_db DB_USERNAME=root DB_PASSWORD=root # Tenant Database Template TENANT_DB_HOST=127.0.0.1 TENANT_DB_PORT=3306 TENANT_DB_USERNAME=root TENANT_DB_PASSWORD=root # Master API Key untuk Management MASTER_API_KEY=your-super-secret-master-key # Backup Email Alert (optional) BACKUP_ALERT_EMAIL=[email protected]
-
Run Migrations
# Migrate central database php artisan migrate # Seed super admin (optional) php artisan db:seed --class=SuperAdminSeeder
-
Start Development Server
# Jika menggunakan Laravel Herd herd link # Atau gunakan artisan serve php artisan serve
Endpoint: POST /api/central/tenants
Headers:
X-Master-API-Key: your-master-key
Accept: application/json
Body:
{
"name": "Warung Makan Bu Joko",
"db_name": "kasir_waroenk",
"db_host": "localhost",
"db_port": 3306,
"db_username": "root",
"db_password": "root"
}Response:
{
"message": "Tenant created successfully",
"data": {
"tenant_id": "warung-makan-bu-joko-abc123",
"name": "Warung Makan Bu Joko",
"api_key": "tk_0123456789abcdef...",
"database": "kasir_waroenk"
},
"warning": "Save the API Key securely! It cannot be retrieved again."
}api_key dengan aman! API key ini digunakan untuk akses tenant.
Base URL: http://localhost/api/central
Required Header: X-Master-API-Key: your-master-key
| Method | Endpoint | Deskripsi |
|---|---|---|
| POST | /tenants |
Create tenant baru |
| GET | /tenants |
List semua tenant |
| GET | /tenants/{id} |
Detail tenant |
| PUT | /tenants/{id} |
Update tenant |
| DELETE | /tenants/{id} |
Delete tenant |
| POST | /tenants/{id}/regenerate-key |
Regenerate API key |
| GET | /tenants/{id}/health |
Health check |
| POST | /tenants/{id}/backup |
Backup database |
| GET | /tenants/{id}/backups |
List backups |
| POST | /tenants/{id}/restore |
Restore database |
Base URL: http://localhost/api
Required Headers:
X-Tenant-API-Key: tk_your_tenant_key
Accept: application/json
| Method | Endpoint | Deskripsi |
|---|---|---|
| POST | /register |
Register user baru |
| POST | /login |
Login user |
| GET | /info |
Info tenant saat ini |
Register Example:
POST /api/register
Headers:
X-Tenant-API-Key: tk_abc123...
Accept: application/json
Body:
{
"name": "John Doe",
"email": "[email protected]",
"username": "johndoe",
"password": "password123",
"password_confirmation": "password123",
"role": "user"
}Login Example:
POST /api/login
Headers:
X-Tenant-API-Key: tk_abc123...
Accept: application/json
Body:
{
"email": "[email protected]",
"password": "password123"
}
Response:
{
"message": "Login successful",
"user": { ... },
"token": "11|abc123xyz..."
}Additional Header: Authorization: Bearer {token}
| Method | Endpoint | Deskripsi |
|---|---|---|
| GET | /me |
Get user profile |
| POST | /logout |
Logout user |
| GET | /products |
List products |
| POST | /products |
Create product |
| GET | /products/{id} |
Show product |
| PUT | /products/{id} |
Update product |
| DELETE | /products/{id} |
Delete product |
| GET | /users |
List users (admin only) |
Example Request:
GET /api/me
Headers:
X-Tenant-API-Key: tk_abc123...
Authorization: Bearer 11|abc123xyz...
Accept: application/json┌─────────┐
│ Admin │
└────┬────┘
│
│ POST /api/central/tenants
│ (name, db_name, db_host, db_username, db_password)
▼
┌──────────────────────────────────────┐
│ Create Tenant in Central DB │
│ │
│ 1. Save tenant metadata │
│ 2. Encrypt db_password │
│ 3. Generate & hash API Key │
│ 4. Store db config (host, port, │
│ username, encrypted password) │
└──────────┬───────────────────────────┘
│
│ Database tenant (db_name) HARUS SUDAH ADA
│ dengan migrations yang sudah di-run
│
│ Response: tenant_id, api_key (plaintext)
▼
┌──────────────────────────────────────┐
│ Admin receives API Key │
│ ⚠️ SAVE API KEY - shown only once! │
│ │
│ Admin gives API Key to Tenant Owner │
└──────────────────────────────────────┘
│
│ Tenant Owner can now use API Key
│ to access their database
▼
┌──────────────────────────────────────┐
│ Tenant users can register/login │
│ using X-Tenant-API-Key header │
└──────────────────────────────────────┘
- Database dengan nama
db_nameHARUS SUDAH DIBUAT sebelumnya - Migrations HARUS SUDAH DI-RUN di database tenant tersebut
- Sistem TIDAK otomatis membuat database atau run migrations
- Sistem hanya menyimpan konfigurasi koneksi ke database yang sudah ada
┌──────────┐
│ User │
└────┬─────┘
│
│ POST /api/register
│ Headers: X-Tenant-API-Key
│ Body: name, email, password
▼
┌──────────────────────┐
│ Register Process │
│ │
│ 1. Validate Tenant │
│ 2. Create User in │
│ Tenant Database │
└──────────┬───────────┘
│
│ Success Response
▼
┌──────────────────────┐
│ POST /api/login │
│ Body: email, pass │
└──────────┬───────────┘
│
▼
┌──────────────────────┐
│ Login Process │
│ │
│ 1. Find User │
│ 2. Verify Password │
│ 3. Generate Token │
│ 4. Save to Tenant DB│
└──────────┬───────────┘
│
│ Response: user, token
▼
┌──────────────────────┐
│ User can now access │
│ protected resources │
└──────────────────────┘
┌────────────────┐
│ User Request │
│ with Headers │
└───────┬────────┘
│
▼
┌─────────────────────┐
│ Has X-Tenant-API- │ NO
│ Key header? ├────► Error: API Key Required
└─────────┬───────────┘
│ YES
▼
┌─────────────────────┐
│ Find Tenant by │ NO
│ API Key ├────► Error: Invalid API Key
└─────────┬───────────┘
│ YES
▼
┌─────────────────────┐
│ Initialize Tenancy │
│ - Set DB Connection │
│ - Load Tenant Data │
└─────────┬───────────┘
│
▼
┌─────────────────────┐
│ Has Authorization │ NO
│ Bearer header? ├────► Error: Unauthenticated
└─────────┬───────────┘
│ YES
▼
┌─────────────────────┐
│ Find Token in │ NO
│ Tenant Database ├────► Error: Invalid Token
└─────────┬───────────┘
│ YES
▼
┌─────────────────────┐
│ Validate Token │ NO
│ belongs to Tenant ├────► Error: Token Mismatch
└─────────┬───────────┘
│ YES
▼
┌─────────────────────┐
│ Check Role/ │ NO
│ Permissions ├────► Error: Forbidden
└─────────┬───────────┘
│ YES
▼
┌─────────────────────┐
│ ✅ Access Granted │
│ Execute Controller │
│ Return Response │
└─────────────────────┘
- Layer 1: Tenant API Key - Identifikasi tenant
- Layer 2: Bearer Token - Autentikasi user
- Layer 3: Token Validation - Validasi token belongs to tenant
- Layer 4: Role-Based Access - Authorization based on role
- Setiap tenant memiliki database terpisah
- Tidak ada data sharing antar tenant
- Token dari Tenant A tidak bisa akses Tenant B
- Password database tenant dienkripsi dengan Laravel encryption
- API key di-hash dengan SHA-256
- User password di-hash dengan bcrypt
Sistem memberikan pesan error yang jelas:
{
"error": "Invalid Tenant API Key",
"message": "The provided API key does not match any tenant...",
"hint": "Make sure you are using the correct API key...",
"provided_key": "tk_abc123..."
}# Backup semua tenant
php artisan tenant:backup --compress
# Backup tenant tertentu
php artisan tenant:backup tenant-id-123 --compress
# Custom retention
php artisan tenant:backup --keep-days=60Backup otomatis sudah dikonfigurasi di app/Console/Kernel.php:
- Daily Backup: Setiap hari jam 02:00 WIB (retention 30 hari)
- Weekly Backup: Setiap Minggu jam 03:00 WIB (retention 90 hari)
Tambahkan cron job:
* * * * * cd /path-to-project && php artisan schedule:run >> /dev/null 2>&1# List semua backups
php artisan tenant:backups
# List backup tenant tertentu
php artisan tenant:backups tenant-id-123# Restore dari backup terbaru
php artisan tenant:restore tenant-id-123
# Restore dari file tertentu
php artisan tenant:restore tenant-id-123 backups/tenant-id/2025/10/backup.sql.gz# Create backup
POST /api/central/tenants/{id}/backup
Headers: X-Master-API-Key: your-key
Body: { "compress": true }
# List backups
GET /api/central/tenants/{id}/backups
# Download backup
GET /api/central/tenants/{id}/backups/download?file=backup.sql.gz
# Restore
POST /api/central/tenants/{id}/restore
Body: { "file": "backup.sql.gz" }# Create tenant
curl -X POST http://localhost/api/central/tenants \
-H "X-Master-API-Key: your-master-key" \
-H "Accept: application/json" \
-d '{"name":"Test Tenant","db_name":"test_db"}'# Register
curl -X POST http://localhost/api/register \
-H "X-Tenant-API-Key: tk_abc123..." \
-H "Accept: application/json" \
-d '{"name":"John","email":"[email protected]","password":"secret"}'
# Login
curl -X POST http://localhost/api/login \
-H "X-Tenant-API-Key: tk_abc123..." \
-H "Accept: application/json" \
-d '{"email":"[email protected]","password":"secret"}'
# Access protected route
curl -X GET http://localhost/api/me \
-H "X-Tenant-API-Key: tk_abc123..." \
-H "Authorization: Bearer 11|token..." \
-H "Accept: application/json"- Simpan tenant API key dengan aman
- Jangan share API key antar tenant
- Regenerate API key jika terjadi kebocoran
- Gunakan database terpisah untuk setiap tenant
- Backup rutin (automated + manual)
- Monitor disk space untuk backup
- Gunakan HTTPS di production
- Rate limiting untuk prevent abuse
- Monitor suspicious activities
- Update dependencies secara berkala
- Health check rutin untuk semua tenant
- Monitor backup status
- Log analysis untuk debug
Contributions are welcome! Please feel free to submit a Pull Request.
This project is open-sourced software licensed under the MIT license.
Juned Setiawan
- GitHub: @JunedSetiawan
Jika ada pertanyaan atau issue, silakan buat GitHub Issue.
Built with ❤️ using Laravel & Stancl Tenancy