gh_UserManager/docs/architecture.md
bermooda-company 1f995295a6 last changes
2026-10-22 16:58:51 +03:30

8.3 KiB

Architecture

Overview

Identity Platform follows an Adobe-style holding model: the Platform is the central hub/reference, and products (Bermooda, Hamsoo, future apps) are sub-products that connect to it. The Platform owns identity; products own their domain data.

┌──────────────────────────────────────────────────────────────┐
│                    IDENTITY PLATFORM                          │
│  ┌────────────┐  ┌────────────┐  ┌────────────┐              │
│  │  Identity  │  │ Authentication│ │ Organization │           │
│  │  (User)    │  │  (Session)   │  │  (Membership)│           │
│  └──────┬─────┘  └──────┬─────┘  └──────┬─────┘              │
│         └────────────────┴────────────────┘                  │
│            PRIVATE KEY (RS256) — NEVER LEAVES PLATFORM       │
└────────────────────────────┬─────────────────────────────────┘
                             │
              ┌──────────────┼──────────────┐
              ▼              ▼              ▼
        ┌───────────┐  ┌───────────┐  ┌───────────┐
        │  Bermooda │  │  Hamsoo   │  │  Future   │
        │   (ERP)   │  │ (Network) │  │   Apps    │
        └───────────┘  └───────────┘  └───────────┘
              ▲              ▲              ▲
              │  PUBLIC KEY (verify only)  │
              └──────────────┴──────────────┘

Data Ownership Boundary

Data Owner Notes
User ID (UUID) Identity Platform Immutable, globally unique
Email & Phone Identity Platform Verified, used for auth
Password Hash Identity Platform Never exposed
Authentication & Sessions Identity Platform Full lifecycle
Organizations Identity Platform Shared across products
Memberships & Roles Identity Platform Owner/Admin/Member
Applications (OAuth Clients) Identity Platform Per-product credentials
Service Credentials Identity Platform Client credentials flow
Security Events / Audit Log Identity Platform Immutable, queryable
Employee & Payroll Bermooda Domain data
Profile & Resume Hamsoo Domain data
Projects & Listings Product Domain data

Rule: Identity Platform never stores domain data (payroll, resumes, projects). Products never store auth data (passwords, sessions, tokens).

Modular Monolith (Backend)

The Django backend is a modular monolith with 11 apps:

App Responsibility
common Base models, permissions, pagination, rate limiting, exceptions
identity Custom User model (UUID PK), managers, serializers
authentication Login, refresh, logout, MFA-ready, rate limiting
organization Organization CRUD, slug generation
membership Membership model (User↔Org), roles, statuses
application OAuth clients (confidential/public), scopes
session Session tracking, device info, revocation
security SecurityEvent model, audit log
oauth OIDC discovery, JWKS, authorization server stubs
config Settings, URLs, WSGI/ASGI
admin Django admin registrations

API Versioning

All endpoints under /api/v1/. Breaking changes → /api/v2/.

OpenAPI schema via drf-spectacular at:

  • JSON: /api/schema/
  • Swagger UI: /api/docs/
  • ReDoc: /api/redoc/

Key Design Decisions

UUID Primary Keys

  • User PK = UUID (immutable, unguessable)
  • All references use UUID — no integer IDs exposed

RS256 Asymmetric Signing

  • Platform holds private key (signing tokens)
  • Products hold public key (verifying tokens via JWKS)
  • Private key never leaves platform — products cannot forge tokens

Session Model

  • Each login creates a Session record (type: browser/api/device)
  • Refresh token stored hashed; rotatable; revocable
  • Device fingerprinting for detection

Organization Model

  • Single Organization model reusable as company/team/workspace
  • Membership links User→Org with role (owner/admin/member) and status
  • Products query memberships for authorization

Rate Limiting

  • Per-user and per-IP via Django cache (Redis in prod)
  • Configurable windows; brute-force triggers lockout
  • Endpoint-level decorators

Security Events

  • Immutable SecurityEvent model
  • Types: login_success, login_failed, logout, token_refreshed, session_revoked, password_changed, mfa_changed, suspicious_activity
  • Severity: low, medium, high, critical

Web UI (server-rendered)

The account UI is rendered by Django templates — no Node.js toolchain, no separate frontend service, no build step.

Route View Purpose
/ identity.ui_views.home Landing page
/register/ identity.ui_views.register Self-registration (reuses RegisterSerializer)
/account/ identity.ui_views.account Profile summary and active sessions
/account/profile/ identity.ui_views.profile Edit self-service profile fields
/account/security/ identity.ui_views.security Email verification, TOTP MFA, recovery codes, passkeys
/account/organizations/ identity.ui_views.organizations Workspaces the user belongs to + create
/account/organizations/<slug>/ identity.ui_views.organization_detail Members and roles (owners/admins only)
/account/developers/ identity.ui_views.developers OAuth clients; staff can register apps and rotate secrets
/account/password/ identity.ui_views.password_change Change password
/account/reset-password/ identity.ui_views.password_reset Request and confirm reset (?token=)
/p/<token>/ passport.ui_views.public_passport Public share-link passport page
/sso/login/ sso.views.SsoLoginView Central login (session cookie for *.hamsoo.me)
/oauth/consent oauth.views.ConsentView First-time authorization consent
/admin/ Django admin Users, organizations, applications, sessions, events

Templates live next to their apps (apps/*/templates/) and share identity/templates/base.html. Styling is self-contained inline CSS in that base template, so WhiteNoise/collectstatic is not on the critical path for the account pages.

Why no SPA

The platform is an IdP: its pages are credential entry points. Server rendering keeps passwords and tokens out of client-side state, gives CSRF protection by default, and removes a second deployable. Products (Hamsoo, Bermooda) keep their own frontends and talk to the API/OIDC endpoints.

Deployment

Docker Compose (Production-Ready)

services:
  postgres:  postgres:16-alpine
  redis:     redis:7-alpine
  api:       gunicorn (3 workers, serves API + account pages)

Environment Separation

  • .env for secrets (not committed)
  • .env.example as template
  • DJANGO_ENV=production enforces DJANGO_SECRET_KEY

Static Files

  • Django: WhiteNoise (CompressedManifestStaticFilesStorage)
  • Account pages carry their CSS inline; collectstatic is only needed for the Django admin assets.

Scaling Considerations

Component Horizontal Scale
API (Django) Stateless — add workers/containers behind LB
PostgreSQL Read replicas for query-heavy workloads
Redis Cluster mode for cache/sessions
Web UI (Django) Horizontal scale with the API workers

Future: Multi-Region

  • Platform deployable per region with shared user namespace
  • Cross-region session sync via Redis
  • DNS-based routing to nearest region