Files
pos-system/services/iam-service-net/SERVICE_DOCS.md
Ho Ngoc Hai f3779c4ebe docs: add SERVICE_DOCS.md for all 24 microservices from per-service code audit
Each SERVICE_DOCS.md documents: Overview, API Endpoints, Commands, Queries,
Domain Model, Database Schema, Integration Events, Dependencies, Configuration.
Generated by 23 parallel audit agents reading actual source code.

Key corrections from audit:
- inventory-service: 12 commands/6 queries (was listed as scaffold)
- promotion-service: 12 commands/10 queries (was listed as 0)
- mission-service: 4 commands/7 queries (was listed as 0)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 17:54:53 +07:00

35 KiB

IAM Service (iam-service-net) — Service Documentation

Auto-generated from code audit on 2026-03-13

Overview

Identity and Access Management service for the GoodGo Platform. Handles user authentication (OAuth2/OIDC via Duende IdentityServer), user registration, role-based access control (RBAC), organizations, groups, access requests/reviews, privileged access management (PAM), identity verification (phone/email/document), audit logging, and compliance reporting.

  • Port: 5001 (Development)
  • Database: iam_service (PostgreSQL / Neon)
  • Base Route: api/v1
  • Auth: Duende IdentityServer (OAuth2 Password Grant, Authorization Code + PKCE, Client Credentials)
  • Framework: .NET 10.0, C# 14

API Endpoints

AuthController — api/v1/auth

Method Route Auth Description
POST /auth/register None Register a new user
POST /auth/change-password Bearer Change user password
POST /auth/logout Bearer Logout and revoke tokens
POST /auth/send-verification-email None Send email verification link
POST /auth/confirm-email None Confirm email with token
POST /auth/2fa/enable Bearer Enable 2FA (returns QR code + recovery codes)
POST /auth/2fa/verify Bearer Verify 2FA TOTP code to complete setup
POST /auth/2fa/disable Bearer Disable 2FA (requires current code)
GET /auth/external-login/{provider} None Initiate OAuth with Google/Facebook
GET /auth/external-callback None Handle OAuth callback
GET /auth/linked-accounts Bearer List linked external providers

OAuth2 Token Endpoint (Duende IdentityServer)

Method Route Auth Description
POST /connect/token Client Credentials OAuth2 token endpoint (password, authorization_code, client_credentials, refresh_token)

UsersController — api/v1/users

Method Route Auth Description
GET /users Bearer + RequireAdmin Get all users (paginated)
GET /users/{id} Bearer + OwnerOrAdmin Get user by ID
PUT /users/{id} Bearer + OwnerOrAdmin Update user (firstName, lastName)
DELETE /users/{id} Bearer + RequireAdmin Soft-delete (deactivate) user
GET /users/me Bearer Get current user info from JWT claims
GET /users/{id}/roles Bearer Get user's assigned roles
GET /users/{id}/permissions Bearer Get user's permissions (from claims)

RolesController — api/v1/roles

Method Route Auth Description
GET /roles Bearer + RequireAdmin Get all roles (paginated)
GET /roles/{id} Bearer + RequireAdmin Get role by ID
POST /roles Bearer + RequireAdmin Create a new role
PUT /roles/{id} Bearer + RequireAdmin Update role (name, description)
DELETE /roles/{id} Bearer + RequireAdmin Delete role (system roles protected)
POST /users/{userId}/roles Bearer + RequireAdmin Assign role to user
DELETE /users/{userId}/roles/{roleName} Bearer + RequireAdmin Remove role from user

GroupsController — api/v1/groups

Method Route Auth Description
GET /groups?organizationId= Bearer + RequireAdmin Get groups by organization
GET /groups/{id} Bearer + RequireAdmin Get group by ID
POST /groups Bearer + RequireAdmin Create group
DELETE /groups/{id} Bearer + RequireAdmin Soft-delete group
POST /groups/{id}/members Bearer + RequireAdmin Add member to group
DELETE /groups/{id}/members/{userId} Bearer + RequireAdmin Remove member from group

OrganizationsController — api/v1/organizations

Method Route Auth Description
GET /organizations/{id} Bearer + RequireAdmin Get organization by ID
GET /organizations/slug/{slug} Bearer + RequireAdmin Get organization by slug
POST /organizations Bearer + RequireAdmin Create organization
PUT /organizations/{id} Bearer + RequireAdmin Update organization
DELETE /organizations/{id} Bearer + RequireAdmin Archive organization (soft delete)
GET /organizations/{id}/hierarchy Bearer + RequireAdmin Get organization hierarchy
GET /organizations/{id}/children Bearer + RequireAdmin Get child organizations

