gh_UserManager/docs/MIGRATION.md
bermooda-company 54d5891edf user
2026-08-23 23:59:14 +03:30

234 lines
7.2 KiB
Markdown

# Migration Guide
## Overview
This document describes how products (Bermooda, Hamsoo, new products) connect to the Identity Platform, and how the platform evolved from a product-coupled UserManager to an independent identity service.
## From UserManager → Central Identity Platform
### Phase 1: Architecture Audit (DONE)
The audit confirmed:
1. **No global roles on User**: The `identity.User` model has NO `employer`/`job_seeker` fields. Roles are contextual via `Membership.role` (owner/admin/member).
2. **No product-domain models in identity layer**: No `Workspace`, `BusinessProfile`, `Employee`, `Payroll`, `Project`, `Resume` tables in the identity database.
3. **No product-specific fields in JWT**: The JWT contains only `user_id`, `user_status`, `identity_key`, and optional `product_key`/`organization_id` (context, not domain).
4. **Organization is generic**: `identity.Organization` has no Bermooda-specific fields (like industry, hiring settings).
The project was **already architected correctly** — it was built as an independent platform rather than a product-coupled service.
### Phase 2-5: Domain Separation (ALREADY DONE)
All product-specific concepts were **never tightly coupled** — the platform was designed with clear boundaries from the start. No destructive changes are needed.
### Phase 6: JWT Payload (DONE)
JWT contains only identity claims:
- Always: `user_id`, `user_status`, `identity_key`, `exp`, `iat`, `iss`, `jti`
- Optional context: `product_key`, `organization_id` (if context established at login)
No product-specific roles or permissions are embedded.
### Phase 7-10: Versioning, Contracts, Security, Docs (IN PROGRESS)
- API versioning: `/v1/` namespace added (parallel to `/api/v1/`)
- Product model added to identity layer
- Active Context support in JWT
- This documentation
## How a Product Integrates
### Step 1: Register Product
Products register themselves with the Identity Platform:
```bash
POST /v1/products/
Authorization: Bearer <admin_token>
Content-Type: application/json
{
"key": "bermooda",
"name": "Bermooda",
"description": "ERP platform for teams"
}
```
Response: `{ "id": "uuid", "key": "bermooda", "name": "Bermooda", ... }`
### Step 2: Register Application (OAuth2 Client)
Products register OAuth2 clients:
```bash
POST /v1/applications/
Authorization: Bearer <admin_token>
Content-Type: application/json
{
"product_key": "bermooda",
"name": "Bermooda Web",
"redirect_uris": ["https://bermooda.example.com/callback"],
"grant_types": ["authorization_code", "refresh_token"],
"response_types": ["code"],
"scopes": ["openid", "profile", "email", "org:read"]
}
```
Response: `{ "client_id": "...", "client_secret": "..." }` (secret shown once)
### Step 3: Authenticate Users
Products redirect users to the Identity Platform login:
```
https://id.example.com/login?product_key=bermooda&organization_id=<uuid>
```
After login, the user is redirected back with a code. The product exchanges the code for tokens:
```bash
POST /v1/auth/token
Content-Type: application/json
{
"grant_type": "authorization_code",
"code": "auth_code_from_platform",
"client_id": "bermooda-client-id",
"client_secret": "bermooda-client-secret",
"redirect_uri": "https://bermooda.example.com/callback",
"code_verifier": "..."
}
```
Response: `{ "access_token": "...", "refresh_token": "...", ... }`
### Step 4: Verify JWTs
Products verify access tokens using the platform's public key:
```python
import jwt
import requests
jwks = requests.get("https://id.example.com/.well-known/jwks.json").json()
# Cache JWKS, extract public key by kid
decoded = jwt.decode(
token,
public_key,
algorithms=["RS256"],
audience="bermooda-client-id",
issuer="https://id.example.com",
)
# decoded = {"user_id": "uuid", "user_status": "active", "identity_key": "usr_...", ...}
```
**Important:** The JWT `sub`/`user_id` is the SAME UUID across all products. Products use this UUID as their FK to identity.User — never creating their own user record.
### Step 5: Resolve User Context
Products resolve the user's identity and context:
```bash
GET /v1/identity/
Authorization: Bearer <access_token>
```
Response:
```json
{
"user": {
"id": "uuid",
"email": "user@example.com",
"phone": "+989123456789",
"status": "active",
"email_verified": true
},
"memberships": [
{ "organization": "org-uuid", "role": "admin", "status": "active" }
],
"organizations": [
{ "id": "org-uuid", "name": "ACME", "slug": "acme" }
],
"products": [
{ "key": "bermooda", "name": "Bermooda" }
]
}
```
The product then maps `organization_id` (identity) to its own concept:
- Bermooda: `Workspace.objects.get(org=org_uuid)`
- Hamsoo: `BusinessProfile.objects.get(user=user_uuid)`
### Step 6: Subscribe to Events
Products subscribe to identity events via Redis Streams:
```python
import redis
r = redis.Redis(host="redis", port=6379, db=0)
# Create consumer group (one per product service)
r.xgroup_create("events.user", "bermooda-sync", mkstream=True)
# Consume events
while True:
events = r.xread(
{"events.user": "$"},
count=10,
block=1000,
)
for stream, messages in events:
for msg_id, msg in messages:
process_event(msg)
r.xack("events.user", "bermooda-sync", msg_id)
```
## Backward Compatibility
The platform maintains backward compatibility:
- **Old endpoints** (`/api/v1/...`) remain functional — they are NOT removed
- **New endpoints** (`/v1/...`) mirror the same functionality with improved naming
- **Old tokens** remain valid until expiry
- **Existing users** retain their UUIDs — no data migration needed
| Old Path | New Path | Status |
|----------|----------|--------|
| `/api/v1/auth/login` | `/v1/auth/login` | Both work |
| `/api/v1/users/` | `/v1/users/` | Both work |
| `/api/v1/organizations/` | `/v1/organizations/` | Both work |
| `/api/v1/applications/` | `/v1/applications/` | Both work |
| `/api/v1/sessions/` | `/v1/sessions/` | Both work |
| `/api/v1/security/events/` | `/v1/security/events/` | Both work |
| (NEW) | `/v1/products/` | New — product catalog |
| (NEW) | `/v1/health/` | New — health check |
## Migration Checklist
- [x] Architecture audit complete
- [x] No product-domain models in identity layer
- [x] No global roles on User model
- [x] JWT contains only identity claims
- [x] Organization is generic (no product-specific fields)
- [x] Product model added to identity layer
- [ ] Add Active Context to admin dashboard
- [ ] Migrate product integrations to new endpoints (gradual)
- [ ] Deprecation timeline for old endpoints (6+ months)
- [ ] Full test coverage of migration scenarios
## Gotchas
1. **Do NOT recreate users in products**: Use the identity User UUID as FK
2. **Do NOT store passwords/tokens in products**: Delegate to Identity Platform
3. **Do NOT embed product roles in JWT**: Read from Membership API
4. **Do NOT hardcode product names**: Use `product_key` from Product registration
5. **Do NOT assume user data is complete**: Products extend with their own profiles
6. **UUIDs are immutable**: Never change a user's UUID; use `identity_key` for external references
## Support
- **API Issues**: Internal ticket system
- **Security**: `security@identity-platform.internal`
- **Documentation**: `/api/docs/` (Swagger UI)