- Enable Single Sign-On (SSO) so users can log in once and access multiple internal apps and dashboards seamlessly.
- SSO login via OpenID Connect (OIDC), IdP handles authentication only
Establish a single source of truth for user profile data, implemented as structured fields known as claims:
-
A single User Identity Service manages all user data (claims, documents, verification, etc.)
- Example claims:
name
,family_name
,mobile
,phone
,address
,national_code
,id_images
,passport_images
, etc.
- Example claims:
-
Each claim is represented as an object with
value
and_meta
fields, allowing tracking of verification, updates, and provenance uniformly. -
Each claim:
- May be present in some apps but absent in others
- Can be optional in one app and mandatory in another
- May require manual verification (e.g., by an admin) for use in specific apps
# Example Claim Structure identity: name: value: "Ali" _meta: verified: true verified_by: "[email protected]" national_code: value: "1234567890" _meta: verified: true verified_by: "[email protected]" contact: email: value: "[email protected]" _meta: verified: true last_updated: "2024-06-20" documents: avatar: url: "https://path/to/doc.jpg" _meta: public: true status: "verified" verified_by: "[email protected]" passport: url: ["https://path/to/page1.jpg", "https://path/to/page2.jpg"] _meta: status: "pending_verification" verified_by: null custom_meta_only_for_passport: "hi!"
Seamless integration with Laravel, Filament, .NET, and future apps without vendor or language lock-in.
Current dashboards have:
- Fragmented UX (duplicate registration/data entry)
- No shared claims or user states
- Inconsistent verification mechanisms
We aim to consolidate identity management while keeping authentication lightweight, and eventually allow for fine-grained access control per app and claim.
- Manages login, registration, and session lifecycle
- Implements OpenID Connect (OIDC) for token-based identity exchange
- Examples: Keycloak, Authentik, Authelia, etc (See existing solutions for options)
- REST API to manage shared claims
- Responsibilities:
- Create, Edit, Store and expose profile claims
- Track verification status
- Handle admin/user-initiated verification flows
- Audit sensitive changes
- Claim editing, particularly for sensitive fields (e.g., passport, national_code, phone_verified), is governed by a role-based access control (RBAC) system within the User Identity Service. This system defines:
- Which roles (e.g., admin, reviewer, superadmin) can view, edit, or verify specific claims
- Which actions are permitted per role (e.g., edit vs. verify vs. override user input)
- Audit logging for all admin-driven changes to sensitive claims
β οΈ Admins do not bypass claim structure or verification flows β their permissions are scoped, auditable, and enforced by API boundaries.
- Claim editing, particularly for sensitive fields (e.g., passport, national_code, phone_verified), is governed by a role-based access control (RBAC) system within the User Identity Service. This system defines:
- Used by the profile service to validate sensitive claims (e.g., phone number, ZIP code, national ID)
- Examples: Twilio, Loqate
- Authenticate via OIDC (using IdP)
- Use the
sub
claim (subject identifier) as the consistent user ID across systems - Retrieve authorized claims from the Profile API
- Claim access rules for each app (e.g., which claims App X can read or edit) are centrally defined and enforced by the User Identity Service.
- Store only app-specific data locally (e.g., theme, app-specific data)
Component | Responsibility |
---|---|
Identity Provider | Authenticates users; issues tokens with user ID (sub ) |
User Identity Service | Central API/Dashboard to manage and validate user claims |
Validation Services | External APIs to validate sensitive fields |
Apps (A, B, etc.) | Use OIDC to authenticate and query claims via User Identity Service API |
- π Unified Authentication: Log in once, access all services
- π¦ Centralized Data: One authoritative profile per user, accessible to all authorized apps
- πΈ Efficient Verification: Expensive checks done once, reused system-wide
- βοΈ Tech-Agnostic: Compatible with diverse stacks (Laravel, .NET, etc.)
- π± Scalable Design: Clean separation of concerns supports growth
GET /profile/{sub} # Fetch full profile
PATCH /profile/{sub} # Update claims (partial)
POST /profile/{sub}/verify:type # Trigger verification (e.g., phone, document)
GET /profile/{sub}/verify:type # Get verification status
- Apps may enforce custom rules (e.g., require
mobile_verified == true
before granting access). - Apps must not persist or override shared claims locally β temporary caching is allowed, but canonical data must come from the Profile API
- Authorization should be claim-scoped per application, enforcing least-privilege access to profile data.
Component | Stack Options |
---|---|
IdP | Keycloak, Authentik, Authelia (OIDC) |
User Identity Service | Laravel |
Validation | Twilio, Loqate, PostGrid |
Frontend Apps | Laravel, Filament, React, .NET |
graph TB
A[User]
X[Admin]
B[IdP: Authentication and Shared Session]
subgraph Applications
C1[App A]
C2[App B]
C3[App C]
end
D[User Identity Service]
E[External Validation APIs]
%% IdP communicates with User Identity Service
B <-->|internal api <br> sync sub| D
%% Login Flow
A -->|Login| B
X -->|Login| B
%% App Access (incl. Laravel)
B -->|sub| C1
B -->|sub| C2
B -->|sub| C3
B -->|sub| D
%% Claims
C1 -->|Read Claims| D
C2 -->|Read Claims| D
C3 -->|Read Claims| D
%% Processing
D -->|Validate Claims| E
-
β Storing claim verification in local app DBs β Always query the User Identity Service API
-
β Direct DB access to User Identity Service β Use secure REST APIs with access controls
-
β Mixing incompatible auth protocols (e.g., SAML + OIDC) β Standardize on OIDC across all apps for maintainability and interoperability
1. Is a separate User Identity Service necessary?
β Yes, because:
- we want dynamic claims creation
- Claims are tied to verification workflows (internal or external)
- Apps require filtered access to claims (e.g. only some can access sensitive documents)
- Complex claims (e.g., passport) can contain multiple structured values (e.g., image URLs) along with rich metadata fields like verified, verified_by, or custom tags, all stored in a uniform schema.
2. Should we store data into OIDC tokens?
π« No.
- The authentication layer exists solely to log the user in and maintain their identity across apps β it should not handle profile or verification data.
- claims should be stored in our User Identity Service and be accessed through API
- Login identifiers (e.g., mobile, username) may be included in OIDC claims for convenience, but all profile data must be sourced from the User Identity Service API.
3. How dynamic can the system be?
π― Goal: Make claim/app definitions dynamic via admin UI.
- Define new claims
- Assign claims to apps
- Control who can view/edit/verify claims
- After reconsideration, itβs likely better to treat claims as semi-static β predefined in updates Admins can control claim availability, visibility, and per-app requirements via the User Identity Service dashboard, rather than relying on fully dynamic definitions.
- Claim: A unit of user profile data (e.g.,
email
,phone_verified
) sub
Claim: Unique user ID issued by IdP, used as primary key across apps- Refer to the Glossary for complete definitions.
*PS: IK existing solutions and glossary links are broken they are just some terms and links