AccessRequestsController — api/v1/access-requests

Method Route Auth Description
POST /access-requests Bearer + RequireAdmin Create access request
GET /access-requests/{id} Bearer + RequireAdmin Get access request by ID
GET /access-requests?requesterId= Bearer + RequireAdmin Get my access requests
GET /access-requests/pending?approverId= Bearer + RequireAdmin Get pending approvals
POST /access-requests/{id}/submit Bearer + RequireAdmin Submit request for approval
POST /access-requests/{id}/approve Bearer + RequireAdmin Approve access request
POST /access-requests/{id}/reject Bearer + RequireAdmin Reject access request
DELETE /access-requests/{id} Bearer + RequireAdmin Cancel access request

AccessReviewsController — api/v1/access-reviews

Method Route Auth Description
POST /access-reviews Bearer + RequireAdmin Create access review
GET /access-reviews/{id} Bearer + RequireAdmin Get access review by ID
POST /access-reviews/{id}/items Bearer + RequireAdmin Add item to review
POST /access-reviews/{id}/start Bearer + RequireAdmin Start access review
POST /access-reviews/{id}/items/{itemId}/review Bearer + RequireAdmin Certify or revoke an item
POST /access-reviews/{id}/complete Bearer + RequireAdmin Complete access review

PrivilegedAccessController — api/v1/privileged-access

Method Route Auth Description
POST /privileged-access/request Bearer + RequireSuperAdmin Request JIT privileged access
GET /privileged-access/active?userId= Bearer + RequireSuperAdmin Get active privileged grants
POST /privileged-access/{id}/revoke Bearer + RequireSuperAdmin Revoke privileged access

UserProfilesController — api/v1/users

Method Route Auth Description
GET /users/{userId}/profile Bearer Get user profile
PUT /users/{userId}/profile Bearer Update user profile (bio, timezone, locale, avatarUrl)
PUT /users/{userId}/profile/attributes/{key} Bearer Set profile attribute (String/Number/Boolean/Date/Json)

VerificationsController — api/v1/verifications

Method Route Auth Description
POST /verifications/phone Bearer + RequireAdmin Request phone OTP verification
POST /verifications/email Bearer + RequireAdmin Request email OTP verification
POST /verifications/{id}/confirm Bearer + RequireAdmin Confirm verification with code

AuditController — api/v1/audit

Method Route Auth Description
GET /audit/logs Bearer + RequireAuditor Get audit logs (filtered by date, eventType, actor, resource)

ComplianceController — api/v1/compliance

Method Route Auth Description
POST /compliance/reports Bearer + RequireAuditor Generate compliance report
GET /compliance/reports Bearer + RequireAuditor Get compliance reports
GET /compliance/reports/{id} Bearer + RequireAuditor Get report by ID
GET /compliance/violations Bearer + RequireAuditor Get unresolved violations
POST /compliance/reports/{id}/complete Bearer + RequireAuditor Complete compliance report

Health Endpoints

Method Route Auth Description
GET /health None Full health check (PostgreSQL)
GET /health/live None Liveness probe (app running)
GET /health/ready None Readiness probe

Commands (Write Operations)

Auth Commands

RegisterUserCommand

  • Input: (Email, Password, FirstName, LastName)
  • Output: RegisterUserCommandResult(UserId, Email, FullName)
  • Logic: Check if user exists by email -> create ApplicationUser -> CreateAsync with password via UserManager -> raise UserRegisteredDomainEvent
  • Validator: Email required + valid format; Password 8+ chars, uppercase, lowercase, digit, special; FirstName/LastName required, max 100

ChangePasswordCommand

  • Input: (UserId, CurrentPassword, NewPassword)
  • Output: ChangePasswordCommandResult(Success, Message)
  • Validator: UserId required; CurrentPassword required; NewPassword 8+ chars with complexity rules; must differ from current

LogoutCommand

  • Input: (UserId)
  • Output: LogoutCommandResult(Success, Message)
  • Logic: Signs out via SignInManager

SendVerificationEmailCommand

  • Input: (Email)
  • Output: SendVerificationEmailResult
  • Validator: Email required, valid format, max 256

ConfirmEmailCommand

  • Input: (Email, Token)
  • Output: ConfirmEmailResult(Success, Message)
  • Validator: Email required + valid; Token required, max 1024

Enable2FACommand

  • Input: (UserId)
  • Output: Enable2FACommandResult(QrCodeBase64, ManualEntryKey, RecoveryCodes)
  • Validator: UserId required

