Skip to content

Instantly share code, notes, and snippets.

@sunfmin
Last active December 23, 2025 00:53
Show Gist options
  • Select an option

  • Save sunfmin/b2151b985e2e488a400a8897ae601839 to your computer and use it in GitHub Desktop.

Select an option

Save sunfmin/b2151b985e2e488a400a8897ae601839 to your computer and use it in GitHub Desktop.

Feature Specification: Qortex IAM Console

Feature Branch: 001-iam-console
Created: 2025-12-18
Status: Draft
Input: Qortex IAM System Architecture Design

Overview

Qortex IAM is an independently deployable authentication and access management service providing unified capabilities for the Qortex product family (PIM, OIM, CIAM, Loyalty Console, Marketing Console):

  • Administrator Authentication (AuthN)
  • Access Control (AuthZ)
  • Single Sign-On (SSO)

Design Goals

  1. Self-Sufficient: Complete functionality when deployed as single system
  2. Multi-System Sharing: Unified authentication, permissions, seamless SSO
  3. Minimal Intrusion: Business systems integrate via SDK + code generation
  4. Extensible: Support new system onboarding and identity source expansion
  5. Customer-Level Deployment: Each customer has isolated deployment and data

Clarifications

Session 2025-12-18

  • Q: How are new users notified of their account creation? → A: System displays invitation link on page after user creation; admin manually shares link with invited user (no email sent by system)
  • Q: Identity sources supported? → A: Built-in (Email/Password, Google OAuth) + External OIDC (customer IdP)
  • Q: Permission model? → A: RBAC with system-level and feature-level permissions (format: {system}:{resource}:{action})
  • Q: How is the first admin user created on initial system access? → A: System shows setup page to create first admin user; once created, setup page becomes inaccessible
  • Q: How is Google OAuth configured? → A: Via UI in Identity Providers page (not environment variables); admin enters client ID and secret in the console
  • Q: How are permissions managed? → A: Predefined by each system; admins cannot create/edit/delete permissions (read-only in IAM Console)
  • Q: How do permissions enter IAM system? → A: Each system registers its permissions via API call on startup (POST /api/v1/systems/register)
  • Q: How does IAM manage its own permissions? → A: IAM is treated as a system like PIM/OIM; it registers its own permissions (iam:user:, iam:role:, etc.) on startup; initial setup creates iam_admin role with all IAM permissions and assigns it to first user
  • Q: What does "administrators" mean in the spec? → A: Permission-based; any user with the required IAM permission for that action (e.g., iam:user:create to create users) - not a fixed role
  • Q: User name field structure for Japan market? → A: Replace single name field with given_name, family_name, given_name_kana, family_name_kana (Japanese market localization)
  • Q: Are kana fields required? → A: Only given_name and family_name are required; kana fields (given_name_kana, family_name_kana) are optional
  • Q: User name display format? → A: Japanese format - family_name given_name (姓 名)
  • Q: UI language? → A: English only (prototype phase)
  • Q: Form field labels for name fields? → A: English labels (Given Name, Family Name, Given Name Kana, Family Name Kana)
  • Q: Kana field input validation? → A: Validate kana fields accept only hiragana/katakana characters

