ADR-0001: Authentication and Authorization Architecture
Status
Accepted - 2025-10-24
Context
TVL Platform requires secure authentication and authorization for a multi-tenant SaaS application with the following requirements:
Business Requirements
- Property managers, owners, and staff need differentiated access
- Multiple users per organization with role-based permissions
- Property-level access delegation (manager can access only assigned properties)
- Audit trail of all access and actions
- Support for API consumers (web UI, future mobile apps, integrations)
Technical Requirements
- Secure authentication without implementing custom password management
- Token-based auth suitable for stateless APIs
- Row-Level Security (RLS) for tenant isolation in Postgres
- Performance: auth checks < 50ms per request
- Compliance: GDPR, SOC 2 Type II readiness
Constraints
- MVP timeline: 3 months
- Small team (2-3 engineers)
- Preference for managed services over custom implementation
- Must integrate with Supabase (Postgres provider)
Decision
We will implement a hybrid authentication and authorization architecture:
Authentication (AuthN)
Google OIDC via Supabase Auth for user authentication
Rationale:
- Supabase Auth provides managed OIDC integration (no custom password logic)
- Google OIDC is familiar to business users (high adoption)
- JWT tokens suitable for stateless API architecture
- Built-in token refresh, session management
- Reduces security surface area (Google handles credentials)
Implementation:
// Authentication flow
1. User clicks "Sign in with Google"
2. Redirect to Google OAuth consent
3. Google returns authorization code
4. Exchange code for ID token (Supabase Auth)
5. Verify ID token and create/update user record
6. Issue JWT with claims: { userId, organizationId, role }
7. Client stores JWT in localStorage (web) or secure storage (mobile)
Authorization (AuthZ)
Role-Based Access Control (RBAC) with Property-Level Delegation
Roles:
- Owner - Full access to organization and owned properties
- Manager - Full access to assigned properties only
- Staff - Limited write access to assigned properties
- Finance - Read-only financial data across organization
- Admin - TVL internal super-admin (cross-tenant access)
Permission Model:
- Permissions stored in database (permissionstable)
- Role-permission mappings in role_permissionstable
- User-role assignments in org_memberstable with optional property scope
- Property-level assignments in property_assignmentstable
Authorization Checks:
// Request flow
1. Extract JWT from Authorization header
2. Validate JWT signature and expiration
3. Extract userId, organizationId from claims
4. Check if user belongs to organization
5. Check user's role for organization
6. If property-specific operation, check property assignment
7. Enforce permission requirements for endpoint
Tenant Isolation
Row-Level Security (RLS) in Postgres/Supabase
Rationale:
- Defense-in-depth: even if application logic fails, RLS prevents cross-tenant access
- Supabase makes RLS easy to implement
- No performance penalty if properly indexed
RLS Policies:
-- Example: properties table
CREATE POLICY "Users can only access properties in their org"
  ON properties
  FOR ALL
  USING (organization_id = current_setting('app.current_org_id')::uuid);
-- Set org context at start of transaction
SET LOCAL app.current_org_id = '<orgId from JWT>';
Alternatives Considered
Alternative 1: Custom Username/Password Auth
Rejected
Pros:
- Full control over auth flow
- No dependency on external provider
Cons:
- High security risk (password storage, hashing, salting, breach response)
- Must implement password reset, 2FA, account recovery
- Compliance burden (password policies, breach notification)
- Significant development time (2-4 weeks)
Decision: Leverage Google's security infrastructure instead
Alternative 2: Auth0 or AWS Cognito
Rejected (for MVP, reconsidered for future)
Pros:
- Enterprise-grade features (SSO, MFA, advanced RBAC)
- Comprehensive identity management
- Better for complex auth scenarios
Cons:
- Additional cost ($240-$1200/year for MVP scale)
- Complexity for simple use case
- Another external dependency
- Supabase Auth is already included
Decision: Use Supabase Auth for MVP; migrate to Auth0 if enterprise SSO required
Alternative 3: Attribute-Based Access Control (ABAC)
Rejected (for MVP, possible future enhancement)
Pros:
- Fine-grained permissions (e.g., "can edit properties with < 10 units")
- More flexible than RBAC
- Better for complex authorization scenarios
Cons:
- Significantly more complex to implement and debug
- Performance impact (rule evaluation on every request)
- Harder for non-technical users to understand
- Overkill for MVP requirements
Decision: Start with RBAC; add ABAC-style rules if customer demand warrants
Alternative 4: API Keys for Authentication
Rejected
Pros:
- Simpler than OAuth for programmatic access
- No token refresh logic
Cons:
- Not suitable for human users (no expiration, hard to revoke)
- Poor UX (users must manage keys)
- Security risk (long-lived credentials)
Decision: JWT for human users; API keys may be added for machine-to-machine in future
Consequences
Positive
- 
Security - No password management burden
- Google handles credential security
- Short-lived JWT tokens (24h expiration)
- RLS provides defense-in-depth
 
