Bite 30C.2 — Tenant‐Driven Authentication Provisioning & Account Recovery - RodrigoMattosoSilveira/EnterpriseRemoteSystems GitHub Wiki
Bite 30C.2 — Tenant-Driven Authentication Provisioning & Account Recovery
Manual Verification Checklist Before Promotion
Automated prerequisite
- All migrations applied successfully.
-
make local-checkpasses. - Full Playwright E2E suite passes.
The manual verification below concentrates on the two architectural changes introduced by Bite 30C.2:
- Tenant-driven Authentication provisioning
- Authentication Account reactivation/recovery
1. Test Setup
Prepare the following identities and data before beginning.
Tenants
Use two separate tenants:
Tenant A
Tenant B
For your current local data, default can be one of them.
Record:
Tenant A:
Tenant B:
Administrators
Have credentials for:
Application Administrator
Tenant A Administrator
Tenant B Administrator
The Tenant Administrators must be genuine tenant-scoped administrators rather than bootstrap-admin.
Test People
Prepare at least these disposable People:
Person 1 — active Membership in Tenant A only
Person 2 — active Memberships in Tenant A and Tenant B
Person 3 — Person exists, but no active Membership in Tenant A
Person 4 — active Tenant A Membership and existing active Authentication Account
Person 5 — active Tenant A Membership and inactive Authentication Account
Person 2 is especially important because it verifies global Account reuse without cross-tenant information disclosure.
Part A — Tenant-Driven Authentication Provisioning
2. Tenant Administrator Can See Authentication Status
Sign in as
Tenant A Administrator
Procedure
- Open People.
- Open Person 1.
- Locate the Authentication section.
Expected
Before authentication is enabled, the page should show approximately:
Authentication
Status: Not enabled
[Enable Authentication]
or equivalent wording.
Verify:
- Authentication status is visible from the Person/Membership context.
- The Tenant Administrator does not need to use
/admin/authentication. - The page does not expose another tenant or another tenant's Actor.
- No Application-Administrator workflow is required for normal provisioning.
3. Enable Authentication for a Person With an Active Membership
Sign in as
Tenant A Administrator
Preconditions
Person 1:
- has an active Person–Tenant Membership in Tenant A;
- does not yet have authentication enabled for Tenant A.
Procedure
- Open Person 1.
- Click Enable Authentication.
- Complete the provisioning operation.
Expected
- Operation succeeds.
- Authentication becomes enabled for Tenant A.
- Exactly one Tenant-A Actor exists for the Account.
- The Actor belongs only to Tenant A.
- No Application Administrator intervention was required.
- No raw database/500 error occurs.
If temporary credentials are generated/displayed by the current implementation:
- They are presented clearly.
- They correspond to the Authentication Account rather than the Actor.
4. Provisioned Person Can Authenticate
Sign out
Open a private/incognito browser window.
Procedure
- Sign in using the Account provisioned in Test 3.
- Complete any currently required first-login/password-change flow.
- Allow the application to reach the user's permitted landing page.
Expected
- Authentication succeeds.
- The user is operating through the Tenant-A Actor.
- Tenant context is Tenant A.
- No Application-Administrator permissions appear.
- Existing Person authorization behavior still applies.
You can confirm the Actor using:
fetch("/api/v1/authz/current-actor", {
credentials: "include",
headers: {
"X-Tenant-ID": "<TENANT-A-ID>"
}
})
.then(r => r.json())
.then(console.log);
Expected:
scope = TENANT
tenantId = Tenant A
5. Person Without an Active Membership Cannot Be Provisioned
Sign in as
Tenant A Administrator
Preconditions
Person 3 exists globally but does not have an active Person–Tenant Membership in Tenant A.
Procedure
Open Person 3 or otherwise attempt the normal authentication-provisioning workflow.
Expected
One of these should occur:
- Enable Authentication is unavailable; or
- the server rejects the request cleanly.
And:
- No Authentication Account is improperly created for Tenant A.
- No Tenant-A Actor is created.
- No 500 error occurs.
- The response does not reveal memberships in other tenants.
Promotion blocker: authentication can be enabled without an active Person–Tenant Membership.
6. Non-Tenant-Administrator Cannot Enable Authentication
Sign in as
An ordinary Person or other non-TENANT_ADMIN tenant user.
Procedure
Attempt to access the Person authentication provisioning action.
Expected
- Action is not displayed, or
- server rejects it with 403.
- No Authentication Account or Actor is created.
- UI does not rely solely on hiding the button; backend authorization is enforced.
Promotion blocker: an ordinary tenant user can initiate provisioning.
7. Tenant Administrator Cannot Provision for Another Tenant
Sign in as
Tenant A Administrator
Procedure
Attempt to enable authentication for a Person whose Membership exists only in Tenant B.
Expected
- Operation is unavailable or rejected.
- No Tenant-B Actor is created by the Tenant-A Administrator.
- Tenant-A Administrator cannot discover that the Person belongs to Tenant B.
- No cross-tenant membership information is returned.
8. Existing Global Account Is Silently Reused in a Second Tenant
This is the most important provisioning test in 30C.2.
Preconditions
Person 2:
Global Person
├── active Membership in Tenant A
└── active Membership in Tenant B
Provision authentication first from Tenant A.
Tenant A procedure
Sign in as:
Tenant A Administrator
- Open Person 2.
- Enable Authentication.
- Record the Account login.
- Sign in once if needed to confirm the Account works.
- Record the Tenant-A Actor ID.
Then sign out of the administrator account.
Tenant B procedure
Sign in as:
Tenant B Administrator
- Find/open the same global Person through Tenant B's authorized Person/Membership workflow.
- Inspect Authentication status before enabling it.
Expected before enabling
Tenant B should see only something equivalent to:
Authentication
Status: Not enabled for this tenant
[Enable Authentication]
Verify:
- Tenant B is not told that an Authentication Account already exists.
- Tenant B is not told that authentication was enabled in Tenant A.
- Tenant A's name is nowhere exposed.
- Tenant A's Actor is nowhere exposed.
Now:
- Click Enable Authentication.
Expected after enabling
ERS should perform:
existing global Account
↓
reuse Account
↓
create Tenant-B Actor only
Verify:
- Operation succeeds.
- No second Authentication Account is created.
- Existing Account login remains unchanged.
- Existing password remains unchanged.
- Tenant-A Actor remains intact.
- [ x A new, distinct Tenant-B Actor exists.
- Tenant B receives no indication that reuse occurred because of another tenant.
Promotion blocker: second-tenant provisioning creates another Authentication Account.
9. One Account Works Through Both Tenant Actors
Use Person 2's Account.
Tenant A
Query:
fetch("/api/v1/authz/current-actor", {
credentials: "include",
headers: {
"X-Tenant-ID": "<TENANT-A-ID>"
}
})
.then(r => r.json())
.then(console.log);
Record:
Tenant-A Actor ID:
Tenant B
Run again with Tenant B:
fetch("/api/v1/authz/current-actor", {
credentials: "include",
headers: {
"X-Tenant-ID": "<TENANT-B-ID>"
}
})
.then(r => r.json())
.then(console.log);
Record:
Tenant-B Actor ID:
Expected
- Same Authentication Account is used.
- Tenant-A Actor and Tenant-B Actor IDs differ.
- Tenant A resolves only Tenant-A roles.
- Tenant B resolves only Tenant-B roles.
- No second login/account is needed.
- Tenant switching does not create Actors dynamically during normal requests.
10. Repeated Enable Authentication Is Idempotent
Sign in as
Tenant Administrator for a Person whose authentication is already enabled in that tenant.
Procedure
- Refresh the Person page.
- Revisit Authentication.
- Attempt the operation again if UI permits.
Expected
Preferably:
Authentication
Status: Enabled
and no Enable button.
If the endpoint can nevertheless be invoked:
- It must not create another Account.
- It must not create another Actor.
- Existing credentials remain unchanged.
- Existing Actor remains the same.
11. Application Administrator Is No Longer Required for Routine Onboarding
Procedure
Complete an ordinary Person → Membership → Enable Authentication flow using only:
Tenant Administrator
Expected
- Workflow completes without visiting
/admin/authentication. - No Application-Administrator operation is necessary.
-
/admin/authenticationremains available for global Account administration.
This verifies the operational responsibility change that motivated 30C.2.
Part B — Account Recovery
12. Deactivate a Disposable Authentication Account
Sign in as
Application Administrator
Preconditions
Use Person 5 or another disposable Account; not the Peson's Name and Email
Preferably the Account has two tenant Actors, because that verifies the Account-level nature of deactivation.
Procedure
- Open
/admin/authentication. - Find the disposable Account.
- Record its Actor bindings.
- Deactivate the Account.
Expected
- Account becomes inactive.
- Actor records remain attached.
- Memberships remain unchanged.
- Person remains unchanged.
13. Existing Session Is Invalidated by Deactivation
Before Test 12, keep the disposable Account signed in in a separate private browser.
After deactivation:
- Refresh a protected page.
- Attempt another protected request.
Expected
- Existing session can no longer continue normal authenticated access.
- User is returned to authentication/login handling.
- No Actor is deleted merely because the Account was deactivated.
14. Inactive Account Cannot Sign In
Procedure
Enter the Account's correct login and password.
Expected
Login should report approximately:
Your authentication account is inactive.
Request reactivation to regain access.
Verify:
- Authentication does not succeed.
- Reset a password is not offered as the solution to inactivity.
- Request reactivation is available.
- The message does not require the Person to know an Application Administrator.
15. Invalid Credentials Must Not Reveal Inactive Account Status
This is an important information-disclosure test.
Procedure
For the same inactive Account:
- Enter the correct login with an incorrect password.
Expected
- ERS behaves like an invalid-credentials attempt.
- It does not expose the reactivation workflow solely because the login identifier exists.
- It does not reveal Account inactivity before credentials are validated.
Promotion blocker: arbitrary users can determine which Accounts are inactive.
16. User Can Request Reactivation
Preconditions
Reach the inactive-account state using valid credentials.
Procedure
- Click Request reactivation.
Expected
- Request succeeds.
- Clear confirmation is displayed.
- Account remains inactive.
- Request does not itself reactivate the Account.
- Existing Actor bindings are unchanged.
17. Repeated Reactivation Requests Do Not Create Duplicates
Using the same inactive Account:
- Request reactivation.
- Repeat the request.
Expected
- There remains one logical pending request.
- User does not generate an unlimited stack of duplicate pending requests.
- Application Administrator sees one current pending recovery item.
18. Application Administrator Can See Pending Reactivation Request
Sign in as
Application Administrator
Procedure
- Open
/admin/authentication. - Locate the reactivation-request area/workbench.
- Find the Account from Test 16.
Expected
The request should contain appropriate Account-level/audit context, such as:
Account
request timestamp
request status
Verify:
- Request is visible.
- Account can be identified.
- Request status is pending.
- No tenant-private membership information is unnecessarily exposed in the recovery record.
19. Application Administrator Can Reject a Reactivation Request
Use a disposable inactive Account/request.
Procedure
- Open pending request.
- Reject it.
- Supply a reason if the UI supports/requires one.
Expected
- Request becomes rejected.
- Account remains inactive.
- Existing Actors remain intact.
- Existing credentials remain intact.
- The rejection does not delete the Account.
- Rejected request is not still shown as pending.
Then attempt login again:
- Account remains unable to authenticate.
20. User Can Request Reactivation Again After Rejection
If supported by the implemented lifecycle:
- Attempt valid login again.
- Request reactivation again.
Expected
- A new/current pending recovery request can be established.
- Previously rejected audit history is preserved.
- Rejection does not permanently corrupt the Account.
21. Application Administrator Can Approve Reactivation
Use another disposable pending request or the re-request from Test 20.
Procedure
- Open the pending request.
- Approve/reactivate it.
Expected
- Account becomes active.
- Request becomes approved/completed rather than pending.
- Same Account ID remains.
- Same login remains.
- Password is not silently replaced.
- Existing Actors remain attached.
- No duplicate Actor is created.
- No duplicate Authentication Account is created.
22. Reactivated Account Can Sign In Again
Procedure
In a fresh private window:
- Enter the same login.
- Enter the existing correct password.
Expected
- Authentication succeeds.
- The same Account is used.
- No unnecessary password reset is required unless separately mandated by current Account policy.
- The expected tenant Actor resolves correctly.
23. Reactivation Revokes Stale Sessions
This verifies the recovery security requirement.
Procedure
If you retained a session/token/browser context from before deactivation/reactivation:
- Try using that stale session after approval.
- Separately perform a fresh sign-in.
Expected
old session -> unusable
fresh sign-in -> works
Verify:
- Reactivation does not magically revive the old browser session.
- User must authenticate through a new/fresh session.
- Fresh login succeeds.
Promotion blocker: a stale pre-deactivation session becomes valid again merely because the Account is reactivated.
24. Reactivation Preserves Multiple Actor Bindings
Best performed using a disposable multi-tenant Account.
Before deactivation record:
Account ID:
Tenant-A Actor ID:
Tenant-B Actor ID:
Then:
deactivate
→ request reactivation
→ approve
After approval record them again.
Expected
same Account ID
same Tenant-A Actor ID
same Tenant-B Actor ID
Verify:
- No Actor disappeared.
- No Actor was replaced.
- No duplicate Actor was created.
- Account-to-Person binding remains unchanged.
25. Tenant Administrator Can Request Reactivation but Cannot Reactivate
Sign in as
Tenant A Administrator
Open a Person whose global Authentication Account is inactive.
Expected
Tenant-facing Authentication UI may offer approximately:
Request Authentication Reactivation
Procedure
- Submit the request.
Expected
- Request is created/escalated.
- Account remains inactive.
- Tenant Administrator cannot approve it.
- Tenant Administrator cannot directly change the global Account to active.
Promotion blocker: Tenant Administrator can directly reactivate a global Authentication Account.
26. Tenant Administrator Recovery UI Does Not Reveal Other Tenants
Use an inactive Account with Actors in Tenant A and Tenant B.
Sign in as
Tenant A Administrator
Procedure
Open that Person's Authentication section.
Expected
- Tenant B is not named.
- Tenant-B Actor is not displayed.
- Number of total Actors is not exposed if that reveals multi-tenancy.
- Account recovery can be requested using only Tenant A context.
Repeat from Tenant B if practical.
27. Ordinary User Cannot Approve Their Own Reactivation
Attempt to reach any Application-Administrator recovery endpoint/action as:
- the inactive user;
- an ordinary authenticated Person;
- a Tenant Administrator.
Expected
- Approval action is unavailable.
- Direct API attempt is rejected.
- Account remains inactive.
Only:
Application Administrator
should be able to perform global reactivation approval/rejection.
Part C — Regression and Integrity
28. Existing Active Authentication Accounts Still Work
Test at least:
single-tenant Person Account
multi-tenant Person Account
Tenant Administrator Account
Application Administrator Account
For each appropriate identity:
- Login succeeds.
- Correct Actor resolves.
- Existing permissions remain unchanged.
- No unnecessary recovery prompt appears.
29. Application Administrator Remains Global
While signed in as the Application Administrator:
fetch("/api/v1/authz/current-actor", {
credentials: "include",
headers: {
"X-Tenant-ID": "default"
}
})
.then(r => r.json())
.then(console.log);
Expected
Approximately:
scope = APPLICATION
roleCodes includes APPLICATION_ADMIN
Verify:
- 30C.2 did not attach an ordinary tenant Actor to the Application Administrator.
- Account-recovery administration remains application/global.
30. Browser Console and Network Check
During all tests, keep DevTools available.
Verify:
- No unexpected
500. - No React exceptions.
- No raw SQL errors.
- Expected authorization failures are clean
401/403. - Provisioning/recovery actions return useful structured errors when rejected.
- No request response leaks another tenant's ID/name merely because the Account exists there.
31. Backend Log Check
Review the backend log after testing.
Look for unexpected:
internal API error
panic
foreign key
UNIQUE constraint
authentication
reactivation
account actor
Expected
- No panic.
- No unexpected internal API errors.
- No duplicate Account constraint violations.
- No duplicate Actor-binding constraint violations.
- No recovery persistence failures.
32. Database Spot Check — Multi-Tenant Provisioning
For Person 2, verify the resulting model.
Conceptually it must be:
Global Person
|
Authentication Account
|
+-- Tenant-A Actor
|
+-- Tenant-B Actor
Not:
Global Person
|
+-- Account A -> Tenant A
|
+-- Account B -> Tenant B
Verify in the database using the existing Authentication tables:
- One global Authentication Account.
- One Account ↔ Person binding.
- Two distinct tenant Actor bindings.
- Each Actor belongs to the correct Tenant.
- No GLOBAL Actor is mixed into the ordinary Person Account.
33. Database Spot Check — Reactivation Request
After creating a pending request:
- Exactly one pending logical request exists for the Account.
- Request references the Account.
- Timestamp is recorded.
After rejection:
- Request reflects rejected/completed state.
- Account remains inactive.
After approval:
- Request reflects approved/completed state.
- Account is active.
- Actor rows are unchanged.
34. Foreign-Key Integrity
After all manual tests:
sqlite3 backend/data/app.db "
PRAGMA foreign_key_check;
"
Expected
No rows returned.
Promotion Gate
Bite 30C.2 is ready for promotion when all of the following are true:
- Tenant Administrator can enable authentication from the Person/Membership context.
- Active Person–Tenant Membership is required.
- Non-Tenant-Administrators cannot provision authentication.
- A Tenant Administrator can provision only within their own tenant.
- ERS decides whether to create or reuse the global Authentication Account.
- First-tenant provisioning creates an Account and tenant Actor correctly.
- Second-tenant provisioning silently reuses the same Account.
- Second-tenant provisioning creates only the second Tenant Actor.
- Tenant B cannot discover Tenant A through authentication status/provisioning.
- Repeated provisioning does not duplicate Accounts or Actors.
- Routine onboarding no longer requires an Application Administrator.
- Inactive Account cannot authenticate.
- Valid credentials for an inactive Account expose Request reactivation.
- Incorrect credentials do not reveal Account inactivity.
- Password reset is not presented as the solution for an inactive Account.
- User can create a reactivation request.
- Duplicate pending reactivation requests are prevented/coalesced.
- Tenant Administrator may request/escalate reactivation but cannot perform it.
- Application Administrator can review pending requests.
- Application Administrator can reject a request without activating the Account.
- Application Administrator can approve a request and reactivate the same Account.
- Reactivation preserves credentials and Actor bindings.
- Reactivation does not create duplicate Accounts or Actors.
- Stale sessions remain revoked after reactivation.
- Fresh login succeeds after reactivation.
- Multi-tenant Account still resolves the correct Actor in each tenant.
- No unexpected
500or SQL errors occur. -
PRAGMA foreign_key_checkis clean.
Promotion-Blocking Defects
Do not promote 30C.2 if any of these occur:
- A Tenant Administrator can enable authentication without an active Membership.
- A non-Tenant-Administrator can provision authentication.
- Tenant A can provision or manipulate Tenant B's Actor.
- Provisioning a second tenant creates a second Authentication Account.
- Tenant B learns that the Person belongs to or authenticates through Tenant A.
- Existing Account credentials are changed merely because a second Tenant Actor is added.
- Repeated provisioning creates duplicate Actors.
- Tenant Administrator can directly reactivate a global Account.
- Inactive Account can still authenticate normally.
- Invalid credentials reveal that an Account is inactive.
- Requesting reactivation directly activates the Account.
- Duplicate pending requests accumulate.
- Reactivation creates a new Account rather than restoring the existing one.
- Reactivation changes or duplicates existing Actor bindings.
- Reactivation revives a stale pre-deactivation session.
- An ordinary user can approve their own recovery request.
- Application Administrator loses global scope or acquires an ordinary tenant Actor.
- Any cross-tenant confidentiality leak is observed.
- Any unexpected database integrity violation or
500occurs.
If these checks pass, Bite 30C.2 has manually validated the architectural behavior it was intended to introduce and is ready for promotion.