Session 2025-12-22

  • Q: Deployment topology for SSO? → A: Same parent domain with multiple subdomains, or multiple apps under the same domain differentiated by /sub-path-prefix/*
  • Q: Should frontend handle JWT tokens? → A: No; browser login state is maintained via httpOnly cookies (session)
  • Q: CSRF/SameSite preference? → A: Prefer SameSite=Lax

User Scenarios & Testing (mandatory)

User Story 1 - User Management (Priority: P1)

As an IAM administrator, I need to manage user accounts so that I can control who has access to Qortex systems and what they can do.

Why this priority: User management is the core function of IAM. Without users, no authentication or authorization can occur.

Independent Test: Can be fully tested by creating a user via invitation, viewing their details, updating their information, and deleting them.

Acceptance Scenarios:

  1. Given I am logged in as an IAM administrator, When I navigate to the Users page, Then I see a paginated list of all users with given name, family name (and kana variants), email, status, identity provider, and assigned roles
  2. Given I am on the Users page, When I click "Invite User" and enter email, given name, family name, given name kana, family name kana, and assign roles, Then the user is created with "invited" status and an invitation link is displayed for me to copy and share
  3. Given I am viewing a user, When I edit their roles or status, Then the changes are saved immediately
  4. Given I am viewing a user, When I delete the user and confirm, Then the user is removed from the system
  5. Given a user has been invited, When they click the invitation link, Then they are taken to a page to set their password
  6. Given a user sets their password via invitation, When they submit, Then their status changes to "active" and they can sign in

User Story 2 - Email/Password Authentication (Priority: P1)

As an invited user, I need to set my password and sign in so that I can access the systems I'm authorized to use.

Why this priority: Email/Password is the primary built-in authentication method. Users cannot access any features without signing in.

Independent Test: Can be tested by completing invitation flow, signing in with credentials, and signing out.

Acceptance Scenarios:

  1. Given I received an invitation link from an administrator, When I click the link, Then I see a page to set my password
  2. Given I am setting my password, When I submit a valid password, Then my account is activated and an authenticated session is established
  3. Given I am on the sign-in page, When I enter valid email and password, Then I am authenticated and redirected to the appropriate system
  4. Given I am on the sign-in page, When I enter invalid credentials, Then I see an error message and remain on the sign-in page
  5. Given I am authenticated, When I sign out, Then my session ends and I am redirected to the sign-in page

User Story 3 - Google OAuth Authentication (Priority: P2)

As a user with Google account linked, I need to sign in via Google so that I can use my existing Google identity.

Why this priority: Google OAuth is a common enterprise authentication method, reducing password fatigue.

Independent Test: Can be tested by initiating Google login flow and verifying user is matched to internal account.

Acceptance Scenarios:

  1. Given I am on the sign-in page, When I click "Sign in with Google", Then I am redirected to Google authorization
  2. Given I authorize on Google, When I am redirected back, Then my Google email is matched to my internal user account
  3. Given my Google email matches an existing user, When the match succeeds, Then an authenticated session is established
  4. Given my Google email does not match any user, When the match fails, Then I see an error that I need to be invited first

User Story 4 - External OIDC Authentication (Priority: P3)

As a user from a customer organization, I need to sign in via my company's identity provider so that I can use my existing corporate credentials.

Why this priority: Enterprise customers often require integration with their existing identity systems (Azure AD, Okta, etc.).

Independent Test: Can be tested by configuring an external OIDC provider and completing the login flow.

Acceptance Scenarios:

  1. Given an external OIDC provider is configured, When I visit the sign-in page, Then I see an option to sign in with the external provider
  2. Given I click the external provider option, When I am redirected, Then I authenticate with my corporate credentials
  3. Given I authenticate successfully, When I am redirected back, Then my email is matched to an internal user and an authenticated session is established
  4. Given my email does not match any user, When the match fails, Then I see an error that I need to be invited first

User Story 5 - Role Management (Priority: P1)

As an IAM administrator, I need to manage roles so that I can define permission sets for different job functions.

Why this priority: Roles are the foundation of RBAC. Without roles, permissions cannot be assigned to users.

Independent Test: Can be tested by creating a role, assigning permissions, and verifying users with that role have the expected access.

Acceptance Scenarios:

  1. Given I am on the Roles page, When I view the list, Then I see all roles with their names and permission counts
  2. Given I am creating a new role, When I enter name and select permissions, Then the role is created
  3. Given I am editing a role, When I add or remove permissions, Then the changes apply to all users with that role
  4. Given I am viewing a role, When I delete it, Then the role is removed (users lose those permissions)

User Story 6 - Permission-Based Access Control (Priority: P1)

As a user with assigned roles, I need the system to enforce my permissions so that I can only access features I'm authorized to use.

Why this priority: Permission enforcement is the core value of IAM - ensuring users can only do what they're allowed to do.

Independent Test: Can be tested by assigning different roles and verifying access is granted/denied appropriately.

Acceptance Scenarios:

  1. Given I have a role with "pim:access" permission, When I try to access PIM, Then I am allowed
  2. Given I do NOT have "pim:access" permission, When I try to access PIM, Then I am denied with 403 error
  3. Given I have "pim:product:create" permission, When I try to create a product, Then the action succeeds
  4. Given I do NOT have "pim:product:create" permission, When I try to create a product, Then the action is denied

User Story 7 - Identity Provider Configuration (Priority: P2)

As an IAM administrator, I need to configure identity providers via the console UI so that users can authenticate via different methods.

Why this priority: Flexibility in authentication methods is required for enterprise customers. UI-based configuration allows non-technical admins to manage auth settings without server access.

Independent Test: Can be tested by configuring Google OAuth and an external OIDC provider through the UI.

Acceptance Scenarios:

  1. Given I am on the Identity Providers page, When I view the list, Then I see configured providers with their status (enabled/disabled)
  2. Given I click "Add Provider" and select Google OAuth, When I enter client ID and client secret in the form, Then the configuration is saved and Google login becomes available on sign-in page
  3. Given I click "Add Provider" and select External OIDC, When I enter discovery URL, client ID, client secret, and scopes, Then the provider is added and appears on sign-in page
  4. Given I am viewing a configured provider, When I click "Edit", Then I can update the configuration (client ID, secret, etc.)
  5. Given I toggle a provider's enabled status to disabled, When users visit sign-in page, Then that login option is hidden
  6. Given I delete a provider, When I confirm deletion, Then the provider is removed and users can no longer use it to sign in

User Story 8 - System & Permission Management (Priority: P2)

As an IAM administrator, I need to view registered systems and their permissions so that I can understand what access rights are available for role assignment.

Why this priority: Multi-system support requires knowing which systems exist and their permission definitions. Permissions are read-only (defined by each system).

Independent Test: Can be tested by viewing systems and their permissions after a system registers via API.

Acceptance Scenarios:

  1. Given I am on the Systems page, When I view the list, Then I see all registered systems with their status (enabled/disabled)
  2. Given I am viewing a system, When I click on it, Then I see all permissions defined for that system (read-only)
  3. Given a Qortex system starts up, When it calls POST /api/v1/systems/register with its permissions, Then the system and permissions are stored in IAM
  4. Given I am viewing permissions, When I try to edit or delete them, Then I cannot (permissions are read-only, managed by source systems)
  5. Given a system re-registers with updated permissions, When IAM receives the registration, Then permissions are updated (new ones added, removed ones deleted)

User Story 9 - Initial System Setup (Priority: P0)

As a system deployer, I need to create the first administrator account when the system is accessed for the first time so that the IAM system can be managed.

Why this priority: Without an initial admin user, no one can manage users, roles, or permissions. This is a prerequisite for all other functionality.

Independent Test: Can be tested by accessing a fresh system deployment and completing the setup flow.

Acceptance Scenarios:

  1. Given the system has no users, When anyone accesses the system, Then they are redirected to the initial setup page
  2. Given I am on the initial setup page, When I enter email, given name, family name, given name kana, family name kana, and password for the first admin, Then the user is created and assigned the built-in IAM administrator role (the system role that grants full IAM access)
  3. Given the first admin user has been created, When anyone tries to access the setup page, Then they are redirected to the sign-in page (setup page is inaccessible)
  4. Given the first admin user exists, When I sign in with those credentials, Then I have full access to manage users, roles, and permissions
  5. Given the first admin user exists, When I sign in, Then the authenticated session contains all IAM permission codes registered by IAM and includes the built-in IAM administrator role

Edge Cases

  • What happens when an administrator tries to delete their own account? → Prevent self-deletion
  • What happens when the last IAM admin tries to remove their iam:access permission? → Prevent orphaning
  • How does the system handle duplicate emails during user creation? → Reject with validation error
  • What happens when a user's session expires? → Redirect to login (and preserve intended destination when possible)
  • What happens when an external OIDC provider is unavailable? → Show error, allow fallback to other methods
  • What happens when a role is deleted that users are assigned to? → Users lose those permissions immediately
  • What happens when filtering returns no results? → Show empty state message

ARIA Snapshots

These snapshots define the expected accessibility structure for each page. Use these as the basis for E2E tests with toMatchAriaSnapshot.

Initial Setup Page

Route: /setup User Story: US-9

- heading "Initial Setup" [level=1]
- textbox "Email"
- textbox "Given Name"
- textbox "Family Name"
- textbox "Given Name Kana"
- textbox "Family Name Kana"
- textbox "Password"
- textbox "Confirm Password"
- button "Create Administrator"

Sign-In Page

Route: /sign-in User Story: US-2, US-3, US-4

- heading "Sign In" [level=1]
- textbox "Email"
- textbox "Password"
- button "Sign In"
- separator
- button "Sign in with Google"
- button /Sign in with .*/