Verify2FACommand

  • Input: (UserId, Code)
  • Output: Verify2FAResult(Success, Message)
  • Validator: UserId required; Code required, exactly 6 digits

Disable2FACommand

  • Input: (UserId, Code)
  • Output: Disable2FAResult(Success, Message)
  • Validator: UserId required; Code required, exactly 6 digits

ExternalLoginCommand

  • Input: (Provider, ProviderUserId, Email, Name?, PictureUrl?)
  • Output: ExternalLoginResult(UserId, Email, IsNewUser, Success, Message)
  • Validator: Provider in [Google, Facebook, Apple]; ProviderUserId required max 256; Email required valid max 256; Name max 200; PictureUrl valid URI max 2048

User Commands

UpdateUserCommand

  • Input: (UserId, FirstName?, LastName?)
  • Output: UpdateUserCommandResult(UserId, Email, FirstName, LastName, FullName)
  • Validator: UserId required; FirstName/LastName max 100

DeleteUserCommand

  • Input: (UserId)
  • Output: DeleteUserCommandResult(Success, Message)
  • Validator: UserId required

Role Commands

CreateRoleCommand

  • Input: (Name, Description?)
  • Output: CreateRoleCommandResult(Id, Name, Description, CreatedAt)
  • Validator: Name required, max 100, alphanumeric + _-. only; Description max 500

UpdateRoleCommand

  • Input: (RoleId, Name, Description?)
  • Output: UpdateRoleCommandResult(Id, Name, Description)
  • Validator: RoleId required; Name required max 100 alphanumeric; Description max 500

DeleteRoleCommand

  • Input: (RoleId)
  • Validator: RoleId required. System roles cannot be deleted.

AssignRoleToUserCommand

  • Input: (UserId, RoleName)
  • Validator: UserId required; RoleName required max 100

RemoveRoleFromUserCommand

  • Input: (UserId, RoleName)
  • Validator: UserId required; RoleName required max 100

Organization Commands

CreateOrganizationCommand

  • Input: (Name, Slug, Description?, ParentOrganizationId?)
  • Output: CreateOrganizationResult(Id, Name, Slug, Description, ParentOrganizationId, Status, CreatedAt)

UpdateOrganizationCommand

  • Input: (Id, Name, Description?)
  • Output: UpdateOrganizationResult(Id, Name, Description, UpdatedAt)

ArchiveOrganizationCommand

  • Input: (Id)
  • Logic: Soft-deletes by setting status to Archived. Cannot archive if has children.

Group Commands

CreateGroupCommand

  • Input: (Name, OrganizationId, Description?)
  • Output: CreateGroupResult(Id, Name, Description, OrganizationId, CreatedAt)

DeleteGroupCommand

  • Input: (GroupId)
  • Logic: Soft-delete via IsDeleted flag

AddGroupMemberCommand

  • Input: (GroupId, UserId, RoleId?) — RoleId: 1=Member, 2=Admin, 3=Owner
  • Output: AddGroupMemberResult(GroupId, UserId, Role, JoinedAt)

RemoveGroupMemberCommand

  • Input: (GroupId, UserId)
  • Logic: Cannot remove last owner

Access Request Commands

CreateAccessRequestCommand

  • Input: (RequesterId, ResourceType, ResourceId, RequestedPermission, Justification?, Priority?, ApproverIds)
  • Priority: 1=Low, 2=Medium, 3=High, 4=Critical

SubmitAccessRequestCommand — Input: (RequestId) ApproveAccessRequestCommand — Input: (RequestId, ApproverId, Comments?) RejectAccessRequestCommand — Input: (RequestId, ApproverId, Comments?) CancelAccessRequestCommand — Input: (RequestId)

Access Review Commands

CreateAccessReviewCommand — Input: (Name, Description?, OwnerId, Scope, DueDate) AddAccessReviewItemCommand — Input: (ReviewId, UserId, ResourceType, ResourceId, Permission) StartAccessReviewCommand — Input: (ReviewId) ReviewItemCommand — Input: (ReviewId, ItemId, ReviewerUserId, Certify, Comments?) CompleteAccessReviewCommand — Input: (ReviewId)

Privileged Access Commands

RequestPrivilegedAccessCommand — Input: (UserId, RoleId, ResourceScope, Reason?, GrantedByUserId, DurationMinutes) — Duration: 5-480 min RevokePrivilegedAccessCommand — Input: (GrantId, RevokedByUserId, Reason?)

Verification Commands

RequestPhoneVerificationCommand — Input: (UserId, PhoneNumber) — Generates 6-digit OTP, hashed with SHA256 RequestEmailVerificationCommand — Input: (UserId, Email) — Same OTP flow ConfirmVerificationCommand — Input: (VerificationId, Code) — Max 5 attempts, 10-min expiry

