test(auth): lock in bootstrap-admin login without email verification (PMS-676) #462

Merged
longjacksonle merged 1 commit from feat/PMS-676-bootstrap-admin-unverified-login into main 2026-07-24 16:16:49 +02:00

Implements PMS-676 (prerequisite investigation: PMS-637). Direction confirmed with the ticket owner: target the production bootstrap path (bunyip-as-OP), not the DEV-only env seed.

Finding

The ticket's premise ("the admin flow currently requires verifying the admin email before granting access, so on a fresh instance the admin can never get in") does not reproduce against current code. There is no email-verification gate on admin login:

  • Production authenticates the bootstrap admin through bunyip-as-OP. The platform-admin claim bunyip_role = "admin" maps to mokosh super_admin in place_bunyip_user (src/modules/auth/middleware.rs, effective_role_from_bunyip), and that path returns an authenticated session regardless of email_verified. An unverified address only skips invite consumption and stores a <sub>@unresolved.invalid placeholder; it never blocks login or the super_admin grant.
  • Legacy password login (service.rs::login) has no email_verified gate either, and the DEV ADMIN_EMAIL/ADMIN_PASSWORD seed creates its row pre-verified.
  • There is no RequireVerified extractor and no email_verified_at-based 403 anywhere in the request path.
  • The BOOTSTRAP_ADMIN_EMAIL "first matching login becomes admin" flow described in standup does not exist in code; only ADMIN_EMAIL/ADMIN_PASSWORD (DEV-only startup seed) and OAUTH_SUPER_ADMIN_EMAILS (Google exact-email auto-provision) do.

So there is no gate to remove. This PR pins the behaviour with regression tests and documents first-run onboarding, which is the actual deliverable.

Changes

  • tests/bunyip_login.rs: bootstrap_admin_unverified_email_still_gets_super_admin (email_verified = false, standing in for SMTP-unconfigured, still yields super_admin + an authenticated session) and bootstrap_admin_verified_email_still_gets_super_admin (email_verified = true does not downgrade the admin).
  • docs/first-run-onboarding.md: end-to-end first run - spin up, sign in as the bootstrap admin via the bunyip platform-admin claim (no verification), configure email via PUT /api/v1/settings/email (admin-only, hot-swaps the mailer, PMS-638), then invite the team who verify normally. Also documents the DEV-only local seed as distinct from production.

Acceptance criteria

  • On a fresh instance with no SMTP configured, the bootstrap admin gets admin access without email verification (proven by the unverified-email test; grounded in place_bunyip_user).
  • Once email is configured, normal verification applies to subsequent users (invite gate unchanged; verified-email test shows no admin downgrade).
  • First-run onboarding steps documented (docs/first-run-onboarding.md).
  • Tests cover bootstrap-admin login with SMTP unconfigured and with it configured.

Verification

cargo test --test bunyip_login: 11 passed. cargo fmt --all --check clean, cargo clippy --test bunyip_login clean.

