Identity and access control which organization, application, case, report, transaction, and activity-log data a portal user can access. The codebase implements enterprise identity with WorkOS modules and email OTP with Magic Auth. The architecture boundary is the authenticated session and the GET /auth/me permission response consumed by the Audit UI and API guards.

Identity implementation

ComponentResponsibility
WorkosAuthModuleWorkOS-backed authentication, organization selection, session/JWT flow
MagicAuthModuleEmail OTP authentication flow
WorkosAuthGuardBackend route guard for authenticated API requests
OrganisationSessionServiceOrganization-session handling
AuthMeServiceSession-to-permission mapping
Session identity is mapped to:
  • Selected organization id.
  • User id and email.
  • Team member metadata in team_member_meta.
  • Owner-level permissions from organization/team metadata.
  • Application-level permissions from team_member_application_permissions.

Access storage

TableFields used for access
organisationsOrganization id and display metadata
team_member_metaorg_id, workos_invitation_id, workos_user_id, invite_email, full_name, role_slug, permissions, external_org, access_expires_at
applicationsorg_id, foreign_id, name, application_type, association.contract_id
team_member_application_permissionsteam_member_meta_id, application_id, common_permissions, administrator_permissions, auditor_permissions
case_auditor_assignmentsCase-level assignment for auditor worklists and case access

GET /auth/me

GET /auth/me returns the organization, owner permissions, and application permission buckets for the current session.
{
  "organization_info": { "org_id": "org_...", "name": "Example Org" },
  "owner": { "applications:read": true },
  "applications": {
    "payments-demo": {
      "application_info": { "name": "Payments Demo" },
      "common": { "logs:view_activity": true },
      "administrator": { "cases:approve_creation": true },
      "auditor": { "reports:view_transactions": true }
    }
  }
}
Important keys:
  • owner contains organization owner permissions.
  • applications[foreignId] contains per-application buckets.
  • foreignId is applications.foreign_id, also used in /workspace/application/:foreignId and /api/applications/:foreignId/....

Workspace resolution

The UI builds available workspaces from auth/me.
UI workspaceConditionRoute
Organization ownerAt least one owner permission is granted/workspace/organization-owner/*
ApplicationAt least one permission bucket exists for applications[foreignId]/workspace/application/:foreignId/*
Navigation is filtered by permission keys. API checks still enforce access server-side.

Owner permissions

Granted at organization scope.
PermissionKey
Create applicationsapplications:create
Read applicationsapplications:read
Manage application administratorsadmins:manage_application_administrators
View organization activity loglogs:view_activity
Create organization reportsreports:create
List organization reportsreports:list
Download organization reportsreports:download

Application permission buckets

Application permissions are stored as three arrays on team_member_application_permissions.

Common

PermissionKey
View activity loglogs:view_activity

Administrator

PermissionKey
Approve case creationcases:approve_creation
Edit case auditorscases:edit

Auditor

PermissionKey
Create casescases:create
Withdraw pending case requestscases:withdraw_pending_request
View transactionsreports:view_transactions
Create application reportsreports:create
List application reportsreports:list
Download application reportsreports:download
Default role buckets are defined in permissions.constants.ts. Organization owners can adjust access through team-management flows.

API enforcement path

Application-scoped routes do not trust the client to provide internal application_id. The server derives it from :foreignId and the authenticated organization.

Case-level access

Application permission is necessary but not always sufficient. Case review additionally checks:
  • Case belongs to the same organization.
  • Case belongs to the resolved application.
  • Case request is not withdrawn.
  • Case has been approved.
  • User is assigned through case_auditor_assignments when auditor-scoped.
  • Case access has not expired under access_days.
  • Requested fields are within the approved disclosure flags.
This is the access boundary for interpreted transaction data.