Set Password Page (Invitation)

Route: /invitation/:token User Story: US-1, US-2

- heading "Set Your Password" [level=1]
- textbox "Password"
- textbox "Confirm Password"
- button "Activate Account"

Users List Page

Route: /users User Story: US-1

- heading "Users" [level=1]
- textbox "Search users..."
- combobox "Status"
- combobox "Role"
- button "Invite User"
- table:
  - rowgroup:
    - row "Name Email Status Identity Provider Roles Actions"
- navigation "Pagination"

Invite User Dialog

Route: /users (dialog) User Story: US-1

- dialog "Invite User":
  - heading "Invite User" [level=2]
  - textbox "Email"
  - textbox "Given Name"
  - textbox "Family Name"
  - textbox "Given Name Kana"
  - textbox "Family Name Kana"
  - group "Roles":
    - checkbox /.*/
  - button "Cancel"
  - button "Send Invitation"

Invitation Link Dialog

Route: /users (dialog after invite) User Story: US-1

- dialog "Invitation Link":
  - heading "Invitation Link" [level=2]
  - textbox "Invitation URL" [readonly]
  - button "Copy Link"
  - button "Close"

User Detail Page

Route: /users/:id User Story: US-1

- heading /.*/ [level=1]
- text "Email"
- text "Status"
- text "Identity Provider"
- heading "Roles" [level=2]
- group "Assigned Roles":
  - checkbox /.*/