Implements PMS-676 (prerequisite investigation: PMS-637). Direction confirmed with the ticket owner: target the production bootstrap path (bunyip-as-OP), not the DEV-only env seed. ## Finding The ticket's premise ("the admin flow currently requires verifying the admin email before granting access, so on a fresh instance the admin can never get in") does not reproduce against current code. There is no email-verification gate on admin login: - Production authenticates the bootstrap admin through bunyip-as-OP. The platform-admin claim `bunyip_role = "admin"` maps to mokosh `super_admin` in `place_bunyip_user` (`src/modules/auth/middleware.rs`, `effective_role_from_bunyip`), and that path returns an authenticated session regardless of `email_verified`. An unverified address only skips invite consumption and stores a `<sub>@unresolved.invalid` placeholder; it never blocks login or the super_admin grant. - Legacy password login (`service.rs::login`) has no `email_verified` gate either, and the DEV `ADMIN_EMAIL`/`ADMIN_PASSWORD` seed creates its row pre-verified. - There is no `RequireVerified` extractor and no `email_verified_at`-based 403 anywhere in the request path. - The `BOOTSTRAP_ADMIN_EMAIL` "first matching login becomes admin" flow described in standup does not exist in code; only `ADMIN_EMAIL`/`ADMIN_PASSWORD` (DEV-only startup seed) and `OAUTH_SUPER_ADMIN_EMAILS` (Google exact-email auto-provision) do. So there is no gate to remove. This PR pins the behaviour with regression tests and documents first-run onboarding, which is the actual deliverable. ## Changes - `tests/bunyip_login.rs`: `bootstrap_admin_unverified_email_still_gets_super_admin` (`email_verified = false`, standing in for SMTP-unconfigured, still yields `super_admin` + an authenticated session) and `bootstrap_admin_verified_email_still_gets_super_admin` (`email_verified = true` does not downgrade the admin). - `docs/first-run-onboarding.md`: end-to-end first run - spin up, sign in as the bootstrap admin via the bunyip platform-admin claim (no verification), configure email via `PUT /api/v1/settings/email` (admin-only, hot-swaps the mailer, PMS-638), then invite the team who verify normally. Also documents the DEV-only local seed as distinct from production. ## Acceptance criteria - [x] On a fresh instance with no SMTP configured, the bootstrap admin gets admin access without email verification (proven by the unverified-email test; grounded in `place_bunyip_user`). - [x] Once email is configured, normal verification applies to subsequent users (invite gate unchanged; verified-email test shows no admin downgrade). - [x] First-run onboarding steps documented (`docs/first-run-onboarding.md`). - [x] Tests cover bootstrap-admin login with SMTP unconfigured and with it configured. ## Verification `cargo test --test bunyip_login`: 11 passed. `cargo fmt --all --check` clean, `cargo clippy --test bunyip_login` clean.
test(auth): lock in bootstrap-admin login without email verification (PMS-676)
All checks were successful
E2E / Playwright against staging (pull_request) Successful in 38s
Check / fmt + clippy + build + tests (pull_request) Successful in 1m30s
Create release / Gate (release-branch merges only) (pull_request) Successful in 1s
Create release / Create release from merged PR (pull_request) Has been skipped
Integration / integration tests (pull_request) Successful in 5m46s
7f371b4b64
PMS-676 asked to let the first/bootstrap admin log in and operate on a fresh instance even when email is unconfigured and the address can never be verified. Investigation (the PMS-637 prerequisite) found that the production path already satisfies this: the SPA authenticates the bootstrap admin through bunyip-as-OP, whose platform-admin claim (bunyip_role = "admin") maps to mokosh super_admin in place_bunyip_user, and that path never gates login on email_verified. An unverified address only skips invite consumption and stores a placeholder email; it does not block login or the super_admin grant. There is no RequireVerified extractor and no email_verified_at-based 403 in the request path.

Rather than remove a gate that does not exist, add regression tests that pin the behaviour so a future change cannot reintroduce the chicken-and-egg, and document the first-run onboarding end to end.

- tests/bunyip_login.rs: bootstrap_admin_unverified_email_still_gets_super_admin (email_verified=false, i.e. SMTP unconfigured, still yields super_admin + an authenticated session) and bootstrap_admin_verified_email_still_gets_super_admin (email_verified=true does not downgrade the admin).
- docs/first-run-onboarding.md: spin up -> sign in as the bootstrap admin via the bunyip platform-admin claim (no verification) -> configure email via PUT /api/v1/settings/email (admin-only, hot-swaps the mailer, PMS-638) -> invite the team, who then verify normally. Also documents the DEV-ONLY ADMIN_EMAIL/ADMIN_PASSWORD local seed (pre-verified row) as distinct from the production path.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WDCg5oWRUicf3pVrUR3CqX
longjacksonle scheduled this pull request to auto merge when all checks succeed 2026-07-24 16:16:47 +02:00
longjacksonle deleted branch feat/PMS-676-bootstrap-admin-unverified-login 2026-07-24 16:16:49 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
psa-systems/mokosh-server!462
No description provided.