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-check passes.
  • Full Playwright E2E suite passes.

The manual verification below concentrates on the two architectural changes introduced by Bite 30C.2:

  1. Tenant-driven Authentication provisioning
  2. 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/authentication remains 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 500 or SQL errors occur.
  • PRAGMA foreign_key_check is 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 500 occurs.

If these checks pass, Bite 30C.2 has manually validated the architectural behavior it was intended to introduce and is ready for promotion.