- button "Save Changes"
- button "Regenerate Invitation Link"
- button "Delete User"

Roles List Page

Route: /roles User Story: US-5

- heading "Roles" [level=1]
- button "Create Role"
- table:
  - rowgroup:
    - row "Name Permissions Actions"

Role Create/Edit Page

Route: /roles/new or /roles/:id User Story: US-5

- heading /Create Role|Edit Role/ [level=1]
- textbox "Role Name"
- textbox "Description"
- heading "Permissions" [level=2]
- group /.* Permissions/:
  - checkbox /.*/
- button "Cancel"
- button "Save Role"
- button "Delete Role"

Identity Providers Page

Route: /identity-providers User Story: US-7

- heading "Identity Providers" [level=1]
- button "Add Provider"
- table:
  - rowgroup:
    - row "Name Type Status Actions"

Google OAuth Provider Form

Route: /identity-providers/google (dialog) User Story: US-7

- dialog "Configure Google OAuth":
  - heading "Google OAuth" [level=2]
  - textbox "Client ID"
  - textbox "Client Secret"
  - switch "Enabled"
  - button "Cancel"
  - button "Save"

External OIDC Provider Form

Route: /identity-providers/oidc (dialog) User Story: US-7

- dialog "Configure OIDC Provider":
  - heading "External OIDC" [level=2]
  - textbox "Provider Name"
  - textbox "Discovery URL"
  - textbox "Client ID"
  - textbox "Client Secret"
  - textbox "Scopes"
  - switch "Enabled"
  - button "Cancel"
  - button "Save"

Systems Page

Route: /systems User Story: US-8

- heading "Systems" [level=1]
- table:
  - rowgroup:
    - row "Name Code Status Permissions"

System Detail Page

Route: /systems/:id User Story: US-8

- heading /.*/ [level=1]
- text "System Code"
- text "Status"
- heading "Permissions" [level=2]
- table:
  - rowgroup:
    - row "Permission Code Name Type"

Requirements (mandatory)