Compliance Commands

GenerateComplianceReportCommand — Input: (Name, ReportTypeId, GeneratedByUserId) CompleteComplianceReportCommand — Input: (ReportId, TotalChecks, PassedChecks, Summary?)


Queries (Read Operations)

GetUsersQuery — Input: (PageNumber, PageSize) — Returns paginated user list GetUserByIdQuery — Input: (UserId) — Returns single user or null GetRolesQuery — Input: (PageNumber, PageSize) — Returns paginated role list GetRoleByIdQuery — Input: (RoleId) — Returns single role or null GetGroupByIdQuery — Input: (GroupId) — Returns group with members/permissions GetGroupsByOrganizationQuery — Input: (OrganizationId) — Returns list of groups GetOrganizationByIdQuery — Input: (OrgId) — Returns organization or null GetOrganizationBySlugQuery — Input: (Slug) — Returns organization by slug GetOrganizationHierarchyQuery — Input: (OrgId) — Returns hierarchy chain GetChildOrganizationsQuery — Input: (OrgId) — Returns direct children GetAccessRequestByIdQuery — Input: (RequestId) — Returns access request with approvers GetMyAccessRequestsQuery — Input: (RequesterId) — Returns requester's requests GetPendingApprovalsQuery — Input: (ApproverId) — Returns requests pending for approver GetUserProfileQuery — Input: (UserId) — Returns profile with attributes, address, phone GetAuditLogsQuery — Input: (FromDate?, ToDate?, EventTypeId?, ActorId?, ResourceType?, Skip, Take) — Filtered audit logs GetComplianceReportsQuery — Input: (ReportTypeId?, Take) — Returns compliance reports GetComplianceReportByIdQuery — Input: (ReportId) — Returns report with violations GetUnresolvedViolationsQuery — Returns all unresolved violations GetActivePrivilegedAccessQuery — Input: (UserId) — Returns active PAM grants


Domain Model

ApplicationUser (extends IdentityUser<Guid>, IAggregateRoot)

  • Private Fields: _firstName, _lastName, _status (UserStatus), _createdAt, _lastLoginAt
  • Public Getters: FirstName, LastName, FullName, Status, StatusId, CreatedAt, LastLoginAt, DomainEvents
  • Behavior Methods: UpdateProfile(firstName, lastName), RecordLogin(), Lock(until?), Unlock(), Activate(), Disable()
  • Domain Events: UserRegisteredDomainEvent, UserLoggedInDomainEvent
  • Statuses: Active(1), Locked(2), Disabled(3), PendingVerification(4)

ApplicationRole (extends IdentityRole<Guid>, IAggregateRoot)

  • Private Fields: _description, _createdAt, _isSystemRole
  • Behavior Methods: Update(name, description?)
  • Domain Events: RoleAssignedDomainEvent
  • System Roles (seeded): User, PremiumUser, Merchant, MerchantStaff, MerchantAdmin, Admin, SuperAdmin, Support

Organization (Entity, IAggregateRoot)

  • Private Fields: _name, _slug, _description, _parentOrganizationId, _status, _settings, _createdAt, _updatedAt
  • Behavior Methods: UpdateInfo(name, description), UpdateSlug(slug), SetParent(parentId?), UpdateSettings(settings), Activate(), Suspend(), Archive()
  • Domain Events: OrganizationCreatedEvent(Id, Name, Slug), OrganizationUpdatedEvent(Id, Name)
  • Statuses: Active(1), Suspended(2), PendingApproval(3), Archived(4)
  • Owned Entity: OrganizationSettings (AllowUserRegistration, RequireEmailVerification, Require2FA, MaxUsersLimit, CustomDomain, SessionTimeoutMinutes)

Group (Entity, IAggregateRoot)

  • Private Fields: _name, _description, _organizationId, _createdAt, _updatedAt, _isDeleted
  • Collections: _members (GroupMember), _permissions (GroupPermission)
  • Behavior Methods: Update(name, description), AddMember(userId, role?, addedBy?), RemoveMember(userId), ChangeMemberRole(userId, newRole), AddPermission(permission, resource?, grantedBy?), RemovePermission(permission, resource?), HasPermission(permission, resource?), Delete(), Restore()
  • Domain Events: GroupCreatedEvent, MemberAddedToGroupEvent, MemberRemovedFromGroupEvent
  • Soft Delete: Query filter on IsDeleted

