ADR-001: Keycloak vs Auth0 for Authentication
Status: ✅ Accepted Date: 2024-08 Deciders: Development Team, CTO Related: ADR-002, ADR-004
Context
Noumaris is a healthcare SaaS platform handling sensitive clinical data. We need a robust authentication and authorization system that:
- Supports HIPAA compliance requirements
- Provides JWT-based authentication for frontend and backend
- Enables role-based access control (RBAC)
- Supports multi-tenancy (multiple institutions)
- Allows customization of login/registration flows
- Has reasonable costs for a bootstrapped startup
Decision
Selected: Keycloak (self-hosted, open-source)
We will use Keycloak as our identity and access management (IAM) solution, deployed on Google Cloud Run alongside our other services.
Rationale
Alternatives Considered
Option 1: Auth0 (SaaS)
Pros:
- Fully managed service (no maintenance)
- Great documentation and DX
- Quick setup
- Built-in security features
Cons:
- Cost: $228/month (Essentials plan for 500 MAU) → $1,000+/month as we scale
- Vendor lock-in: Difficult to migrate away
- HIPAA compliance: Requires Business Associate Agreement (BAA) - only on Enterprise plan ($$$)
- Data residency: Limited control over where data is stored
- Customization limits: Restricted template customization
Verdict: ❌ Rejected - Too expensive for MVP, HIPAA compliance requires enterprise pricing
Option 2: Supabase Auth
Pros:
- Open-source
- PostgreSQL-based (familiar)
- Great DX with JavaScript SDK
- Affordable pricing
Cons:
- Less mature than Keycloak or Auth0
- Fewer enterprise features
- Limited RBAC capabilities
- Self-hosting required for HIPAA compliance
- Smaller community and ecosystem
Verdict: ❌ Rejected - Not mature enough for healthcare use case
Option 3: AWS Cognito
Pros:
- Fully managed AWS service
- Integrates with AWS ecosystem
- HIPAA compliant (with BAA)
- Reasonable pricing
Cons:
- AWS vendor lock-in
- Complex configuration
- Limited customization of UI
- Not as flexible for custom workflows
- We're using Google Cloud, not AWS
Verdict: ❌ Rejected - Cloud provider mismatch, limited customization
Option 4: Keycloak (SELECTED)
Pros:
- ✅ Open-source and free
- ✅ Self-hosted - Full control over data and infrastructure
- ✅ HIPAA-ready - Can deploy in compliant manner
- ✅ Mature and battle-tested - Used by major enterprises
- ✅ Full customization - Custom themes, login flows, extensions
- ✅ Standard protocols - OAuth2, OpenID Connect, SAML
- ✅ Multi-tenancy support - Realms for different institutions
- ✅ Cost-effective - Only infrastructure costs (~$50-100/month on Cloud Run)
- ✅ Active community - Large ecosystem, good documentation
Cons:
- Requires hosting and maintenance effort
- Steeper learning curve than Auth0
- Need to manage updates and security patches
- More complex initial setup
Verdict: ✅ SELECTED
Consequences
Positive
- Cost Savings: $300/month saved vs Auth0 ($228/month Auth0 vs ~$50/month Cloud Run)
- HIPAA Compliance: Full control enables BAA compliance without enterprise pricing
- Flexibility: Can customize every aspect of auth flow
- Data Sovereignty: Patient data stays in our infrastructure
- No Vendor Lock-in: Can migrate or self-host anywhere
- Scalability: Horizontal scaling with Cloud Run
Negative
- Operational Overhead: Need to monitor, update, and maintain Keycloak
- Initial Setup Time: More complex than Auth0
- DevOps Complexity: Managing Docker images, deployments, backups
- Security Responsibility: We're responsible for security patches
Mitigations
- Automation: Use Terraform for infrastructure as code (ADR-004)
- Monitoring: Set up health checks, alerts, logging
- Documentation: Comprehensive setup guides for team
- Backup Strategy: Automated database backups to Google Cloud Storage
- Security: Subscribe to Keycloak security mailing list, automated updates
Implementation Notes
Deployment Architecture
┌─────────────────────────────────────┐
│ Frontend (React) │
│ - Keycloak-js for login │
│ - JWT token in localStorage │
└─────────────────────────────────────┘
↓
┌─────────────────────────────────────┐
│ Keycloak (Cloud Run) │
│ - Port: 8080 (8081 local) │
│ - Realm: noumaris │
│ - PostgreSQL backend │
└─────────────────────────────────────┘
↓
┌─────────────────────────────────────┐
│ Backend API (FastAPI) │
│ - Validates JWT with public key │
│ - Extracts roles from token │
└─────────────────────────────────────┘Configuration Approach
- Local Development: Docker Compose with auto-import realm configuration
- Production: Terraform-managed configuration (ADR-004)
- Themes: Custom React-based theme using Keycloakify (ADR-002)
Roles & Permissions
- Realm Roles: superadmin, institution_admin, resident, user
- JWT Claims:
realm_access.rolesarray - Backend Validation: Decorators (
@require_superadmin,@require_institution_admin)