Functional Requirements

Initial System Setup

  • FR-000a: System MUST detect when no users exist and redirect to initial setup page
  • FR-000b: System MUST allow creation of first admin user with email, given_name, family_name, given_name_kana, family_name_kana, and password on setup page
  • FR-000c: System MUST register IAM as a system with its own permissions on first startup (same as other Qortex systems)
  • FR-000d: System MUST create a built-in IAM administrator role that contains all IAM permissions during initial setup
  • FR-000e: System MUST assign the built-in IAM administrator role to the first user automatically
  • FR-000f: System MUST make setup page inaccessible once first admin user exists (redirect to sign-in)
  • FR-000g: System MUST NOT allow bypassing setup page check via direct URL access
  • FR-000h: System MUST ensure the first admin's authenticated session exposes all IAM permission codes and includes the built-in IAM administrator role

User Management (requires respective iam:user: permissions)*

  • FR-001: System MUST allow users with iam:user:create permission to create users with email, given_name, family_name, given_name_kana, family_name_kana, and assigned roles
  • FR-002: System MUST generate secure invitation token and display invitation link on page after user creation (no email sent by system)
  • FR-002a: System MUST allow users with iam:user:create permission to copy invitation link to clipboard
  • FR-002b: System MUST allow users with iam:user:update permission to regenerate invitation link for users with "invited" status
  • FR-003: System MUST allow invited users to set password via invitation link
  • FR-004: System MUST change user status from "invited" to "active" after password is set
  • FR-005: System MUST validate that email is unique across all users
  • FR-006: System MUST allow users with iam:user:read permission to view a paginated list of all users
  • FR-007: System MUST allow users with iam:user:update permission to update user roles and status
  • FR-008: System MUST allow users with iam:user:delete permission to delete users (except self-deletion)
  • FR-009: System MUST support user statuses: active, inactive, invited, suspended
  • FR-010: System MUST display user's identity provider (local, google, oidc)