GroupMember (Entity)

  • Fields: GroupId, UserId, Role (GroupRole), JoinedAt, AddedByUserId
  • GroupRole: Member(1), Admin(2), Owner(3)

GroupPermission (Entity)

  • Fields: GroupId, Permission, Resource, GrantedAt, GrantedByUserId

UserProfile (Entity)

  • Private Fields: _userId, _bio, _avatarUrl, _phoneNumber (PhoneNumber VO), _address (Address VO), _timezone, _locale, _dateOfBirth, _createdAt, _updatedAt
  • Collections: _attributes (ProfileAttribute)
  • Behavior Methods: UpdateBasicInfo(bio, timezone, locale), SetAvatar(url), SetPhoneNumber(phone), SetAddress(address), SetDateOfBirth(dob), SetAttribute(key, value) (String/Number/Boolean/Date/Json overloads), RemoveAttribute(key), GetAttributeValue(key), GetAge()
  • Value Objects: PhoneNumber (CountryCode, NationalNumber), Address (Street, Street2, City, State, PostalCode, Country)

ProfileAttribute (Entity)

  • Fields: UserProfileId, Key, Value, ValueType (ProfileAttributeType)
  • ProfileAttributeType: String(1), Number(2), Boolean(3), Date(4), Json(5)

IdentityVerification (Entity, IAggregateRoot)

  • Private Fields: _userId, _type (VerificationType), _status (VerificationStatus), _verificationData, _verificationCodeHash, _requestedAt, _verifiedAt, _expiresAt, _attemptCount, _rejectionReason, _metadata
  • Factory Methods: CreatePhoneVerification(userId, phoneNumber) returns (Verification, OTP), CreateEmailVerification(userId, email) returns (Verification, OTP), CreateDocumentVerification(userId, documentUrl, documentType?)
  • Behavior Methods: VerifyCode(code) (max 5 attempts), MarkAsVerified(), MarkAsRejected(reason), MarkAsExpired(), Cancel()
  • Domain Events: VerificationRequestedEvent, VerificationCompletedEvent
  • OTP: 6-digit, SHA256 hashed, 10-min expiry
  • VerificationType: Email(1), Phone(2), Document(3), Identity(4)
  • VerificationStatus: Pending(1), InProgress(2), Verified(3), Rejected(4), Expired(5), Cancelled(6)

AccessRequest (Entity, IAggregateRoot)

  • Private Fields: _requesterId, _resourceType, _resourceId, _requestedPermission, _justification, _status, _priority, _createdAt, _submittedAt, _resolvedAt, _expiresAt
  • Collections: _approvers (AccessRequestApprover)
  • Factory: Create(requesterId, resourceType, resourceId, permission, justification?, priority?)
  • Behavior Methods: AddApprover(userId), Submit(expirationDays=7), Approve(approverId, comments?), Reject(approverId, reason?), Cancel(), Expire(), UpdateJustification(justification)
  • Domain Events: AccessRequestCreatedEvent, AccessRequestSubmittedEvent, AccessRequestApprovedEvent, AccessRequestRejectedEvent
  • Status Flow: Draft -> Pending -> Approved/Rejected/Expired/Cancelled
  • Priority: Low(1), Medium(2), High(3), Critical(4)

AccessReview (Entity, IAggregateRoot)

  • Private Fields: _name, _description, _ownerId, _scope, _status, _createdAt, _startedAt, _dueDate, _completedAt
  • Collections: _items (AccessReviewItem)
  • Behavior Methods: AddItem(userId, resourceType, resourceId, permission), Start(), ReviewItem(itemId, reviewerUserId, certify, comments?), Complete(), Cancel()
  • Domain Events: AccessReviewCreatedEvent, AccessReviewStartedEvent, AccessReviewCompletedEvent
  • Status Flow: Draft -> Active -> Completed/Cancelled
  • ReviewDecision: Pending, Certify, Revoke

PrivilegedAccessGrant (Entity, IAggregateRoot)

  • Private Fields: _userId, _roleId, _resourceScope, _reason, _grantedByUserId, _status, _createdAt, _startsAt, _expiresAt, _revokedAt, _revokedByUserId, _revocationReason
  • Factory: Create(userId, roleId, resourceScope, reason?, grantedByUserId, durationMinutes=60) (5-480 min), CreateScheduled(...) for future activation
  • Behavior Methods: Activate(), Revoke(revokedByUserId, reason?), Expire(), Extend(additionalMinutes, extendedByUserId) (5-240 min)
  • Domain Events: PrivilegedAccessGrantedEvent, PrivilegedAccessRevokedEvent
  • Status: Pending, Active, Expired, Revoked

