feat: replace token-based impersonation with enterprise-grade header-based system
Replaces the insecure token-in-localStorage approach with a header-based impersonation system backed by cache sessions and MFA verification. Key changes: - New impersonation_sessions audit table (immutable, ULID PK) - MFA verification required to start impersonation (TOTP/email/backup) - X-Impersonate-User header + HandleImpersonation middleware - Per-request auth context swap (admin session never modified) - IP pinning, sensitive route blocking, no nesting, sliding 60-min TTL - Activity log auto-tagged with impersonated_by during sessions - Frontend: sessionStorage, BroadcastChannel sync, countdown timer - ImpersonateDialog with reason + MFA verification flow - 26 comprehensive tests covering core, middleware, audit, lifecycle Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -95,7 +95,7 @@ Route::post('portal/token-auth', [PortalTokenController::class, 'auth'])->middle
|
||||
|
||||
// Platform Admin routes
|
||||
Route::prefix('admin')
|
||||
->middleware(['auth:sanctum', 'role:super_admin'])
|
||||
->middleware(['auth:sanctum', 'impersonation', 'role:super_admin'])
|
||||
->name('admin.')
|
||||
->group(function () {
|
||||
// Organisations
|
||||
@@ -115,12 +115,14 @@ Route::prefix('admin')
|
||||
// Activity log
|
||||
Route::get('activity-log', [AdminActivityLogController::class, 'index']);
|
||||
|
||||
// Impersonation (start)
|
||||
// Impersonation — specific routes before wildcard
|
||||
Route::get('impersonate/status', [AdminImpersonationController::class, 'status']);
|
||||
Route::post('impersonate/send-mfa-code', [AdminImpersonationController::class, 'sendMfaCode']);
|
||||
Route::post('impersonate/{user}', [AdminImpersonationController::class, 'start']);
|
||||
});
|
||||
|
||||
// Protected routes
|
||||
Route::middleware('auth:sanctum')->group(function () {
|
||||
Route::middleware(['auth:sanctum', 'impersonation'])->group(function () {
|
||||
// Impersonation (stop — accessible by impersonated user, not just super_admin)
|
||||
Route::post('admin/stop-impersonation', [AdminImpersonationController::class, 'stop']);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user