Admin Access Architecture
Final Production-Ready Model | March 6, 2026
1. Overview
This is the final, production-ready access control architecture for Easy Rent A Car. It replaces all ad-hoc implementations with a clean, deterministic model.
Core Principle
Two-tier access (Platform Admin vs Company Admin), scope-based routing, zero ambiguity.
2. Final Role Model
Platform Roles
platform_owner- Highest platform privilegeplatform_admin- Standard platform adminplatform_support- Platform support staff
Company Roles
company_owner- Highest company privilegecompany_admin- Standard company adminfleet_manager- Vehicle operationsreservations_manager- Booking managementfinance_manager- Financial operationssupport_agent- Customer support
3. Key Entity Changes
User Entity Schema
{
"platform_role": "platform_owner|platform_admin|platform_support|null",
"company_id": "string (required if company_role set)",
"company_role": "company_owner|company_admin|fleet_manager|...|null",
"status": "active|inactive|pending_activation"
}Rule: User has EITHER platform_role OR company_role, never both.
UserInvitation Entity Schema
{
"scope": "platform|company",
"platform_role": "string (if scope=platform)",
"company_role": "string (if scope=company)",
"company_id": "string (if scope=company)",
"invitation_status": "pending|accepted|expired|cancelled",
"token": "hashed token (64-char hex)",
"token_expires_at": "ISO datetime (30 days)"
}4. Core Auth Functions
// Get user's scope getUserScope(user) → 'platform' | 'company' | null // Platform checks isPlatformAdmin(user) → boolean hasPlatformRole(user, role) → boolean hasPlatformRoleOrHigher(user, minRole) → boolean // Company checks isCompanyAdmin(user) → boolean hasCompanyRole(user, role) → boolean hasCompanyRoleOrHigher(user, minRole) → boolean
5. Login Redirect Rules
- User authenticates via Google OAuth
- System checks user scope:
scope = 'platform'→/admin-super-dashboardscope = 'company'→/admin-dashboardscope = null→/admin-login(denied)
6. Guard Components
AdminGuard
Base authentication check. Ensures user is authenticated + has valid scope.
PlatformAdminGuard
Restricts pages to platform admins only. Check: isPlatformAdmin(user)
CompanyAdminGuard
Restricts pages to company admins. Check: isCompanyAdmin(user) OR (isPlatformAdmin(user) AND impersonatingCompanyId)
7. Sidebar & Branding
Platform Admin Sidebar
- Icon: 🔒 Lock
- Title: "Easy Rent A Car Admin"
- Subtitle: "Platform Admin"
- Background: Dark gray (bg-gray-950)
- Menu: Dashboard, Companies, Users, Tenants, Domains, Feature Flags, Import, Maintenance
Company Admin Sidebar
- Icon: 🚗 Car (or company logo)
- Title: Company name
- Subtitle: "Company Admin"
- Background: Dark blue (bg-slate-900)
- Menu: Dashboard, Vehicles, Groups, Pricing, Policies, Drivers, Reservations, Finance, Payments
8. Impersonation (Secure)
Flow
- Platform admin clicks "Impersonate" on company
- Set
impersonatingCompanyIdin sessionStorage - Show yellow warning banner in sidebar
CompanyAdminGuardallows access- Logout → impersonation ends, audit logged
Security Rules
- No privilege escalation
- Tenant isolation enforced
- Audit trail logged (impersonate_start/end)
- Session-based (ends on logout)
9. Tenant Isolation (Backend Rules)
All backend queries for company-scoped data MUST filter by company_id:
// WRONG - allows access to any company's data
const vehicles = await base44.entities.Vehicle.list();
// RIGHT - only returns vehicles for user's company
const vehicles = await base44.entities.Vehicle.filter({
company_id: user.company_id
});Affected Entities: Vehicle, VehicleGroup, PricingRule, Reservation, DriverProfile, CompanyPolicy, Payment, Payout, Review, etc.
10. Files Changed
✅ Entities
entities/User.json- Updated schemaentities/UserInvitation.json- Updated schema
✅ Auth & Guards
components/admin/AdminAuthContext.jsx- New scope logiccomponents/admin/AdminGuard.jsx- Rewrittencomponents/admin/PlatformAdminGuard.jsx- Rewrittencomponents/admin/CompanyAdminGuard.jsx- Rewritten
✅ Pages & Layout
pages/AdminLogin.jsx- Uses getUserScope()pages/AdminRoot.jsx- Uses getUserScope()pages/AdminFallback.jsx- Uses getUserScope()Layout.js- Removed minRole paramcomponents/admin/AdminSidebar.jsx- Scope-based branding
11. Production Checklist
- ✅ User entity migrated to new role model
- ✅ Existing users migrated or cleanup ready
- ✅ All guards use deterministic scope logic
- ✅ All backend functions enforce tenant isolation
- ✅ All pages use correct guards
- ✅ Error pages show clear role/scope info
- ✅ No deprecated role checks in codebase
- ✅ All pages tested with correct roles
- ✅ Logout clears impersonation session
- ✅ Audit logging for impersonation
- ✅ AdminSidebar shows correct branding
- ✅ Admin domain routing uses new guards
- ✅ Invitation model matches new schema
12. Summary
✅ Production-Ready Architecture
- Clear: Two scopes, predefined roles, zero ambiguity
- Secure: Tenant isolation, audit logging, no escalation
- Maintainable: Centralized logic in AdminAuthContext
- Scalable: Easy to add new roles or pages
- Testable: Deterministic checks, clear errors
- User-friendly: Clear UI indicators, helpful messages