User Identity Domain Architecture
User Identity Domain Architecture
Key Takeaways
- Keycloak 26.3.2 is the identity hub for all 35+ production services. Two custom SPIs: Magic Link (passwordless email/SMS via Twilio) and Session Restrictor. Any migration must preserve these.
- Gen 2 is a full superset of Gen 1 — no feature gaps found in celebrity or fan services. Gen 1 endpoints are preserved for backward compatibility.
- Single-tenant deployment model — Keycloak realm and all services are configured per-tenant (fanfuzenil). No database-level tenant isolation. Each brand requires separate deployment.
- CIB Seven Keycloak plugin EOL — Camunda 7 CE ends October 2025. The plugin enabling Keycloak→Camunda identity sync needs a replacement strategy.
- All user IDs are Keycloak UUIDs — celebrity and fan profile tables use Keycloak UUIDs as primary keys. No local identity generation.
Migration Decision Question
What is the authentication/authorization migration path?
Migration Verdict
Verdict: Upgrade Complexity: L Key Constraint: Magic Link SPI (custom Keycloak extension) must be preserved or reimplemented. CIB Seven plugin needs replacement. Dependencies: All 35+ services depend on Keycloak for JWT validation. Keycloak migration affects everything.
Services Inventory
| Service | Gen | Deployed? | Stack | Purpose |
|---|---|---|---|---|
| identityx-26 | 2 | Yes (4/4 tenants) | Keycloak 26.3.2 | OAuth2/OIDC identity provider |
| identityx-25 | 2 | Yes (1/4 — agilenetwork) | Keycloak 25.x | Legacy Keycloak instance |
| users | 2 | Yes (4/4) | Java 21 / SB 3.5.4 | Keycloak admin API bridge |
| celebrity | 2 | Yes (4/4) | Java 21 / SB 3.5.4 | Expert profile management |
| fan | 2 | Yes (4/4) | Java 21 / SB 3.5.4 | Fan profile + following |
| cibseven-keycloak | 2 | Via BPM engine | Plugin | Camunda↔︎Keycloak identity sync |
Not in Production
| Service | Status | Notes |
|---|---|---|
| peeq-celebrity (Gen 1) | Replaced | Gen 2 celebrity is superset |
| peeq-fan (Gen 1) | Replaced | Gen 2 fan is functionally identical |
| peeq-keycloak | Archived | Replaced by keycloak repo |
API Surface
Users Service (GraphQL — 15+ operations)
User Management: updateKeycloakUser, whoAmI, searchUsers Magic Link Auth: magicEmailLogin, magicSmsCode, magicSmsLogin Role Management: createRole, deleteRole, addRoleToUser, removeRoleFromUser Group Management: createGroup, deleteGroup, addUserToGroup, removeUserFromGroup Verification: verifyEmail, verifyPhone
Celebrity Service (GraphQL — 27 operations + 2 REST)
Mutations (5): createCelebrity, updateCelebProfile, createShortLink, setStoreCode, toggleCelebrityProfileDelete Queries (12): queryCelebrityProfiles, celebrity (deprecated), celebrities (deprecated), celebritiesPaged (deprecated), plus count/analytics queries Referral Codes (8): CRUD for referral codes with usage tracking REST (2): Profile/event social share pages with Open Graph metadata
Fan Service (GraphQL — 21 operations + 1 REST)
Follow System (7): followCelebrity, unFollowCelebrity, followed, followedCount, doIFollowCelebs, follower counts Index Page (4): CRUD for homepage elements (admin) Media (4): Save/remove media, getFanMedia, adminGetMedia Preferences (2): getGlobalPreferences, setGlobalPreference MailChimp (1): addUserToFanMailingList REST (1): Index page rendering
Who Calls This Domain? Who Does It Call?
graph LR
subgraph "Identity Domain"
KC[Keycloak 26.3]
US[Users Service]
CEL[Celebrity]
FAN[Fan]
end
subgraph "Callers"
FE1[peeq-mono]
FE2[frontends]
BPM[CIB Seven BPM]
end
subgraph "Called Services"
TAGS[Tags]
INV[Inventory]
TRACK[Tracking]
SSE[SSE]
MC[MailChimp]
TW[Twilio SMS]
end
FE1 -->|GraphQL| CEL & FAN & US
FE2 -->|GraphQL| CEL & FAN & US
FE1 & FE2 -->|OAuth2| KC
BPM -->|ReadOnly IP| KC
CEL -->|GraphQL| TAGS & INV
CEL -->|REST| TRACK
CEL -->|Keycloak Admin| KC
FAN -->|Keycloak Admin| KC
FAN -->|REST| MC
US -->|Admin API| KC
KC -->|SMS| TW
CEL -.->|RabbitMQ| SSE
FAN -.->|RabbitMQ| SSE
US -.->|RabbitMQ| CEL & FAN
API Backward Compatibility Constraints
- GraphQL schema changes would break both peeq-mono and frontends simultaneously
- Gen 2 preserves all Gen 1 query/mutation names (deprecated but functional) for backward compatibility
- Keycloak realm config changes (client IDs, roles, groups) affect all services
- JWT token format is shared — any issuer URL or claim changes break all 35+ services
- Magic Link SPI — custom token format and SMS flow are proprietary
Data Model
Celebrity Profile Schema (22 Flyway migrations)
erDiagram
celebrity_profiles {
uuid celebrity_user_id PK
date created_on
string bio
string display_name
string first_name
string last_name
string metadata
string status
string store_code
int display_weight
enum celeb_profile_type
string company_name
string title
string profile_video
boolean is_deleted
date deleted_on
uuid deleted_by_id
}
celebrity_profile_image {
uuid id PK
uuid celebrity_user_id FK
string url
date created_on
string meta_data
}
celebrity_profile_banner {
uuid id PK
uuid celebrity_user_id FK
string url
date created_on
}
referral_codes {
uuid id PK
string code UK
string type
int limit
int used
uuid assigned_to
}
celebrity_profile_shortlinks {
uuid id PK
uuid celebrity_user_id FK
string name
string body
}
profile_status_history {
uuid id PK
uuid celebrity_user_id FK
string status
date occurred_on
}
celebrity_profiles ||--o{ celebrity_profile_image : has
celebrity_profiles ||--o{ celebrity_profile_banner : has
celebrity_profiles ||--o{ celebrity_profile_shortlinks : has
celebrity_profiles ||--o{ profile_status_history : tracks
celebrity_profiles }o--o| referral_codes : uses
Fan Profile Schema (11 Flyway migrations)
erDiagram
fan_profiles {
uuid fan_user_id PK
date created_on
string metadata
boolean fan_profile_exclusively
}
fan_follows {
string id PK "MD5(fan+celeb)"
uuid fan_user_id
uuid celeb_user_id
date followed_on
string followed_metadata
date unfollowed_on
}
fan_follow_changes {
uuid id PK
date occurred_on
uuid fan_user_id
uuid celeb_user_id
string change
}
fan_media {
uuid id PK
uuid fan FK
uuid media_id
}
global_preferences {
uuid id PK
enum type
text preference_value
}
fan_profiles ||--o{ fan_follows : follows
fan_profiles ||--o{ fan_follow_changes : audit
fan_profiles ||--o{ fan_media : saves
Data Migration Specifics
- Schema transformation: Minimal — Gen 2 schemas are additive over Gen 1 (nullable new columns)
- Foreign key dependencies: Both profiles FK to Keycloak UUIDs (external dependency)
- Data volume: Unknown (no DB access). Celebrity has 22 migrations suggesting active evolution. Fan has 11 suggesting simpler schema.
- Stateful data: Keycloak sessions, magic link tokens (Redis-backed, 24h TTL), OAuth2 refresh tokens
In-Flight State
- Keycloak sessions: Active user sessions must be preserved or gracefully expired during migration
- Magic link tokens: Redis-backed with 24h validity — need to drain or accept brief auth interruption
- OAuth2 refresh tokens: Stored in Keycloak DB — realm export/import preserves these
- RabbitMQ: Users service publishes role/group lifecycle events consumed by celebrity and fan services. Queue drain needed during migration.
- CIB Seven session cache: Camunda identity provider caches user lookups — invalidation needed
External Integrations
| Integration | Service | Purpose | Migration Impact |
|---|---|---|---|
| Twilio | Keycloak (Magic Link SPI) | SMS code delivery | API key migration |
| MailChimp | Fan | Email list management | API key + audience ID |
| Google Cloud Storage | Celebrity | Profile images/banners | Bucket access |
| Celebrity | Open Graph social sharing | App ID config | |
| Tracking Service | Celebrity | Short link generation | URL config |
| Intercom | Users | Customer messaging | Config migration |
Inter-Service Communication
Synchronous (REST/GraphQL)
| From | To | Protocol | Purpose |
|---|---|---|---|
| Celebrity | Tags | GraphQL | Tag lookup for search |
| Celebrity | Inventory | GraphQL | Event details for share pages |
| Celebrity | Tracking | REST | Short link creation |
| Celebrity | Keycloak | Admin API | User profile sync |
| Fan | Keycloak | Admin API | User info retrieval |
| Users | Keycloak | Admin API | All user/role/group operations |
Asynchronous (RabbitMQ)
| Publisher | Message | Consumer | Exchange |
|---|---|---|---|
| Celebrity | CelebrityProfileCreated | ? | celebrity |
| Celebrity | CelebrityProfileUpdated | ? | celebrity |
| Celebrity | CelebrityProfileSoftDeleteFlagToggled | ? | celebrity |
| Celebrity | CreateSseGroup | SSE | sse |
| Celebrity | AddRoleToUser | Users | users |
| Celebrity | GenerateEncryptionKeys | Encryption | encryption |
| Celebrity | CreateDwollaAccount | Wallet? | dwolla |
| Celebrity | UpdateUserPii | Users | users |
| Fan | FanProfileCreated | ? | fans |
| Fan | CelebrityFollowed | ? | fans |
| Fan | CelebrityUnfollowed | ? | fans |
| Fan | AddUserToSseGroup | SSE | sse |
| Users | UserPiiUpdated | Celebrity, Fan | users |
| Users | RoleCreated/Deleted | ? | users |
| Users | GroupCreated/Deleted | ? | users |
| Keycloak | KeycloakEvent (register) | Fan | keycloak.events |
Notable: Celebrity publishes
CreateDwollaAccount — evidence that Dwolla was once
connected to the identity domain. Supports H2 investigation in Session
5.
Gen 1 → Gen 2 Comparison
| Aspect | Gen 1 | Gen 2 | Gap |
|---|---|---|---|
| GraphQL Framework | SPQR (io.leangen) | Spring Native GraphQL | None — all endpoints preserved |
| Celebrity Profile Fields | Basic (bio, name, tags) | Extended (+type, company, title, video, soft delete) | Gen 2 is superset |
| Fan Profile Fields | 4 fields | 4 fields (identical) | None |
| Security | Explicit KeycloakSecurityContext | Implicit SecurityUtil | Cleaner but functionally same |
| Persistence | javax.persistence | jakarta.persistence | Jakarta namespace |
| Test Coverage | ~0 | 2-3 files | Both very low |
No feature gaps identified. Gen 2 fully replaces Gen 1 for the identity domain.
Keycloak Migration Risk Assessment
High Risk
- Magic Link SPI — Custom Java code compiled into Keycloak Docker image. Must be rebuilt for any Keycloak major version upgrade. Tightly coupled to Twilio for SMS.
- Realm Configuration — Client secrets, redirect URIs, and scope mappings are environment-specific. Export/import via Keycloak admin API, but custom attributes may need manual verification.
- CIB Seven Plugin EOL — Camunda 7 CE support ends. If BPM engine is upgraded to Camunda 8 or replaced, the Keycloak identity provider plugin must be replaced.
Medium Risk
- Session Migration — Moving from identityx-25 to identityx-26 (already happening on agilenetwork tenant) requires session invalidation or migration.
- Multi-Tenant Expansion — Current single-realm model doesn’t support multi-tenant data isolation. If brands need separate user pools, architectural change is needed.
Low Risk
- Service JWT Validation — All services validate JWT via issuer URI. Changing Keycloak URL requires config update to all 35+ services.
- Database Migration — Identity domain schemas are straightforward (33 total migrations across celeb + fan). Additive changes only.
Modernization Implications
Consolidation Opportunity
Celebrity and Fan services share identical architecture (same core-lib, same patterns, same messaging). They could be consolidated into a single Profile Service with role-based differentiation. However: - Different domain complexity (celebrity has 22 migrations vs fan’s 11) - Different external integrations (celebrity: tracking/GCS; fan: MailChimp) - Consolidation risk may not justify the effort
Keycloak Strategy Options
- Keep Keycloak, upgrade (recommended) — Keycloak 26 is modern. Preserve Magic Link SPI. Replace CIB Seven plugin.
- Replace with Auth0/Okta — Would require reimplementing Magic Link, migrating all client configs, and updating 35+ services. Very high effort.
- Replace with custom auth — Not recommended. Keycloak provides too much out-of-box (OAuth2, OIDC, MFA, social login, user management).
Multi-Brand Architecture
The identity domain reveals H11 (multi-brand is frontend-only) is partially supported: Keycloak uses environment variables for realm config, suggesting separate deployments per tenant. No backend brand-specific business logic found in celebrity or fan services. However, GCS asset paths are tenant-specific, and MailChimp audience IDs are hardcoded.
Last updated: 2026-01-30 — Session 2 Review by: 2026-07-30 Staleness risk: Medium — Keycloak version and SPI code may evolve