- 
Developer Experience - Supabase Auth SDK simplifies implementation
- JWT standard widely supported
- Clear RBAC model easy to reason about
 
- 
User Experience - Familiar "Sign in with Google" flow
- No password to remember
- Fast authentication (< 2s)
 
- 
Compliance - GDPR: User can delete Google account
- SOC 2: Leverages Google's certifications
- Audit trail via RLS and application logs
 
- 
Cost - Supabase Auth included in plan (no extra cost)
- No Auth0/Cognito fees
 
Negative
- 
Vendor Lock-in - Dependent on Google OIDC (if Google changes policy, major impact)
- Dependent on Supabase Auth (migration effort if switching providers)
- Mitigation: OAuth/OIDC is standard; can migrate to another provider with effort
 
- 
Limited Social Login - Only Google OIDC for MVP (no Facebook, Apple, Microsoft)
- Mitigation: Add more providers if customer demand; Supabase supports multiple
 
- 
RBAC Rigidity - Fixed roles may not fit all customer workflows
- Mitigation: Document customization path; consider ABAC post-MVP
 
- 
JWT Statelessness Trade-off - Cannot revoke JWT before expiration (24h window)
- Mitigation: Keep expiration short; implement token blacklist if needed
 
- 
Property-Level Permissions Complexity - Junction table for property assignments adds query complexity
- Performance impact if not properly indexed
- Mitigation: Add composite indexes on (user_id, organization_id, property_id)
 
Implementation Plan
Phase 1: Authentication (Week 1-2)
- Integrate Supabase Auth with Google OIDC
- Implement JWT validation middleware
- Create user registration/login endpoints
- Add session management (token refresh)
Phase 2: Basic Authorization (Week 3-4)
- Create roles,permissions,role_permissionstables
- Seed predefined roles and permissions
- Implement RBAC middleware
- Add user-role assignment endpoints
Phase 3: Property-Level Delegation (Week 5-6)
- Create property_assignmentstable
- Implement property-level auth checks
- Add property assignment management UI
- Test delegation workflows
Phase 4: RLS (Week 7-8)
- Enable RLS on all tenant-scoped tables
- Create RLS policies
- Update application to set org context
- Test cross-tenant isolation
Phase 5: Audit & Security Hardening (Week 9-10)
- Add audit logging for all auth events
- Implement rate limiting on auth endpoints
- Security audit and penetration testing
- Documentation and runbooks
Validation Checklist
- User can sign in with Google
- JWT tokens issued with correct claims
- Token expiration enforced (24h)
- Role-based permissions block unauthorized actions
- Property-level delegation works correctly
- RLS prevents cross-tenant data access
- Audit logs capture auth events
- Rate limiting prevents brute-force attacks
- Performance: auth checks < 50ms (p95)
- Security: No secrets in client-side code
- Compliance: GDPR data export includes auth logs
References
- Google OIDC Documentation
- Supabase Auth Documentation
- JWT Best Practices (RFC 8725)
- OWASP Authentication Cheat Sheet
- NIST Digital Identity Guidelines (SP 800-63B)
Appendix: Permission Matrix
| Role | Create Property | Edit Property | Delete Property | View Bookings | Manage Payments | Manage Users | 
|---|---|---|---|---|---|---|
| Owner | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | 
| Manager | ✅ (assigned) | ✅ (assigned) | ❌ | ✅ (assigned) | ❌ | ❌ | 
| Staff | ❌ | ✅ (limited) | ❌ | ✅ (assigned) | ❌ | ❌ | 
| Finance | ❌ | ❌ | ❌ | ✅ (read-only) | ✅ (read-only) | ❌ | 
| Admin | ✅ (all orgs) | ✅ (all orgs) | ✅ (all orgs) | ✅ (all orgs) | ✅ (all orgs) | ✅ (all orgs) | 
Sources
- meta/research-log.md
- docs/02-domains/identity-access/spec.md
- docs/00-overview/platform-overview.md