AuditLog (Entity, IAggregateRoot)

  • Fields: EventType, ActorId, ActorEmail, ResourceType, ResourceId, Action, Details, IpAddress, UserAgent, Success, Timestamp
  • Factory: Create(eventType, resourceType, ...), LoginEvent(...), AccessGrantedEvent(...)
  • AuditEventType: Login(1), LoginFailed(2), Logout(3), PasswordChanged(4), PasswordReset(5), UserCreated(6), UserUpdated(7), UserDeleted(8), RoleAssigned(9), RoleRemoved(10), AccessGranted(11), AccessRevoked(12), PolicyViolation(13), MfaEnabled(14), MfaDisabled(15)

ComplianceReport (Entity, IAggregateRoot)

  • Fields: Name, ReportType, Status, GeneratedByUserId, CreatedAt, CompletedAt, Summary, TotalChecks, PassedChecks, FailedChecks
  • Collections: _violations (ComplianceViolation)
  • Behavior Methods: StartGenerating(), SetResults(totalChecks, passedChecks, summary?), AddViolation(rule, severity, description, remediation?), Complete(), Fail(reason?)
  • Domain Events: ComplianceReportCreatedEvent, ComplianceReportCompletedEvent
  • ComplianceReportType: GDPR, SOC2, HIPAA, ISO27001, PCI_DSS, Custom
  • ComplianceReportStatus: Pending, Generating, Completed, Failed
  • ViolationSeverity: Low, Medium, High, Critical

Database Schema

Identity Tables (ASP.NET Core Identity, custom table names)

Table Columns Notes
users id (Guid PK), email, normalized_email, username, normalized_username, password_hash, security_stamp, concurrency_stamp, phone_number, phone_number_confirmed, two_factor_enabled, lockout_end, lockout_enabled, access_failed_count, email_confirmed, first_name (max 100), last_name (max 100), created_at, last_login_at, status_id (FK) Custom fields via private field mapping
roles id (Guid PK), name, normalized_name, concurrency_stamp, description, created_at, is_system_role Custom fields added
user_roles user_id, role_id Join table
user_claims id, user_id, claim_type, claim_value User claims
user_logins login_provider, provider_key, provider_display_name, user_id External logins
user_tokens user_id, login_provider, name, value User tokens
role_claims id, role_id, claim_type, claim_value Role claims
user_statuses id (int PK), name (max 50) Seeded: Active(1), Locked(2), Disabled(3), PendingVerification(4)

Organizations

Table Columns Indexes
organizations id (Guid PK), name (max 200), slug (max 100), description (max 1000), parent_organization_id (FK self), status_id (int), settings_allow_user_registration, settings_require_email_verification, settings_require_2fa, settings_max_users_limit, settings_custom_domain (max 255), settings_session_timeout_minutes, created_at, updated_at UNIQUE ix_organizations_slug; FK fk_organizations_parent (Restrict delete)
organization_statuses id (int PK), name (max 50) Seeded: Active(1), Suspended(2), PendingApproval(3), Archived(4)

Groups

Table Columns Indexes
groups id (Guid PK), name (max 200), description (max 1000), organization_id (Guid), is_deleted (bool, default false), created_at, updated_at ix_groups_organization_id; Query filter: is_deleted = false
group_members id (Guid PK), group_id (Guid FK), user_id (Guid), role_id (int), joined_at, added_by_user_id (Guid?) UNIQUE ix_group_members_group_user; ix_group_members_user_id
group_permissions id (Guid PK), group_id (Guid FK), permission (max 100), resource (max 500), granted_at, granted_by_user_id (Guid?) UNIQUE ix_group_permissions_unique (group_id, permission, resource)
group_roles id (int PK), name (max 50) Seeded: Member(1), Admin(2), Owner(3)

User Profiles

Table Columns Indexes
user_profiles id (Guid PK), user_id (Guid), bio (max 2000), avatar_url (max 500), timezone (max 50), locale (max 10), date_of_birth, phone_country_code (max 5), phone_national_number (max 15), address_street (max 200), address_street2 (max 200), address_city (max 100), address_state (max 100), address_postal_code (max 20), address_country (max 2, ISO 3166-1), created_at, updated_at UNIQUE ix_user_profiles_user_id
profile_attributes id (Guid PK), user_profile_id (Guid FK Cascade), key (max 100), value (max 4000), value_type_id (int), created_at, updated_at UNIQUE ix_profile_attributes_profile_key (user_profile_id, key)
profile_attribute_types id (int PK), name (max 50) Seeded: String(1), Number(2), Boolean(3), Date(4), Json(5)

Identity Verification

Table Columns Indexes
identity_verifications id (Guid PK), user_id (Guid), type_id (int), status_id (int), verification_data (max 1000), verification_code_hash (max 100), requested_at, verified_at, expires_at, attempt_count (default 0), rejection_reason (max 500), metadata (jsonb) ix_identity_verifications_user_id; ix_identity_verifications_user_type_status
verification_types id (int PK), name (max 50) Seeded: Email(1), Phone(2), Document(3), Identity(4)
verification_statuses id (int PK), name (max 50) Seeded: Pending(1), InProgress(2), Verified(3), Rejected(4), Expired(5), Cancelled(6)

Access Requests

Table Columns Indexes
AccessRequests Id (Guid PK), RequesterId (Guid), ResourceType (max 100), ResourceId (Guid), RequestedPermission (max 100), Justification (max 2000), StatusId (int, value conversion), PriorityId (int, value conversion), CreatedAt, SubmittedAt, ResolvedAt, ExpiresAt IX_AccessRequests_RequesterId; IX_AccessRequests_Resource (ResourceType, ResourceId)
AccessRequestApprovers Id (Guid PK), RequestId (Guid FK Cascade), UserId (Guid), ApprovalOrder (int), StatusId (int, value conversion), RespondedAt, Comments (max 1000) IX_AccessRequestApprovers_UserId

Access Reviews

Table Columns Indexes
AccessReviews Id (Guid PK), Name (max 200), Description (max 1000), OwnerId (Guid), Scope (max 200), StatusId (int), CreatedAt, StartedAt, DueDate, CompletedAt IX_AccessReviews_OwnerId
AccessReviewItems Id (Guid PK), ReviewId (Guid FK Cascade), UserId (Guid), ResourceType (max 100), ResourceId (Guid), Permission (max 100), DecisionId (int), ReviewedByUserId (Guid?), ReviewedAt, Comments (max 500) IX_AccessReviewItems_UserId

Privileged Access

Table Columns Indexes
PrivilegedAccessGrants Id (Guid PK), UserId (Guid), RoleId (Guid), ResourceScope (max 200), Reason (max 500), GrantedByUserId (Guid), StatusId (int), CreatedAt, StartsAt, ExpiresAt, RevokedAt, RevokedByUserId (Guid?), RevocationReason (max 500) IX_PrivilegedAccessGrants_UserId; IX_PrivilegedAccessGrants_Active (UserId, RoleId, Status)

Audit & Compliance

Table Columns Indexes
AuditLogs Id (Guid PK), EventTypeId (int), ActorId (Guid?), ActorEmail (max 256), ResourceType (max 100), ResourceId (Guid?), Action (max 200), Details (max 4000), IpAddress (max 45), UserAgent (max 500), Success (bool), Timestamp IX_AuditLogs_Timestamp; IX_AuditLogs_ActorId; IX_AuditLogs_EventType; IX_AuditLogs_Resource
ComplianceReports Id (Guid PK), Name (max 200), ReportTypeId (int), StatusId (int), GeneratedByUserId (Guid), CreatedAt, CompletedAt, Summary (max 4000), TotalChecks, PassedChecks, FailedChecks IX_ComplianceReports_CreatedAt
ComplianceViolations Id (Guid PK), ReportId (Guid FK Cascade), Rule (max 200), SeverityId (int), Description (max 1000), Remediation (max 1000), AffectedResource (max 200), Resolved (bool), ResolvedAt

Dependencies

External Services Called

  • Redis (167.114.174.113:6379) — Session/token caching via ICacheService / RedisCacheService
  • SMTP (smtp.mailgun.org:587) — Email verification via IEmailService / SmtpEmailService
  • Google OAuth — External login (ClientId/Secret from config, currently empty)
  • Facebook OAuth — External login (AppId/Secret from config, currently empty)

Infrastructure Services

  • ITwoFactorService / TotpTwoFactorService — TOTP-based 2FA with QR code generation
  • ISocialLoginService / SocialLoginService — Social login orchestration
  • IVerificationOtpDispatcher — Dispatches OTP codes (registered in Program.cs)
  • ICacheService / RedisCacheService — Redis-backed caching

Domain Events Raised (potential cross-service integration points)

  • UserRegisteredDomainEvent — User registered
  • UserLoggedInDomainEvent — User logged in
  • RoleAssignedDomainEvent — Role assigned to user
  • OrganizationCreatedEvent / OrganizationUpdatedEvent
  • GroupCreatedEvent / MemberAddedToGroupEvent / MemberRemovedFromGroupEvent
  • VerificationRequestedEvent / VerificationCompletedEvent
  • AccessRequestCreatedEvent / AccessRequestSubmittedEvent / AccessRequestApprovedEvent / AccessRequestRejectedEvent
  • AccessReviewCreatedEvent / AccessReviewStartedEvent / AccessReviewCompletedEvent
  • PrivilegedAccessGrantedEvent / PrivilegedAccessRevokedEvent
  • ComplianceReportCreatedEvent / ComplianceReportCompletedEvent

Repositories (10 total)

Interface Implementation Aggregate
IUserRepository UserRepository ApplicationUser
IRoleRepository RoleRepository ApplicationRole
IOrganizationRepository OrganizationRepository Organization
IGroupRepository GroupRepository Group
IIdentityVerificationRepository IdentityVerificationRepository IdentityVerification
IAccessRequestRepository AccessRequestRepository AccessRequest
IAccessReviewRepository AccessReviewRepository AccessReview
IPrivilegedAccessRepository PrivilegedAccessRepository PrivilegedAccessGrant
IAuditLogRepository AuditLogRepository AuditLog
IComplianceReportRepository ComplianceReportRepository ComplianceReport

IdentityServer Configuration

OAuth2 Clients

Client ID Grant Type Token Lifetime Notes
web-app Authorization Code + PKCE 15 min (access), 7 days (refresh sliding) Requires client secret
mobile-app Authorization Code + PKCE 15 min (access), 7 days (refresh sliding) Public client, no secret
service-client Client Credentials Default Service-to-service
password-client Resource Owner Password 8 hours (access), 7 days (refresh) Legacy/admin sessions
swagger-ui Resource Owner Password 1 hour (access), 1 day (refresh) Testing convenience

Scopes

  • openid, profile, email, roles (identity resources)
  • api (API scope with role, email, name claims)

API Resources

  • iam-api, goodgo-api, goodgo-services — all use api scope with role/email/name claims

MediatR Pipeline Behaviors

  1. LoggingBehavior — Request/response logging with execution time
  2. ValidatorBehavior — FluentValidation in pipeline (throws ValidationException)
  3. TransactionBehavior — Auto-wraps Commands in DB transactions (skips Queries)

Authorization Policies

Policy Requirement
RequireAdmin User must have "Admin" or "SuperAdmin" role
RequireSuperAdmin User must have "SuperAdmin" role
RequireAuditor User must have "Admin", "SuperAdmin", or "Auditor" role
OwnerOrAdmin User must be the resource owner or have Admin role
LocalApi Bearer-authenticated user with "openid" scope

Error Handling (ProblemDetails RFC 7807)

Exception HTTP Status
FluentValidation.ValidationException 400 Bad Request
DuplicateResourceException 409 Conflict
EntityNotFoundException 404 Not Found
AuthenticationFailedException 401 Unauthorized
BusinessRuleException 422 Unprocessable Entity

Configuration

appsettings.json Key Settings

ConnectionStrings:DefaultConnection  — PostgreSQL (Neon cloud)
Redis:Host/Port/Password/Database    — Remote Redis instance
Jwt:Secret/Issuer/Audience           — JWT config (15 min access, 7 day refresh)
Email:SmtpServer/SmtpPort/SmtpLogin  — Mailgun SMTP (smtp.mailgun.org:587)
TwoFactor:Issuer/CodeLength/Validity — TOTP config (6 digits, 30s validity)
SocialLogin:Google/Facebook          — OAuth credentials (currently empty)
IdentityServer:Authority/IssuerUri   — http://localhost:5001 / http://iam-service

Identity Password Policy

  • Minimum 8 characters
  • Require digit, lowercase, uppercase, non-alphanumeric
  • Lockout: 15 min after 5 failed attempts

Data Seeding (on startup)

System roles seeded via DataSeeder.SeedRolesAsync(): User, PremiumUser, Merchant, MerchantStaff, MerchantAdmin, Admin, SuperAdmin, Support

Migrations (7 total)

  1. 20260112104902_InitialCreate — Users, roles, Identity tables
  2. 20260114074030_Phase2_IdentityManagement — Organizations, groups, verifications
  3. 20260114074751_AddProfileAttributes — Profile attributes system
  4. 20260114084200_Phase3A_AccessRequests — Access request workflow
  5. 20260114084754_Phase3A_AccessRequests_ValueConversion — Status/Priority value converters
  6. 20260114085640_Phase3B_AccessReviewsAndPAM — Access reviews, privileged access
  7. 20260114091114_Phase4A_AuditAndCompliance — Audit logs, compliance reports

Auto-Migration

EF Core migrations are auto-applied on startup in Development environment.