Authentication - Email/Password

  • FR-011: System MUST provide email/password sign-in for local users
  • FR-012: System MUST establish an authenticated session for a successfully authenticated user
  • FR-012a: System MUST persist login state using httpOnly cookies (frontend does not manage tokens directly)
  • FR-013: System MUST support SSO across Qortex systems when deployed under the same parent domain, including:
    • Multiple subdomains under the same parent domain
    • Multiple apps under the same domain differentiated by a /sub-path-prefix/*
  • FR-014: System MUST provide sign-out mechanism that invalidates the server-side session and clears the session cookie
  • FR-015: System MUST renew/extend an active session automatically on user activity, without requiring re-authentication
  • FR-015a: System MUST enforce a session expiration policy with:
    • Idle timeout: 2 hours
    • Absolute lifetime: 7 days
  • FR-015b: System MUST set authenticated session cookies with SameSite=Lax and MUST protect state-changing requests against CSRF attacks

Authentication - Programmatic Access (Non-Browser Clients)

  • FR-021a: System MUST support non-browser clients (e.g., AI agents) accessing the same data APIs without relying on browser cookies
  • FR-021b: System MUST provide an authentication mechanism for non-browser clients based on revocable API credentials (e.g., API tokens) sent via request headers
  • FR-021c: System MUST allow administrators to revoke non-browser client credentials, and revocation MUST take effect immediately for subsequent requests

Authentication - Google OAuth

  • FR-016: System MUST support Google OAuth login when configured
  • FR-017: System MUST match Google email to existing user account
  • FR-018: System MUST reject Google login if no matching user exists (invitation required)

Identity Provider Management (UI-based)

  • FR-022: System MUST provide UI form to configure Google OAuth (client ID, client secret)
  • FR-023: System MUST provide UI form to configure external OIDC providers (discovery URL, client ID, client secret, scopes)
  • FR-024: System MUST allow enabling/disabling identity providers via UI toggle
  • FR-025: System MUST show available login options on sign-in page based on enabled providers
  • FR-025a: System MUST allow editing existing provider configurations via UI
  • FR-025b: System MUST allow deleting identity providers (except built-in local provider)
  • FR-025c: System MUST validate provider configuration before saving (e.g., test OIDC discovery URL)

Role & Permission Management

  • FR-026: System MUST support RBAC with roles as permission collections
  • FR-027: System MUST use permission format: {system}:{resource}:{action}
  • FR-028: System MUST support system-level permissions (e.g., pim:access)
  • FR-029: System MUST support feature-level permissions (e.g., pim:product:create)
  • FR-030: System MUST allow creating, editing, and deleting custom roles
  • FR-031: System MUST provide a way for clients to obtain the authenticated user's permissions for client-side checks
  • FR-032: System MUST enforce permissions on API endpoints

System & Permission Registration

  • FR-033: System MUST maintain registry of Qortex systems (IAM, PIM, OIM, etc.)
  • FR-034: System MUST store permissions per system (read-only, defined by source systems)
  • FR-035: System MUST provide API endpoint POST /api/v1/systems/register for system registration
  • FR-035a: Registration payload MUST include system code, name, and array of permissions
  • FR-035b: Each permission MUST include code (format: {system}:{resource}:{action}), name, and type (system/feature)
  • FR-035c: System MUST update permissions on re-registration (add new, remove deleted)
  • FR-035d: System MUST NOT allow manual creation/edit/deletion of permissions via UI (read-only display)
  • FR-035e: System registration API MUST be authenticated with system API key

Search and Filtering

  • FR-036: System MUST allow filtering users by status
  • FR-037: System MUST allow filtering users by role
  • FR-038: System MUST allow searching users by email, given_name, family_name, or kana fields
  • FR-039: System MUST support cursor-based pagination

Key Entities

  • User: Person with system access. Attributes: id, email, given_name (required), family_name (required), given_name_kana (optional), family_name_kana (optional), status, identity_provider_id, password_hash, invitation_token, created_at, updated_at
  • Role: Named permission collection. Attributes: id, code, name, description, is_system (preset vs custom)
  • Permission: Access right. Attributes: id, system_id, code, name, type (system/feature)
  • System: Qortex product. Attributes: id, code, name, description, enabled
  • IdentityProvider: Auth source. Attributes: id, type (local/google/oidc), name, config (jsonb), enabled, is_default
  • UserRole: Many-to-many join. Attributes: user_id, role_id
  • RolePermission: Many-to-many join. Attributes: role_id, permission_id

Permission Naming Convention

Format: {system}:{resource}:{action}

Action Description
access System-level access permission
create Create resource
read View resource
update Modify resource
delete Remove resource
import Import data
export Export data
manage Full control (includes all actions)

Examples:

  • iam:access - Access IAM Console
  • iam:user:create - Create users
  • pim:product:read - View products
  • oim:order:refund - Process refunds

IAM System Permissions (Self-Registered)

IAM registers itself as a system with the following permissions:

Permission Code Name Type
iam:access Access IAM Console system
iam:user:create Create Users feature
iam:user:read View Users feature
iam:user:update Update Users feature
iam:user:delete Delete Users feature
iam:role:create Create Roles feature
iam:role:read View Roles feature
iam:role:update Update Roles feature
iam:role:delete Delete Roles feature
iam:idp:create Create Identity Providers feature
iam:idp:read View Identity Providers feature
iam:idp:update Update Identity Providers feature
iam:idp:delete Delete Identity Providers feature
iam:system:read View Systems feature

iam_admin role (created during initial setup): Contains all IAM permissions above.

Success Criteria (mandatory)

Measurable Outcomes

  • SC-001: Administrators can invite a new user in under 1 minute
  • SC-002: Invited users can set password and sign in within 2 minutes of clicking link
  • SC-003: Users can sign in on first attempt with valid credentials
  • SC-004: Google/OIDC login completes in under 5 seconds after provider auth
  • SC-005: Session renewal happens transparently without user action
  • SC-006: Permission checks complete in under 10ms
  • SC-007: Role changes take effect immediately on next request
  • SC-008: System displays appropriate error messages for all auth failures
  • SC-009: SSO works seamlessly across all Qortex systems on same domain
  • SC-010: 100% of unauthorized access attempts are blocked
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment