xperise B2B Business Account, Membership, Role and Authentication Specification
Document type: Software Specification / Single Source of Truth
Feature domain: Business Account Management, User Membership, Authentication and Two-Factor Authentication
Version: 1.0 Draft
Status: Ready for product review and technical estimation
Confirmed Inputs and Assumptions
Confirmed Requirements
| ID | Requirement |
|---|---|
| CI-001 | xperise supports B2B businesses. |
| CI-002 | Each account is identified using an email address or phone number. |
| CI-003 | Users can log in with email/password, phone/password, or Google login. |
| CI-004 | A business can be created by an account that does not currently belong to a business. |
| CI-005 | Each business must have a unique tax code in the system. |
| CI-006 | Each business has a full name and a short name; the short name is the primary display name in most interfaces. |
| CI-007 | A business can have departments and users. |
| CI-008 | Each business user is associated with an account in the system. |
| CI-009 | A business admin can add a user using the user's name and email address. |
| CI-010 | A business user has one of three roles: ADMIN, ACCOUNTANT, or NORMAL_USER. |
| CI-011 | A business may enable two-factor authentication as an additional security layer. |
| CI-012 | When business-level 2FA is enabled, login requires primary authentication plus a valid 2FA challenge. |
| CI-013 | 2FA codes may be delivered by email, phone number, or ZaloOA. |
| CI-014 | An account can join only one active business at a time. |
| CI-015 | An account has only one role within its active business. |
| CI-016 | Passwords must contain at least 12 characters, including uppercase letters, lowercase letters, and numbers. |
Assumptions Requiring Confirmation
| ID | Assumption |
|---|---|
| AS-001 | An account may exist without belonging to any business. |
| AS-002 | A business creator automatically becomes the first ADMIN of the business. |
| AS-003 | An admin adds an existing or new account through an invitation workflow rather than immediately activating membership. |
| AS-004 | A user belongs to zero or one department within a business. |
| AS-005 | Google-login users must still complete business-level 2FA when the business requires it. |
| AS-006 | Phone numbers must be verified before being usable for login or phone-based 2FA. |
| AS-007 | Email addresses must be verified before invitation acceptance or email-based 2FA. |
| AS-008 | ZaloOA delivery is available only after a compatible Zalo identity is linked and verified. |
| AS-009 | A removed user becomes eligible to join or create another business when no active membership remains. |
| AS-010 | Business short names do not need to be unique; tax codes must be unique. |
1. Feature Overview
1.1 Purpose
This feature enables xperise to manage B2B customer businesses and their users securely. It covers account identity, business registration, business membership, departments, role-based access, optional mandatory two-factor authentication, and user onboarding through invitation.
1.2 In Scope
| Area | Included capability |
|---|---|
| Account | Account identity based on email address and/or phone number |
| Authentication | Login by email/password, phone/password, and Google authentication |
| Password Security | Password validation and secure storage rules |
| Business | Business creation, tax code uniqueness, full name and short name |
| Membership | One active business membership per account |
| Department | Create, update, deactivate and assign users to departments |
| User Administration | Invite users by name and email |
| Roles | Admin, Accountant and Normal User |
| Security | Enable/disable mandatory business 2FA; supported delivery channels |
| Auditability | Capture administration and security events |
1.3 Out of Scope for This Version
| ID | Excluded capability |
|---|---|
| OS-001 | Multiple active businesses for one account |
| OS-002 | Custom roles or custom permissions |
| OS-003 | Multiple roles per user in one business |
| OS-004 | Enterprise SSO providers other than Google |
| OS-005 | Organization hierarchy above business level |
| OS-006 | Department-level permissions |
| OS-007 | Billing, subscription and payment management |
| OS-008 | Bulk user import |
2. Business Goal
2.1 Primary Goals
| ID | Goal |
|---|---|
| BG-001 | Allow a company to create and manage its organization securely on xperise. |
| BG-002 | Ensure each business is uniquely identifiable by tax code. |
| BG-003 | Allow business administrators to onboard users efficiently. |
| BG-004 | Enforce clear role-based permissions. |
| BG-005 | Protect business access using optional mandatory 2FA. |
| BG-006 | Maintain reliable account-to-business membership integrity. |
2.2 Success Criteria
| ID | Success criterion |
|---|---|
| SC-001 | A new business can be registered without duplicate tax codes. |
| SC-002 | An invited user can activate membership and access the correct business. |
| SC-003 | A user cannot belong to more than one active business. |
| SC-004 | Admins can manage departments and membership without engineering support. |
| SC-005 | When 2FA is enabled, no member can access the business without completing the challenge. |
| SC-006 | Security and membership actions are auditable. |
3. User Roles
3.1 Actor Definitions
| Actor | Description |
|---|---|
| Guest | A person who has not authenticated. |
| Account Without Business | Authenticated account with no active business membership. |
| Invited User | An account or email address with a pending business invitation. |
| Business User | Account with active membership in one business. |
| Business Admin | Business user with role ADMIN. |
| Accountant | Business user with role ACCOUNTANT. |
| Normal User | Business user with role NORMAL_USER. |
3.2 Business Role Definitions
| Role code | Display name | Description |
|---|---|---|
ADMIN | Admin | Manages business profile, departments, users, roles and business security settings. |
ACCOUNTANT | Accountant | Performs accounting-related work outside this feature; receives no administrative rights under this specification. |
NORMAL_USER | Normal User | Uses the platform within the assigned business without administrative permissions. |
3.3 Role Constraints
| ID | Rule |
|---|---|
| ROLE-001 | Each active business membership must contain exactly one role. |
| ROLE-002 | The business creator must be assigned role ADMIN automatically. |
| ROLE-003 | A business must always have at least one active ADMIN. |
| ROLE-004 | An admin must not remove or downgrade the final remaining active admin. |
| ROLE-005 | ACCOUNTANT and NORMAL_USER must not manage users, departments, roles or business security settings. |
4. User Stories
4.1 Account and Authentication
| ID | User story |
|---|---|
| US-AUTH-001 | As a user, I want to log in using email and password so that I can access my account. |
| US-AUTH-002 | As a user, I want to log in using phone number and password so that I can access my account without email. |
| US-AUTH-003 | As a user, I want to log in using Google so that authentication is faster and easier. |
| US-AUTH-004 | As a user in a secured business, I want to receive and enter a 2FA code so that unauthorized access is prevented. |
| US-AUTH-005 | As a user, I want to choose an eligible 2FA delivery channel so that I can receive my code reliably. |
4.2 Business, Department and Membership
| ID | User story |
|---|---|
| US-BIZ-001 | As an account without a business, I want to create a business workspace so my organization can use xperise. |
| US-BIZ-002 | As a business creator, I want duplicate tax code registration prevented. |
| US-BIZ-003 | As a business creator, I want to become admin automatically so I can complete setup. |
| US-DEPT-001 | As an admin, I want to create, update and deactivate departments so users can be organized accurately. |
| US-DEPT-002 | As an admin, I want to assign users to departments. |
| US-USER-001 | As an admin, I want to invite a user by name and email so they can join my business. |
| US-USER-002 | As an invited user, I want to accept an invitation and access the correct business. |
| US-USER-003 | As an admin, I want to set a user's role so access matches responsibility. |
| US-USER-004 | As an admin, I want to remove a user so former staff cannot access business information. |
| US-USER-005 | As an admin, I want to see invitation statuses so I can track onboarding. |
4.3 Security
| ID | User story |
|---|---|
| US-SEC-001 | As an admin, I want to enable mandatory 2FA for the business so all members have stronger access protection. |
| US-SEC-002 | As an admin, I want to identify users who cannot complete selected 2FA methods before rollout. |
| US-SEC-003 | As a user, I want a secure fallback delivery method, where permitted, so channel failures do not permanently block me. |
5. Functional Requirements
5.1 Account and Password Requirements
| ID | Requirement |
|---|---|
| FR-ACC-001 | The system shall create an account record for each registered user. |
| FR-ACC-002 | An account shall support an email address, a phone number, or both. |
| FR-ACC-003 | Normalized email addresses shall be unique across accounts. |
| FR-ACC-004 | Normalized phone numbers shall be unique across accounts. |
| FR-ACC-005 | A verified identifier shall not be associated with multiple accounts. |
| FR-ACC-006 | The system shall maintain verification status separately for email and phone number. |
| FR-ACC-007 | An account may exist without an active business membership. |
| FR-PWD-001 | A password shall contain at least 12 characters. |
| FR-PWD-002 | A password shall contain at least one uppercase letter, one lowercase letter and one number. |
| FR-PWD-003 | Passwords shall never be stored in plain text. |
| FR-PWD-004 | Password rules shall apply during password creation and reset. |
| FR-PWD-005 | Google-authenticated users need not create a password unless password login is enabled later. |
Normalization example: User@Company.com and user@company.com must be treated as the same email address after normalization.
5.2 Authentication Requirements
| ID | Requirement |
|---|---|
| FR-AUTH-001 | The system shall support login using email address and password. |
| FR-AUTH-002 | The system shall support login using phone number and password. |
| FR-AUTH-003 | The system shall support login using Google authentication. |
| FR-AUTH-004 | Invalid identifiers or password validation failures shall reject login. |
| FR-AUTH-005 | After primary authentication succeeds, the system shall determine whether the active business requires 2FA. |
| FR-AUTH-006 | When 2FA is required, the system shall not issue an authenticated business session until 2FA validation succeeds. |
| FR-AUTH-007 | Google authentication shall not bypass mandatory business 2FA. |
| FR-AUTH-008 | Successful and failed authentication attempts shall be auditable. |
5.3 Business Requirements
| ID | Requirement |
|---|---|
| FR-BIZ-001 | Only an authenticated account with no active business membership may create a new business. |
| FR-BIZ-002 | Business creation shall require a tax code, full name and short name. |
| FR-BIZ-003 | Tax code shall be normalized before uniqueness validation. |
| FR-BIZ-004 | Normalized tax code shall be unique across all eligible non-deleted businesses. |
| FR-BIZ-005 | On successful creation, the creator shall automatically receive active membership with role ADMIN. |
| FR-BIZ-006 | Short name shall be the default display name in operational UI. |
| FR-BIZ-007 | Creation timestamp and creator account shall be captured. |
| FR-BIZ-008 | Business creation and creator membership creation shall be atomic. |
5.4 Department Requirements
| ID | Requirement |
|---|---|
| FR-DEPT-001 | An admin shall be able to create a department in their active business. |
| FR-DEPT-002 | A department shall require a department name. |
| FR-DEPT-003 | Normalized department names shall be unique within the same business. |
| FR-DEPT-004 | An admin shall be able to rename an active department. |
| FR-DEPT-005 | An admin shall be able to deactivate a department. |
| FR-DEPT-006 | A deactivated department shall not be available for new assignments. |
| FR-DEPT-007 | Handling users assigned to a deactivated department shall follow the decision documented in OQ-002/OQ-003. |
| FR-DEPT-008 | Under AS-004, a user shall be assigned to at most one department. |
5.5 Invitation and Membership Requirements
| ID | Requirement |
|---|---|
| FR-INV-001 | An admin shall be able to invite a business user using full name and email address. |
| FR-INV-002 | An invitation shall assign one role: ADMIN, ACCOUNTANT, or NORMAL_USER. |
| FR-INV-003 | Department assignment during invitation shall be optional unless later made mandatory. |
| FR-INV-004 | An invitation shall be rejected when its email belongs to an account with an active business membership. |
| FR-INV-005 | The system shall not create duplicate pending invitations for the same business and email. |
| FR-INV-006 | An invitation notification shall be sent to the provided email. |
| FR-INV-007 | Every invitation shall have an expiry time. |
| FR-INV-008 | An invited user shall not receive business access before acceptance and required authentication steps complete. |
| FR-INV-009 | An admin shall be able to resend or revoke a pending invitation. |
| FR-MEM-001 | An account shall have at most one active business membership. |
| FR-MEM-002 | An active membership shall link one account to one business and exactly one role. |
| FR-MEM-003 | A membership may link a user to one department. |
| FR-MEM-004 | Invitation acceptance shall revalidate that no conflicting active membership exists. |
| FR-MEM-005 | An admin shall be able to deactivate a member except when this violates the minimum-admin rule. |
| FR-MEM-006 | A deactivated member shall lose business access immediately. |
| FR-MEM-007 | Historical actions of a removed member shall be preserved for audit and reporting. |
5.6 Role Management Requirements
| ID | Requirement |
|---|---|
| FR-RBAC-001 | An admin shall be able to change another active member's role. |
| FR-RBAC-002 | Supported roles shall be ADMIN, ACCOUNTANT, and NORMAL_USER. |
| FR-RBAC-003 | A member shall have exactly one role at a time. |
| FR-RBAC-004 | The system shall prevent removal or downgrade of the last active admin. |
| FR-RBAC-005 | Role changes shall take effect immediately for subsequent authorization checks. |
| FR-RBAC-006 | Role changes shall be audited with previous role, new role and actor. |
5.7 Two-Factor Authentication Requirements
| ID | Requirement |
|---|---|
| FR-2FA-001 | An admin shall be able to enable mandatory 2FA at business level. |
| FR-2FA-002 | When mandatory 2FA is enabled, every active business user shall complete 2FA after successful primary authentication. |
| FR-2FA-003 | Supported channels shall include EMAIL, PHONE, and ZALO_OA. |
| FR-2FA-004 | A user may use only a channel linked to a verified eligible destination. |
| FR-2FA-005 | The system shall generate a time-limited, one-time code per challenge. |
| FR-2FA-006 | A 2FA code shall be usable only once. |
| FR-2FA-007 | Expired, invalid or previously used codes shall be rejected. |
| FR-2FA-008 | The system shall limit resend frequency and failed verification attempts. |
| FR-2FA-009 | The verification UI shall mask email addresses and phone numbers. |
| FR-2FA-010 | Enablement, disablement, delivery requests and verification results shall be audited. |
| FR-2FA-011 | If no valid delivery channel exists, business access shall be blocked and a defined recovery path shall be displayed. |
| FR-2FA-012 | Channel readiness shall be validated or explicitly warned about before mandatory 2FA is enabled. |
5.8 Audit Requirements
| ID | Requirement |
|---|---|
| FR-AUD-001 | Business creation events shall be recorded. |
| FR-AUD-002 | Department creation, update and deactivation events shall be recorded. |
| FR-AUD-003 | Invitation creation, resend, acceptance, expiry and revocation events shall be recorded. |
| FR-AUD-004 | Role changes and membership deactivation events shall be recorded. |
| FR-AUD-005 | Business 2FA setting changes shall be recorded. |
| FR-AUD-006 | Audit records shall identify actor, business, action, timestamp and affected entity. |
| FR-AUD-007 | Audit records shall not store raw passwords, invitation tokens or OTP codes. |
6. Business Rules
6.1 Account and Business Rules
| ID | Rule |
|---|---|
| BR-ACC-001 | Each account is internally identified by account_id, regardless of login method. |
| BR-ACC-002 | An email address or phone number may belong to only one account after normalization. |
| BR-ACC-003 | An unverified destination shall not be used for security-sensitive delivery. |
| BR-ACC-004 | A Google identity shall be linked to exactly one xperise account. |
| BR-BIZ-001 | Each business must have one unique normalized tax code. |
| BR-BIZ-002 | A business must have a full legal name and short display name. |
| BR-BIZ-003 | The short name is used by default in application operational screens. |
| BR-BIZ-004 | The full name is used where legal or formal identification is required. |
| BR-BIZ-005 | An account with active membership cannot create another business. |
| BR-BIZ-006 | The business creator automatically becomes its first active admin. |
6.2 Membership and Department Rules
| ID | Rule |
|---|---|
| BR-MEM-001 | An account may have only one active membership at any moment. |
| BR-MEM-002 | Each membership must have exactly one role. |
| BR-MEM-003 | An invitation does not grant access until accepted. |
| BR-MEM-004 | Invitation acceptance must fail if the account has joined another business since the invitation was issued. |
| BR-MEM-005 | Membership deactivation revokes access but does not delete historical activities. |
| BR-MEM-006 | A business must retain at least one active admin. |
| BR-DEPT-001 | A department belongs to exactly one business. |
| BR-DEPT-002 | A normalized department name cannot be duplicated within one business. |
| BR-DEPT-003 | Different businesses may use identical department names. |
| BR-DEPT-004 | Under current assumption, a user belongs to at most one active department. |
6.3 Security Rules
| ID | Rule |
|---|---|
| BR-SEC-001 | Mandatory business 2FA applies to every active member, including admins. |
| BR-SEC-002 | Primary authentication must succeed before a 2FA challenge is generated. |
| BR-SEC-003 | Google authentication is primary authentication only and does not replace required business 2FA. |
| BR-SEC-004 | A 2FA code expires after its configured duration and cannot be reused. |
| BR-SEC-005 | Repeated invalid attempts shall invalidate or temporarily block the challenge according to security configuration. |
| BR-SEC-006 | No admin shall be able to silently bypass 2FA for another member. |
7. Workflow
7.1 New Account Creates a Business
- The user registers or logs into an account.
- The system checks whether the account has active business membership.
- If membership exists, business creation is rejected.
- If no membership exists, the user enters tax code, full name and short name.
- The system validates required fields and normalized tax code uniqueness.
- The system creates the business and active
ADMINmembership within one transaction. - The system records an audit event and opens the new business workspace.
7.2 Admin Invites a User
- Admin opens User Management and selects Add User.
- Admin enters full name and email, selects role and optionally department.
- The system validates permissions, email format, membership conflicts, duplicate invitations and department status.
- The system creates a
PENDINGinvitation with token and expiry. - The system sends an invitation email and records an audit event.
- The invitation is shown in the admin invitation list.
7.3 Invited User Accepts Invitation
- The user opens an invitation link.
- The system validates that the token is valid, pending and unexpired.
- The user signs in or registers and verifies required identity fields.
- The system revalidates that the account has no active membership.
- The system activates membership with the invitation's role and eligible department assignment.
- The invitation is marked
ACCEPTED; an audit event is recorded. - The user receives access subject to the business's authentication/security settings.
7.4 Login With Mandatory 2FA
- The user completes email/password, phone/password or Google authentication.
- The system checks the active business's
two_fa_requiredsetting. - If false, the system issues a session.
- If true, the system identifies verified eligible 2FA channels.
- The user selects a channel, or the default eligible channel is applied.
- The system sends a time-limited one-time code.
- The user enters the code.
- Only a correct, unused and unexpired code grants an authenticated session.
- Failed and successful 2FA events are audited.
7.5 Admin Enables Mandatory 2FA
- Admin opens Security Settings.
- Admin selects Enable Mandatory 2FA.
- The system calculates active-user readiness by verified delivery channel.
- If all members are ready, admin confirms enablement.
- If any member is not ready, the system blocks enablement or presents an explicit impact confirmation according to the final product decision.
- The system records the changed setting in the audit log.
- 2FA is enforced from the next applicable login or session renewal.
8. Status / State Transition Rules
8.1 Business Status
| Status | Description |
|---|---|
ACTIVE | Business can be accessed by active members. |
INACTIVE | Business exists but access is suspended. |
DELETED | Soft-deleted record retained for audit or retention requirements. |
| From | Action | To | Allowed actor | Validation |
|---|---|---|---|---|
| None | Create business | ACTIVE | Account without business | Tax code must be unique. |
ACTIVE | Suspend business | INACTIVE | Platform administration, outside business-admin scope | All business access blocked. |
INACTIVE | Reactivate business | ACTIVE | Platform administration | Membership rules remain valid. |
ACTIVE / INACTIVE | Soft delete | DELETED | Platform administration | Tax-code reuse policy requires decision. |
8.2 Membership Status
| Status | Description |
|---|---|
ACTIVE | Member can access business according to role and security rules. |
DEACTIVATED | Membership is not valid for access. |
| From | Action | To | Allowed actor | Validation |
|---|---|---|---|---|
| None | Business creation completes | ACTIVE | System | Creator receives ADMIN. |
| None | Accept valid invitation | ACTIVE | Invited user | No other active membership. |
ACTIVE | Remove user | DEACTIVATED | Admin | Must not remove final admin. |
DEACTIVATED | Reactivate user | ACTIVE | Admin | Account has no other active membership. |
8.3 Invitation Status
| Status | Description |
|---|---|
PENDING | Invitation may be accepted. |
ACCEPTED | Invitation was accepted and membership activated. |
EXPIRED | Expiry occurred before acceptance. |
REVOKED | Invitation was cancelled by an admin. |
| From | Action | To | Validation |
|---|---|---|---|
| None | Send invitation | PENDING | No conflicting active membership or duplicate pending invitation. |
PENDING | Accept invitation | ACCEPTED | Token valid; membership eligibility rechecked. |
PENDING | Expiry reached | EXPIRED | Automatic transition. |
PENDING | Revoke invitation | REVOKED | No membership is created. |
EXPIRED / REVOKED | Resend invitation | New PENDING invitation | New token and expiry are required. |
8.4 Department Status
| Status | Description |
|---|---|
ACTIVE | Department can receive user assignments. |
INACTIVE | Department cannot receive new assignments. |
| From | Action | To | Validation |
|---|---|---|---|
| None | Create department | ACTIVE | Name unique within business. |
ACTIVE | Deactivate department | INACTIVE | Assigned-user handling requires product decision. |
INACTIVE | Reactivate department | ACTIVE | Name remains unique. |
8.5 2FA Challenge Status
| Status | Description |
|---|---|
CREATED | Challenge generated but delivery not confirmed. |
SENT | Code delivered through selected channel. |
VERIFIED | Correct code successfully validated. |
FAILED | Challenge exceeded permitted attempts. |
EXPIRED | Challenge expiry time reached. |
CANCELLED | Challenge was replaced or invalidated. |
| From | Action | To | Rule |
|---|---|---|---|
| None | Generate challenge | CREATED | Primary authentication must have succeeded. |
CREATED | Deliver code | SENT | Channel must be eligible. |
SENT | Enter valid code | VERIFIED | Code must be correct, unused and unexpired. |
SENT | Expiry reached | EXPIRED | No session is issued. |
SENT | Maximum invalid attempts reached | FAILED | A new challenge is required. |
SENT | Send replacement code | CANCELLED | Previous code becomes unusable. |
9. Data Model Requirements
9.1 Core Entities
Account
| Field | Type | Required | Rules |
|---|---|---|---|
account_id | UUID | Yes | Primary key. |
full_name | String | Yes | User display name. |
email | String | Conditional | Required if phone is absent or invitation is email-based. |
normalized_email | String | Conditional | Unique when present. |
email_verified_at | Timestamp | No | Null until verified. |
phone_number | String | Conditional | Required if email is absent. |
normalized_phone_number | String | Conditional | Unique when present. |
phone_verified_at | Timestamp | No | Null until verified. |
password_hash | String | Conditional | Required for password login only. |
status | Enum | Yes | ACTIVE, LOCKED, DEACTIVATED. |
created_at / updated_at | Timestamp | Yes | System generated. |
Authentication Identity
| Field | Type | Required | Rules |
|---|---|---|---|
auth_identity_id | UUID | Yes | Primary key. |
account_id | UUID | Yes | References account. |
provider | Enum | Yes | PASSWORD, GOOGLE. |
provider_subject_id | String | Conditional | Required for Google; unique within provider. |
created_at | Timestamp | Yes | System generated. |
Business
| Field | Type | Required | Rules |
|---|---|---|---|
business_id | UUID | Yes | Primary key. |
tax_code | String | Yes | Original value entered. |
normalized_tax_code | String | Yes | Globally unique for eligible records. |
full_name | String | Yes | Legal business name. |
short_name | String | Yes | Primary display name. |
status | Enum | Yes | ACTIVE, INACTIVE, DELETED. |
two_fa_required | Boolean | Yes | Default false. |
created_by_account_id | UUID | Yes | References creator. |
created_at / updated_at | Timestamp | Yes | System generated. |
Department
| Field | Type | Required | Rules |
|---|---|---|---|
department_id | UUID | Yes | Primary key. |
business_id | UUID | Yes | References business. |
name | String | Yes | Display name. |
normalized_name | String | Yes | Unique within business. |
status | Enum | Yes | ACTIVE, INACTIVE. |
created_by_account_id | UUID | Yes | References admin creator. |
created_at / updated_at | Timestamp | Yes | System generated. |
Business Membership
| Field | Type | Required | Rules |
|---|---|---|---|
membership_id | UUID | Yes | Primary key. |
business_id | UUID | Yes | References business. |
account_id | UUID | Yes | References account. |
role | Enum | Yes | ADMIN, ACCOUNTANT, NORMAL_USER. |
department_id | UUID | No | Active department in same business. |
status | Enum | Yes | ACTIVE, DEACTIVATED. |
activated_at | Timestamp | Conditional | Required when active. |
deactivated_at | Timestamp | No | Set when removed. |
created_at / updated_at | Timestamp | Yes | System generated. |
User Invitation
| Field | Type | Required | Rules |
|---|---|---|---|
invitation_id | UUID | Yes | Primary key. |
business_id | UUID | Yes | References inviting business. |
invited_name | String | Yes | Name entered by admin. |
invited_email / normalized_invited_email | String | Yes | Notification address and lookup value. |
role | Enum | Yes | Assigned on acceptance. |
department_id | UUID | No | Optional active department. |
status | Enum | Yes | PENDING, ACCEPTED, EXPIRED, REVOKED. |
invitation_token_hash | String | Yes | Raw token shall not be stored. |
expires_at | Timestamp | Yes | Required. |
accepted_by_account_id | UUID | No | Set on acceptance. |
invited_by_account_id | UUID | Yes | Admin actor. |
created_at / updated_at | Timestamp | Yes | System generated. |
Two-Factor Destination and Challenge
| Entity | Key fields | Rules |
|---|---|---|
TWO_FA_DESTINATION | destination_id, account_id, channel, destination_reference, verified_at, status, is_default | Channels: EMAIL, PHONE, ZALO_OA; only verified active destinations usable. |
TWO_FA_CHALLENGE | challenge_id, account_id, business_id, channel, destination_id, code_hash, status, attempt_count, expires_at, verified_at | Raw code shall not be stored; one-time usage and expiry enforced. |
Audit Log
| Field | Type | Required | Rules |
|---|---|---|---|
audit_log_id | UUID | Yes | Primary key. |
business_id | UUID | Conditional | Required for business-related actions. |
actor_account_id | UUID | Conditional | Null only for automated events. |
action_type | String/Enum | Yes | Example: BUSINESS_CREATED, USER_INVITED, ROLE_CHANGED. |
target_type / target_id | String/UUID | Yes | Affected entity. |
previous_value / new_value | JSON | No | Sensitive information excluded. |
created_at | Timestamp | Yes | Immutable timestamp. |
9.2 Data Constraints
| ID | Constraint |
|---|---|
| DM-001 | At most one ACTIVE membership shall exist per account_id. |
| DM-002 | A membership's department_id, when present, must belong to the same business. |
| DM-003 | An active business must retain at least one active membership where role = ADMIN. |
| DM-004 | Only one active pending invitation may exist for a business and normalized invited email. |
| DM-005 | Unique constraints shall protect email, phone and tax code against concurrent duplicate writes. |
10. API Requirements
10.1 API Principles
| ID | Requirement |
|---|---|
| API-GEN-001 | Authenticated endpoints shall validate the account session. |
| API-GEN-002 | Business-scoped endpoints shall validate active membership in the requested business. |
| API-GEN-003 | Admin-only endpoints shall validate role = ADMIN. |
| API-GEN-004 | APIs shall return stable machine-readable error codes and user-readable messages. |
| API-GEN-005 | Login, OTP resend and OTP verification endpoints shall be rate limited. |
| API-GEN-006 | Passwords, raw invitation tokens and raw OTP values shall never be returned. |
10.2 Endpoint Catalogue
| Method | Endpoint | Purpose | Authorization |
|---|---|---|---|
POST | /api/v1/auth/register | Register account with email/phone and password | Public |
POST | /api/v1/auth/login | Login using email/phone and password | Public |
POST | /api/v1/auth/google | Login using Google | Public |
POST | /api/v1/auth/2fa/challenges | Send 2FA code | Temporary authenticated challenge |
POST | /api/v1/auth/2fa/challenges/{challengeId}/verify | Verify 2FA code | Temporary authenticated challenge |
POST | /api/v1/businesses | Create business | Authenticated account without business |
GET | /api/v1/businesses/current | Get current business | Active member |
PATCH | /api/v1/businesses/{businessId} | Edit basic business information | Admin |
GET | /api/v1/businesses/{businessId}/departments | List departments | Active member |
POST | /api/v1/businesses/{businessId}/departments | Create department | Admin |
PATCH | /api/v1/businesses/{businessId}/departments/{departmentId} | Edit department | Admin |
POST | /api/v1/businesses/{businessId}/departments/{departmentId}/deactivate | Deactivate department | Admin |
GET | /api/v1/businesses/{businessId}/invitations | List invitations | Admin |
POST | /api/v1/businesses/{businessId}/invitations | Send invitation | Admin |
POST | /api/v1/businesses/{businessId}/invitations/{invitationId}/resend | Resend invitation | Admin |
POST | /api/v1/businesses/{businessId}/invitations/{invitationId}/revoke | Revoke invitation | Admin |
POST | /api/v1/invitations/{token}/accept | Accept invitation | Invited authenticated user |
GET | /api/v1/businesses/{businessId}/members | List members | Admin |
PATCH | /api/v1/businesses/{businessId}/members/{membershipId} | Update role/department | Admin |
POST | /api/v1/businesses/{businessId}/members/{membershipId}/deactivate | Remove member access | Admin |
GET | /api/v1/businesses/{businessId}/security-settings | View security settings | Admin |
GET | /api/v1/businesses/{businessId}/security-settings/2fa-readiness | View 2FA readiness | Admin |
PATCH | /api/v1/businesses/{businessId}/security-settings | Enable/disable mandatory 2FA | Admin |
10.3 Example Requests and Responses
Create Business
{
"taxCode": "0312345678",
"fullName": "ABC Technology Joint Stock Company",
"shortName": "ABC Tech"
}
{
"businessId": "<uuid>",
"taxCode": "0312345678",
"fullName": "ABC Technology Joint Stock Company",
"shortName": "ABC Tech",
"status": "ACTIVE",
"currentUserRole": "ADMIN"
}
Login Response When 2FA Is Required
{
"authenticationStatus": "TWO_FA_REQUIRED",
"temporaryAuthToken": "<temporary-token>",
"availableChannels": [
{
"channel": "EMAIL",
"maskedDestination": "a***@company.com"
}
]
}
Invite User
{
"fullName": "Tran Thi Mai",
"email": "mai@company.com",
"role": "ACCOUNTANT",
"departmentId": "<uuid>"
}
Standard Error Response
{
"errorCode": "BUSINESS_TAX_CODE_ALREADY_EXISTS",
"message": "A business with this tax code already exists.",
"fieldErrors": [
{
"field": "taxCode",
"message": "Tax code is already registered."
}
],
"traceId": "<trace-id>"
}
11. Frontend UI Requirements
11.1 General UI Rules
| ID | Requirement |
|---|---|
| UI-GEN-001 | Business short name shall appear in primary navigation and business context headers. |
| UI-GEN-002 | Business full name and tax code shall appear in profile details. |
| UI-GEN-003 | Actions not permitted for the current role shall be hidden or disabled according to product design standards. |
| UI-GEN-004 | UI visibility rules shall not replace backend authorization checks. |
| UI-GEN-005 | Form errors shall identify invalid fields with actionable messages. |
11.2 Required Screens and Forms
| Screen | Required content |
|---|---|
| Login | Email/password, phone/password and Google login paths; show/hide password; error state. |
| 2FA Verification | Eligible channel selector, masked destination, OTP input, expiry/resend status, validation errors. |
| Create Business | Tax code, full name, short name; duplicate/error validation. |
| Business Profile | Short name, full name, tax code; editable name fields for admin only. |
| Department Management | Department name, status, optional user count, create/edit/deactivate actions for admin. |
| User Management | User name, email, department, role, membership status and admin actions. |
| Invitation Management | Invited user, email, role, department, status, sent/expiry dates, resend/revoke actions. |
| Security Settings | Mandatory 2FA toggle, supported channels, readiness summary, impacted users and confirmation dialog. |
11.3 Form and Interaction Rules
| ID | Requirement |
|---|---|
| UI-AUTH-001 | Login shall allow switching between email and phone login. |
| UI-AUTH-002 | A successful primary login requiring 2FA shall redirect to verification instead of the business workspace. |
| UI-BIZ-001 | Create Business shall be available only to accounts without active membership. |
| UI-DEPT-001 | Inactive departments shall not be selectable for new assignments. |
| UI-USER-001 | Add User form shall require full name, email and role; department remains optional until confirmed otherwise. |
| UI-SEC-001 | Only admins shall be able to change business-level 2FA settings. |
| UI-SEC-002 | Before mandatory 2FA is enabled, user readiness shall be displayed. |
| UI-SEC-003 | Verification UI shall never reveal complete phone numbers or email addresses. |
12. Permission Matrix
| Action | Account Without Business | Admin | Accountant | Normal User |
|---|---|---|---|---|
| Create a business | Yes | No | No | No |
| View own business basic information | N/A | Yes | Yes | Yes |
| Update business full name or short name | No | Yes | No | No |
| Update tax code through standard edit | No | No | No | No |
| Create, rename or deactivate department | No | Yes | No | No |
| View department list | No | Yes | Yes | Yes, where operationally required |
| Invite, resend or revoke user invitation | No | Yes | No | No |
| View full business user list | No | Yes | To confirm | To confirm |
| Change role or department assignment | No | Yes | No | No |
| Deactivate or reactivate member | No | Yes, except final admin | No | No |
| Enable or disable business 2FA | No | Yes | No | No |
| View 2FA readiness report | No | Yes | No | No |
13. Edge Cases
| ID | Scenario | Expected result |
|---|---|---|
| EC-AUTH-001 | User enters email using different casing. | Same account identified after normalization. |
| EC-AUTH-002 | User registers with an existing email or normalized phone number. | Registration rejected. |
| EC-AUTH-003 | Google email matches an existing password account. | Apply confirmed account-linking policy; no silent duplicate account. |
| EC-AUTH-004 | Google auth succeeds but business requires 2FA. | 2FA is required before access. |
| EC-BIZ-001 | Active business member attempts to create another business. | Creation rejected. |
| EC-BIZ-002 | Two users submit the same tax code simultaneously. | Database uniqueness permits only one creation. |
| EC-BIZ-003 | Business is created but admin membership creation fails. | Entire transaction rolls back. |
| EC-INV-001 | Admin invites a current same-business member. | No duplicate invitation is created. |
| EC-INV-002 | Admin invites an account active in another business. | Invitation rejected. |
| EC-INV-003 | Two admins invite the same email simultaneously. | Only one active pending invitation exists. |
| EC-INV-004 | A user has invitations from two businesses. | First accepted membership succeeds; second acceptance fails. |
| EC-INV-005 | Invitation expires or is revoked before acceptance. | Acceptance denied. |
| EC-INV-006 | Assigned department becomes inactive before acceptance. | Acceptance follows pending product decision on reassignment/blocking. |
| EC-DEPT-001 | Finance is created when finance already exists. | Duplicate rejected after normalization. |
| EC-DEPT-002 | Admin assigns a user to another business's department or inactive department. | Request rejected. |
| EC-RBAC-001 | Final admin downgrades or deactivates themselves. | Request rejected. |
| EC-RBAC-002 | Concurrent admin changes would leave no admin. | Transactional validation prevents invalid final state. |
| EC-2FA-001 | 2FA is enabled but a user has no verified channel. | Follow readiness policy; do not silently allow access. |
| EC-2FA-002 | User repeatedly requests OTP or enters invalid codes. | Resend/attempt rate limits apply. |
| EC-2FA-003 | Correct OTP is entered after expiry or after prior usage. | Verification rejected. |
| EC-2FA-004 | A replacement OTP is sent. | Previous OTP is invalidated. |
| EC-2FA-005 | Delivery provider fails but another channel is eligible. | User may use eligible fallback where allowed. |
| EC-2FA-006 | User changes email or phone destination. | New destination must be verified before 2FA use. |
14. Error Handling
14.1 Principles
| ID | Rule |
|---|---|
| ERR-001 | Errors shall contain a stable machine-readable code. |
| ERR-002 | Errors shall contain a user-readable message. |
| ERR-003 | Authentication errors shall not expose sensitive account existence details where applicable. |
| ERR-004 | Validation errors shall identify invalid input fields. |
| ERR-005 | Security-related failures shall be auditable. |
| ERR-006 | Backend validation remains authoritative regardless of frontend validation. |
14.2 Error Code Catalogue
| Error code | HTTP status | Trigger | User-facing message |
|---|---|---|---|
ACCOUNT_EMAIL_ALREADY_EXISTS | 409 | Email already belongs to an account | This email address is already registered. |
ACCOUNT_PHONE_ALREADY_EXISTS | 409 | Phone already belongs to an account | This phone number is already registered. |
PASSWORD_POLICY_NOT_MET | 400 | Password fails validation | Password does not meet security requirements. |
INVALID_LOGIN_CREDENTIALS | 401 | Invalid identifier or password | Email/phone number or password is incorrect. |
GOOGLE_AUTHENTICATION_FAILED | 401 | Google sign-in cannot be validated | Google sign-in failed. Please try again. |
TWO_FA_REQUIRED | 202 | Primary login succeeded; OTP required | Verification is required to continue. |
TWO_FA_NO_ELIGIBLE_CHANNEL | 403 | No verified destination available | No verified verification method is available. Contact your administrator. |
TWO_FA_CODE_INVALID | 400 | OTP incorrect | Verification code is incorrect. |
TWO_FA_CODE_EXPIRED | 400 | OTP expired | Verification code has expired. Request a new code. |
TWO_FA_ATTEMPTS_EXCEEDED | 429 | Too many failed OTP entries | Too many failed attempts. Request a new code later. |
TWO_FA_RESEND_TOO_SOON | 429 | Resend cooldown active | Please wait before requesting another code. |
BUSINESS_ACTIVE_MEMBERSHIP_EXISTS | 409 | User tries to create or join another business | Your account already belongs to an active business. |
BUSINESS_TAX_CODE_ALREADY_EXISTS | 409 | Duplicate tax code | A business with this tax code already exists. |
DEPARTMENT_NAME_ALREADY_EXISTS | 409 | Duplicate department name | A department with this name already exists. |
DEPARTMENT_INACTIVE | 409 | Assignment to inactive department | This department is inactive and cannot receive users. |
INVITATION_ALREADY_PENDING | 409 | Duplicate pending invitation | An active invitation has already been sent to this email. |
INVITATION_EXPIRED | 410 | Token expired | This invitation has expired. Please request a new invitation. |
INVITATION_REVOKED | 410 | Token revoked | This invitation is no longer valid. |
LAST_ADMIN_REQUIRED | 409 | Change would leave business with no admin | The business must have at least one active admin. |
FORBIDDEN_ROLE_PERMISSION | 403 | Role lacks permission | You do not have permission to perform this action. |
15. Acceptance Criteria
| ID | Given | When | Then |
|---|---|---|---|
| AC-AUTH-001 | A unique email/phone and valid password are supplied | Account is registered | User can log in with the registered identifier and password. |
| AC-AUTH-002 | Password fails length or composition rules | Registration/reset is submitted | Request is rejected with PASSWORD_POLICY_NOT_MET. |
| AC-AUTH-003 | Valid Google login for a business without 2FA | Authentication succeeds | Authenticated session is issued. |
| AC-AUTH-004 | Valid Google login for a business requiring 2FA | Authentication succeeds | User must complete 2FA before access. |
| AC-BIZ-001 | Authenticated account has no active business | Unique tax code, full name and short name are submitted | Active business is created. |
| AC-BIZ-002 | Business creation succeeds | Transaction completes | Creator receives active ADMIN membership. |
| AC-BIZ-003 | Normalized tax code already exists | Creation is submitted | Request is rejected. |
| AC-BIZ-004 | User already has active membership | Creation is attempted | Request is rejected. |
| AC-DEPT-001 | Admin is authenticated | Unique department is submitted | Active department appears in list. |
| AC-DEPT-002 | Inactive department exists | User assignment is submitted for it | Assignment is rejected. |
| AC-INV-001 | Admin enters eligible user name, email and role | Invitation is submitted | PENDING invitation is created and sent. |
| AC-INV-002 | Valid pending invitation and user has no active business | User accepts | Membership becomes ACTIVE with invited role. |
| AC-INV-003 | User already joined another business | User accepts invitation | Acceptance is rejected. |
| AC-MEM-001 | Membership is deactivated | User requests business access | Access is denied immediately. |
| AC-RBAC-001 | Business has two active admins | One admin downgrades the other | Change succeeds and is audited. |
| AC-RBAC-002 | Business has only one active admin | That admin is downgraded or removed | Request is rejected with LAST_ADMIN_REQUIRED. |
| AC-2FA-001 | Business 2FA is disabled | User completes primary auth | Access may be granted without OTP. |
| AC-2FA-002 | Business 2FA is enabled and eligible channel exists | User logs in | User must validate OTP before access. |
| AC-2FA-003 | Valid unused unexpired OTP exists | Correct code is submitted | Session is issued; challenge becomes VERIFIED. |
| AC-2FA-004 | OTP is expired or already used | Code is submitted | Access is rejected. |
| AC-2FA-005 | No eligible channel exists in 2FA-required business | User authenticates | Business access is blocked and recovery guidance shown. |
16. Open Questions
16.1 Product Decisions
| ID | Question | Impact |
|---|---|---|
| OQ-001 | Can a user belong to multiple departments, or only one? | Data model, UI and reporting; current assumption is one maximum. |
| OQ-002 | Is department assignment mandatory for each business user? | Invitation and membership validation. |
| OQ-003 | What happens to users assigned to a deactivated department? | Department deactivation flow. |
| OQ-004 | Should business short name be unique? | Registration and display ambiguity. |
| OQ-005 | Can tax code be changed after registration or reused after deletion? | Verification and uniqueness policy. |
| OQ-006 | What is the invitation expiry duration? | Security and onboarding UX. |
| OQ-007 | Can a deactivated member be reactivated directly, or must they be re-invited? | Membership lifecycle. |
| OQ-008 | Should Accountants or Normal Users view the business user directory? | Privacy and permissions. |
16.2 Security and Technical Decisions
| ID | Question | Impact |
|---|---|---|
| OQ-SEC-001 | What are OTP length, expiry duration, resend cooldown and failed-attempt limit? | API, UI and security configuration. |
| OQ-SEC-002 | Can an admin enable 2FA if some users have no verified delivery channel? | Rollout and lockout handling. |
| OQ-SEC-003 | Does disabling 2FA require admin reauthentication or 2FA confirmation? | Protection from compromised admin sessions. |
| OQ-SEC-004 | How is ZaloOA identity linked and verified? | Integration and data model. |
| OQ-SEC-005 | Does phone-based 2FA use SMS, voice call or another delivery service? | Integration scope. |
| OQ-SEC-006 | Are recovery codes or backup methods supported? | Lockout prevention. |
| OQ-TECH-001 | What tax-code normalization rules apply per supported country? | International data integrity. |
| OQ-TECH-002 | Does initial rollout support only Vietnamese businesses or multiple countries? | Tax code and phone validation. |
| OQ-TECH-003 | Which email, phone and ZaloOA service providers will be used? | Delivery implementation. |
| OQ-TECH-004 | What authentication/session/token strategy is required? | Security architecture. |
| OQ-TECH-005 | What audit retention period is required? | Compliance and storage. |
17. Test Case Generation Notes
17.1 Test Case Principles
| ID | Instruction |
|---|---|
| TGN-001 | Generate tests by requirement ID so each rule is traceable. |
| TGN-002 | Include positive, negative, permission, concurrency, security and state-transition tests. |
| TGN-003 | Separate API, frontend, integration and end-to-end test suites. |
| TGN-004 | Every acceptance criterion shall map to at least one end-to-end test. |
| TGN-005 | Every error code shall map to at least one negative test. |
| TGN-006 | Every role restriction shall be tested against every unauthorized role. |
| TGN-007 | Security tests shall confirm sensitive values are absent from API responses, logs and UI. |
| TGN-008 | Tests dependent on unresolved decisions shall be labelled BLOCKED_PENDING_PRODUCT_DECISION. |
17.2 Required Test Suites
| Suite | Required coverage |
|---|---|
| Account and Login | Registration by email/phone, duplicate identifiers, password policy, login methods, Google identity linking and normalization. |
| Business | Creation eligibility, required fields, tax code uniqueness, concurrency, atomic creator-admin membership and short-name display. |
| Department | Create/update/deactivate permissions, normalized name duplicates, active assignment validation and cross-business rejection. |
| Invitation and Membership | Invite lifecycle, acceptance, expiry/revocation, conflicts across businesses, membership removal/reactivation and historical preservation. |
| Roles and Permissions | All roles, authorized and unauthorized actions, last-admin protection, immediate permission updates and frontend visibility. |
| Two-Factor Authentication | Enablement readiness, all delivery channels, valid/invalid/expired/reused OTP, resend limits, fallback failure, masking and audit. |
17.3 Suggested Test Case Template
### TC-{MODULE}-{NUMBER}: {Test Case Name}
**Requirement References:** FR-XXX-001, BR-XXX-001, AC-XXX-001
**Test Type:** API / UI / Integration / End-to-End / Security
**Priority:** Critical / High / Medium / Low
**Preconditions**
1. ...
**Test Data**
- ...
**Steps**
1. ...
2. ...
**Expected Results**
1. ...
2. ...
**Error Code Expected, if applicable**
- `ERROR_CODE`
**Automation Candidate**
- Yes / No
**Notes**
- ...
17.4 High-Priority Automation Candidates
| Priority | Test scenario | Reason |
|---|---|---|
| Critical | Prevent duplicate tax code registration | Core data integrity. |
| Critical | Prevent account from joining more than one active business | Core membership integrity. |
| Critical | Automatically assign creator as admin | Required initial administration access. |
| Critical | Prevent removal or downgrade of last active admin | Prevent business lockout. |
| Critical | Require 2FA when business enables it | Security-critical. |
| Critical | Reject expired or reused OTP | Security-critical. |
| High | Invite and accept eligible user | Main onboarding workflow. |
| High | Authorize every admin API by role | Access-control integrity. |
| High | Validate department assignments | Organization-data integrity. |
| High | Require mandatory 2FA after Google authentication | Security consistency. |
Recommended Initial Implementation Decisions
| ID | Recommendation |
|---|---|
| REC-001 | Keep one active business membership per account in version 1. |
| REC-002 | Keep one role per membership and one department per user in version 1. |
| REC-003 | Make the business creator the first admin automatically. |
| REC-004 | Use invitation-based onboarding rather than directly adding activated users. |
| REC-005 | Do not allow tax code changes through standard business editing; define a separate verified process later. |
| REC-006 | Require at least one verified 2FA destination for every active user before mandatory business 2FA can be enabled. |
| REC-007 | Treat Google login as primary authentication only; mandatory business 2FA remains required. |
| REC-008 | Use deactivation/soft deletion for business, membership and department lifecycle to preserve auditability. |
| REC-009 | Record all administrative and security-related actions in an immutable audit log. |
Requirement Traceability Summary
| Feature area | Functional requirements | Business rules | Acceptance criteria |
|---|---|---|---|
| Account and Password | FR-ACC-001 to FR-ACC-007; FR-PWD-001 to FR-PWD-005 | BR-ACC-001 to BR-ACC-004 | AC-AUTH-001 to AC-AUTH-004 |
| Authentication | FR-AUTH-001 to FR-AUTH-008 | BR-SEC-001 to BR-SEC-006 | AC-AUTH-001 to AC-AUTH-004 |
| Business Creation | FR-BIZ-001 to FR-BIZ-008 | BR-BIZ-001 to BR-BIZ-006 | AC-BIZ-001 to AC-BIZ-004 |
| Department | FR-DEPT-001 to FR-DEPT-008 | BR-DEPT-001 to BR-DEPT-004 | AC-DEPT-001 to AC-DEPT-002 |
| Invitation and Membership | FR-INV-001 to FR-INV-009; FR-MEM-001 to FR-MEM-007 | BR-MEM-001 to BR-MEM-006 | AC-INV-001 to AC-INV-003; AC-MEM-001 |
| Role Management | FR-RBAC-001 to FR-RBAC-006 | ROLE-001 to ROLE-005 | AC-RBAC-001 to AC-RBAC-002 |
| Two-Factor Authentication | FR-2FA-001 to FR-2FA-012 | BR-SEC-001 to BR-SEC-006 | AC-2FA-001 to AC-2FA-005 |
| Audit | FR-AUD-001 to FR-AUD-007 | Applies across modules | Validated through administration and security test suites |