# Scratchpad — EE+CE Domain-Scoped SSO Takeover and Domain Approval

- Plan slug: `ee-ce-domain-scoped-sso-takeover`
- Created: `2026-03-03`

## What This Is

Working notes for expanding domain-scoped MSP SSO discovery to support:
- EE domain ownership approval before tenant takeover
- CE advisory domain registration behavior
- reliable Nine Minds app-level fallback for unmanaged domains

## Decisions

- (2026-03-03) CE domain registration is advisory (no mandatory ownership verification gate).
- (2026-03-03) EE requires domain ownership verification before a tenant can take over SSO routing for a domain.
- (2026-03-03) Unmanaged/unapproved domains should route to Nine Minds app-level SSO providers.
- (2026-03-03) Keep `/auth/msp/signin` UX and anti-enumeration response contracts intact.
- (2026-03-03) Lifecycle status model for MSP SSO domain claims uses `advisory | pending | verified | verified_legacy | rejected | revoked` with per-status timestamps to preserve backward compatibility while enabling EE verification workflows.
- (2026-03-03) Verification challenges are stored as tenant-scoped records keyed to domain claim rows so challenge rotation can invalidate prior material while preserving audit timestamps.
- (2026-03-03) Lifecycle backfill uses deployment edition context (`EDITION` / `NEXT_PUBLIC_EDITION`) because MSP tenant rows do not carry per-tenant CE/EE markers in the shared schema.
- (2026-03-03) Shared helpers for domain lifecycle now live in `packages/auth/src/lib/sso/mspSsoResolution.ts` so both discovery and settings actions use the same normalization and validation behavior.
- (2026-03-03) Added `listMspSsoDomainClaims` action with permission gate parity to existing domain management checks; action returns lifecycle metadata for each active claim.
- (2026-03-03) Added `requestMspSsoDomainClaim` action to create/reuse pending claims and provision DNS TXT challenge material with hashed verification values.
- (2026-03-03) Added `refreshMspSsoDomainClaimChallenge` action that always rotates active challenge material for an existing claim and reasserts `pending` claim status.
- (2026-03-03) Added `verifyMspSsoDomainClaimOwnership` action that resolves DNS TXT records for the stored challenge label/value and promotes claim status to `verified` only on exact match.
- (2026-03-03) Added `revokeMspSsoDomainClaim` action to transition active claims to `revoked` and invalidate any still-active verification challenges.
- (2026-03-03) CE advisory save/remove path now explicitly sets/retains `claim_status='advisory'` on active registrations without any ownership proof gate.
- (2026-03-03) Verified ownership conflict guard is now enforced twice: app-layer check during verify action and DB-layer partial unique index on `lower(domain)` for active `verified|verified_legacy` rows.
- (2026-03-03) EE settings UI now renders lifecycle-aware claim rows with status badges and per-claim actions (`request`, `verify`, `refresh challenge`, `revoke`) instead of only a plain editable domain list.
- (2026-03-03) Pending EE claims now render inline DNS TXT instructions in settings (host + value) sourced from the active challenge row.
- (2026-03-03) CE UI copy now explicitly labels domain registration as advisory and clarifies that ownership verification is not enforced in Community Edition.
- (2026-03-03) Settings copy now consistently states fallback behavior: domains without eligible claims route to Nine Minds app-level SSO providers.
- (2026-03-03) Discovery helper now resolves domain ownership with both edition and claim lifecycle context (`eligibleForTakeover`) before selecting `tenant` vs `app` source.
- (2026-03-03) EE routing gate is now strict: only `verified`/`verified_legacy` claims can produce tenant-scoped provider discovery; all other statuses bypass tenant takeover.
- (2026-03-03) EE fallback behavior now explicitly routes `pending`, `rejected`, `revoked`, and ambiguous domain claims to app-level provider discovery.
- (2026-03-03) CE routing now treats advisory claims as takeover-eligible, enabling tenant-scoped provider discovery without ownership verification requirements.
- (2026-03-03) Unregistered/unresolved domains in both editions now consistently return app-level discovery output, preserving fallback behavior independent of tenant claim state.
- (2026-03-03) Discover route contract remains invariant (`{ ok: true, providers: [] }` on invalid/rate-limited/error paths) even after lifecycle-aware helper changes.

## Discoveries / Constraints

- (2026-03-03) Existing domain-scoped discovery/resolver path already exists at `server/src/app/api/auth/msp/sso/discover/route.ts` and `server/src/app/api/auth/msp/sso/resolve/route.ts`.
- (2026-03-03) Current domain mapping persistence uses `msp_sso_tenant_login_domains` and already supports normalized domain lookup.
- (2026-03-03) Current fallback behavior already supports app-level provider readiness (`GOOGLE_OAUTH_*`, `MICROSOFT_OAUTH_*`).
- (2026-03-03) CE/EE login wiring currently relies on edition aliasing for SSO provider buttons; CE parity must explicitly keep discovery-enabled behavior.
- (2026-03-03) Existing managed email-domain flow (Resend-style) provides a useful pattern for async domain verification lifecycle in EE settings.
- (2026-03-03) Existing `msp_sso_tenant_login_domains` table is already deployed in this branch and uses only `is_active`; lifecycle behavior must be introduced as additive columns to avoid breaking current discovery.
- (2026-03-03) There is no tenant-level CE/EE column in the database schema; edition-aware defaults/backfill must rely on deployment edition context (env) rather than per-tenant data.
- (2026-03-03) Challenge persistence needs composite tenant+claim foreign key (`tenant`, `claim_id`) because login-domain claims use composite primary key (`tenant`, `id`) for Citus compatibility.
- (2026-03-03) Backfill migration sets EE existing rows to `verified_legacy` with inferred `claimed_at`/`verified_at` timestamps, while CE keeps advisory defaults.
- (2026-03-03) Domain normalization now strips common decorations (`@`, `mailto:`, URL host wrappers, trailing dot) before validation, reducing mismatch drift between settings and login discovery.
- (2026-03-03) Claims listing tolerates rollout ordering by checking lifecycle column existence at runtime and defaulting unknown/missing statuses to `advisory`.
- (2026-03-03) Claim request action is idempotent for existing same-tenant `pending` claims when an active challenge already exists; otherwise it rotates by deactivating old active challenges and creating a new one.
- (2026-03-03) Refresh semantics are intentionally non-idempotent: each refresh deactivates previous active challenge rows and creates a new DNS TXT value for the same claim id.
- (2026-03-03) Verification action keeps neutral admin-safe failure messaging for DNS mismatch/missing challenge and does not expose sensitive internals.
- (2026-03-03) Revoke flow updates lifecycle metadata (`claim_status_updated_at`, `revoked_at`) and keeps the claim row for historical/audit purposes instead of deleting it.
- (2026-03-03) CE advisory remove remains non-destructive (`is_active=false`) so historical rows stay available for later reactivation/audit.
- (2026-03-03) Conflict is resolved at verify time (not request time), matching PRD recommendation and preserving neutral request behavior.
- (2026-03-03) CE and EE UX now branch in the same component using edition detection: CE keeps advisory save/remove list; EE uses immediate claim workflow actions.
- (2026-03-03) Verification status feedback is surfaced through inline/error alert state and action-specific toasts (verify/refresh/request/revoke outcomes).
- (2026-03-03) Header/description messaging now diverges by edition so EE focuses on claim lifecycle actions while CE emphasizes advisory registration semantics.
- (2026-03-03) Added a persistent informational alert in the settings panel to reinforce fallback behavior independently of edition-specific controls.
- (2026-03-03) Claim status precedence normalization (`verified > verified_legacy > pending > advisory > rejected > revoked`) prevents duplicate-row drift from producing unstable routing decisions.
- (2026-03-03) Ambiguous multi-tenant mappings remain fail-closed and are never eligible for EE tenant takeover.
- (2026-03-03) EE non-eligible statuses preserve anti-enumerating behavior by returning normal app-source provider readiness (including empty set when app providers are not configured).
- (2026-03-03) CE still blocks `revoked`/`rejected` claims from tenant routing, preventing stale/deactivated claims from taking over discovery.
- (2026-03-03) Discovery still returns valid neutral payloads when fallback providers are empty, avoiding auth-flow branching on domain existence.
- (2026-03-03) Discovery logs remain safe metadata only (`source`, `providerCount`, `domain`, `ambiguous`), with no raw email disclosure.

## Commands / Runbooks

- (2026-03-03) Scaffolded plan bundle:
  - `python3 /Users/roberisaacs/.codex/skills/alga-plan/scripts/scaffold_plan.py "EE+CE Domain-Scoped SSO Takeover and Domain Approval" --slug ee-ce-domain-scoped-sso-takeover`
- (2026-03-03) Optional plan validation:
  - `python3 /Users/roberisaacs/.codex/skills/alga-plan/scripts/validate_plan.py ee/docs/plans/2026-03-03-ee-ce-domain-scoped-sso-takeover`
- (2026-03-03) F001 implementation checks:
  - `node -c server/migrations/20260303100000_add_msp_sso_domain_claim_lifecycle.cjs`
  - `npx vitest run server/src/test/unit/migrations/mspSsoTenantLoginDomainsMigration.test.ts`
- (2026-03-03) F002 implementation checks:
  - `node -c server/migrations/20260303101000_create_msp_sso_domain_verification_challenges.cjs`
  - `cd server && npx vitest run src/test/unit/migrations/mspSsoTenantLoginDomainsMigration.test.ts`
- (2026-03-03) F003 implementation checks:
  - `node -c server/migrations/20260303102000_backfill_msp_sso_domain_claim_status_defaults.cjs`
  - `cd server && npx vitest run src/test/unit/migrations/mspSsoTenantLoginDomainsMigration.test.ts`
- (2026-03-03) F004 implementation checks:
  - `cd server && npx vitest run ../packages/auth/src/lib/sso/mspSsoResolution.test.ts`
  - `cd server && npx vitest run ../packages/integrations/src/actions/integrations/mspSsoDomainActions.test.ts`
- (2026-03-03) F005 implementation checks:
  - `cd server && npx vitest run ../packages/integrations/src/actions/integrations/mspSsoDomainActions.test.ts`
- (2026-03-03) F006 implementation checks:
  - `cd server && npx vitest run ../packages/integrations/src/actions/integrations/mspSsoDomainActions.test.ts`
- (2026-03-03) F007 implementation checks:
  - `cd server && npx vitest run ../packages/integrations/src/actions/integrations/mspSsoDomainActions.test.ts`
- (2026-03-03) F008 implementation checks:
  - `cd server && npx vitest run ../packages/integrations/src/actions/integrations/mspSsoDomainActions.test.ts`
- (2026-03-03) F009 implementation checks:
  - `cd server && npx vitest run ../packages/integrations/src/actions/integrations/mspSsoDomainActions.test.ts`
- (2026-03-03) F010 implementation checks:
  - `cd server && npx vitest run ../packages/integrations/src/actions/integrations/mspSsoDomainActions.test.ts`
- (2026-03-03) F011 implementation checks:
  - `node -c server/migrations/20260303103000_enforce_verified_msp_sso_domain_ownership.cjs`
  - `cd server && npx vitest run ../packages/integrations/src/actions/integrations/mspSsoDomainActions.test.ts`
- (2026-03-03) F012 implementation checks:
  - `cd server && npx vitest run ../packages/integrations/src/actions/integrations/mspSsoDomainActions.test.ts`
  - `cd server && npx vitest run ../packages/integrations/src/components/settings/integrations/IntegrationsSettingsPage.providers.test.ts`
- (2026-03-03) F013 implementation checks:
  - `cd server && npx vitest run ../packages/integrations/src/actions/integrations/mspSsoDomainActions.test.ts`
  - `cd server && npx vitest run ../packages/integrations/src/components/settings/integrations/IntegrationsSettingsPage.providers.test.ts`
- (2026-03-03) F014 implementation checks:
  - `cd server && npx vitest run ../packages/integrations/src/components/settings/integrations/IntegrationsSettingsPage.providers.test.ts`
- (2026-03-03) F015 implementation checks:
  - `cd server && npx vitest run ../packages/integrations/src/components/settings/integrations/IntegrationsSettingsPage.providers.test.ts`
- (2026-03-03) F016 implementation checks:
  - `cd server && npx vitest run ../packages/auth/src/lib/sso/mspSsoResolution.test.ts`
- (2026-03-03) F017 implementation checks:
  - `cd server && npx vitest run ../packages/auth/src/lib/sso/mspSsoResolution.test.ts`
- (2026-03-03) F018 implementation checks:
  - `cd server && npx vitest run ../packages/auth/src/lib/sso/mspSsoResolution.test.ts`
- (2026-03-03) F019 implementation checks:
  - `cd server && npx vitest run ../packages/auth/src/lib/sso/mspSsoResolution.test.ts`
- (2026-03-03) F020 implementation checks:
  - `cd server && npx vitest run ../packages/auth/src/lib/sso/mspSsoResolution.test.ts`
- (2026-03-03) F021 implementation checks:
  - `cd server && npx vitest run src/app/api/auth/msp/sso/discover/route.test.ts`

## Links / References

- Prior domain-scoped discovery plan:
  - `ee/docs/plans/2026-02-24-msp-domain-scoped-sso-discovery/PRD.md`
- Discovery/resolver implementation:
  - `packages/auth/src/lib/sso/mspSsoResolution.ts`
  - `server/src/app/api/auth/msp/sso/discover/route.ts`
  - `server/src/app/api/auth/msp/sso/resolve/route.ts`
- Domain management actions/UI:
  - `packages/integrations/src/actions/integrations/mspSsoDomainActions.ts`
  - `packages/integrations/src/components/settings/integrations/MspSsoLoginDomainsSettings.tsx`
- Managed email-domain reference patterns:
  - `ee/server/src/lib/actions/email-actions/managedDomainActions.ts`
  - `packages/integrations/src/actions/email-actions/emailDomainActions.ts`

## Open Questions

- Should EE verified-domain conflicts be blocked immediately at claim request time or at verification completion time?
- Should EE verified claims support periodic re-verification in a future phase, or remain manual revoke/re-verify only?
- (2026-03-03) Completed `F022`: resolver now revalidates tenant takeover eligibility at resolve-time using current domain lifecycle (`resolveTenantForMspSsoDomain`) before allowing tenant source, and safely falls back to app source only when allow-list permits and app credentials exist.
- (2026-03-03) Security hardening tied to resolver revalidation: discovery cookie now carries normalized domain, and tenant discovery context is only used when resolver email domain matches cookie domain.
- (2026-03-03) Validation run for `F022`:
  - `cd server && npx vitest run ../packages/auth/src/lib/sso/mspSsoResolution.test.ts src/app/api/auth/msp/sso/discover/route.test.ts src/app/api/auth/msp/sso/resolve/route.test.ts`
- (2026-03-03) Completed `F023`: resolver generic failure contract remains invariant for invalid/disallowed/rate-limited resolution paths while lifecycle checks are applied internally.
- (2026-03-03) Validation run for `F023`:
  - `cd server && npx vitest run src/app/api/auth/msp/sso/resolve/route.test.ts`
- (2026-03-03) Completed `F024`: discovery payload now includes normalized domain and parsing rejects signed payloads with invalid domain values; resolve/discover cookie tests assert `httpOnly`/`sameSite=lax`/short TTL (`maxAge=300`).
- (2026-03-03) Validation run for `F024`:
  - `cd server && npx vitest run ../packages/auth/src/lib/sso/mspSsoResolution.test.ts src/app/api/auth/msp/sso/discover/route.test.ts src/app/api/auth/msp/sso/resolve/route.test.ts`
- (2026-03-03) Completed `F025`: MSP credentials sign-in path remains unchanged and isolated from resolver-cookie logic (`MspCredentialsFlow.contract.test.ts`).
- (2026-03-03) Validation run for `F025`:
  - `cd server && npx vitest run ../packages/auth/src/components/MspCredentialsFlow.contract.test.ts`
- (2026-03-03) Completed `F026`: added explicit client-portal auth contract coverage to assert no MSP SSO discovery/resolve endpoint wiring in client-portal sign-in code paths.
- (2026-03-03) Note: `src/test/unit/app/auth/client-portal/signin/page.test.ts` currently fails in isolation due an existing import/mocking path mismatch (`@alga-psa/db/models/UserSession`), so validation used package-level contract tests instead.
- (2026-03-03) Validation run for `F026`:
  - `cd server && npx vitest run ../packages/auth/src/components/ClientLoginForm.ssoGuard.test.ts ../packages/auth/src/components/ClientPortalAuthUnchanged.contract.test.ts`
- (2026-03-03) Completed `F027`: CE/EE parity contract confirms MSP SSO entry wiring uses discovery-enabled provider buttons implementation (no null/stub path).
- (2026-03-03) Validation run for `F027`:
  - `cd server && npx vitest run ../packages/auth/src/components/ssoProviderButtonsImport.test.ts ../packages/auth/src/components/ssoProviderButtons.ceEeParity.test.ts`
- (2026-03-03) Completed `F028`: added contract assertion that `MspLoginForm` passes the live typed email state into `SsoProviderButtons` (`email={email}`), preserving discovery input propagation for CE/EE builds via shared entry wiring.
- (2026-03-03) Validation run for `F028`:
  - `cd server && npx vitest run ../packages/auth/src/components/ssoProviderButtonsImport.test.ts`
- (2026-03-03) Completed `F029`: existing MSP SSO button behavior suite continues to validate disabled state rules, in-flight discovery behavior, provider eligibility gating, disabled-click no-op, and remembered provider preference handling.
- (2026-03-03) Validation run for `F029`:
  - `cd server && npx vitest run ../packages/auth/src/components/SsoProviderButtons.msp.test.tsx`
- (2026-03-03) Completed `F030`: added tenant-admin runbook doc for EE domain takeover lifecycle (request -> DNS TXT verification -> verified -> revoke) at `docs/integrations/msp-sso-domain-claims.md`.
- (2026-03-03) Validation run for `F030`:
  - `cd server && npx vitest run ../packages/auth/src/lib/sso/mspSsoDocsContract.test.ts`
- (2026-03-03) Completed `F031`: documentation now includes CE advisory mode behavior, explicitly stating non-blocking ownership verification and advisory routing limitations.
- (2026-03-03) Validation source: `docs/integrations/msp-sso-domain-claims.md` (section `CE Advisory Domain Registration`) and docs contract assertions.
- (2026-03-03) Completed `F032`: documentation now includes Nine Minds app-level fallback prerequisites and unmanaged/unapproved domain routing behavior with required `*_OAUTH_*` env vars.
- (2026-03-03) Validation source: `docs/integrations/msp-sso-domain-claims.md` (section `App-Level Fallback`) and docs contract assertions.
- (2026-03-03) Completed `F033`: expanded action-level unit coverage for lifecycle endpoints and permission guards in `mspSsoDomainActions.test.ts` (forbidden checks across request/refresh/verify/revoke plus validation contracts).
- (2026-03-03) Validation run for `F033`:
  - `cd server && npx vitest run ../packages/integrations/src/actions/integrations/mspSsoDomainActions.test.ts`
- (2026-03-03) Completed `F034`: expanded discovery/resolver matrix coverage with explicit EE verified/pending/revoked/ambiguous cases and CE advisory routing case in `mspSsoResolution.test.ts`, plus endpoint contract suites for discover/resolve.
- (2026-03-03) Validation run for `F034`:
  - `cd server && npx vitest run ../packages/auth/src/lib/sso/mspSsoResolution.test.ts src/app/api/auth/msp/sso/discover/route.test.ts src/app/api/auth/msp/sso/resolve/route.test.ts`
- (2026-03-03) Completed `F035`: route contract tests verify `/auth/msp/signin` entry path and callbackUrl passthrough/default behavior remain unchanged under lifecycle integration.
- (2026-03-03) Validation run for `F035`:
  - `cd server && npx vitest run ../packages/auth/src/components/MspSignInRoute.contract.test.ts`
- (2026-03-03) Completed `T001`: added migration contract suite `server/src/test/unit/migrations/mspSsoDomainLifecycle.migrations.test.ts` validating lifecycle schema additions (`claim_status` + lifecycle metadata columns/check constraint).
- (2026-03-03) Validation run for `T001`:
  - `cd server && npx vitest run src/test/unit/migrations/mspSsoDomainLifecycle.migrations.test.ts`
- (2026-03-03) Completed `T002`: migration contract suite covers challenge table creation/index contract in `mspSsoDomainLifecycle.migrations.test.ts`.
- (2026-03-03) Completed `T003`: migration contract suite verifies rollback drops lifecycle columns and challenge table.
- (2026-03-03) Completed `T004`: migration contract suite verifies EE backfill SQL sets `verified_legacy` defaults.
- (2026-03-03) Completed `T005`: migration contract suite verifies CE backfill SQL sets `advisory` defaults.
- (2026-03-03) Completed `T006`: domain normalization covered by helper normalization tests and save-domain normalization behavior.
- (2026-03-03) Completed `T007`: malformed domain validation covered by deterministic neutral error assertions in action tests.
- (2026-03-03) Completed `T008`: permission denial for list-claims and lifecycle actions covered in `mspSsoDomainActions.test.ts`.
- (2026-03-03) Completed `T009`: claims list metadata contract covered in `mspSsoDomainActions.test.ts` (normalized claim fields + lifecycle timestamps).
- (2026-03-03) Completed `T010`: request-claim happy path coverage added (pending claim + active DNS challenge creation).
- (2026-03-03) Completed `T011`: request-claim idempotency coverage added for existing pending claim + active challenge.
- (2026-03-03) Completed `T012`: refresh challenge coverage added for token rotation and invalidation of previous active challenge.
- (2026-03-03) Completed `T013`: verify-claim success coverage added for matching DNS TXT challenge promoting claim to verified.
- (2026-03-03) Completed `T014`: verify-claim mismatch coverage added with neutral actionable error and pending-state preservation.
- (2026-03-03) Completed `T015`: revoke coverage added (verified claim transitions to revoked and active challenge invalidates).
- (2026-03-03) Completed `T016`: conflict coverage added (verification blocked when another tenant already holds verified claim).
- (2026-03-03) Completed `T017`: CE advisory add-domain behavior covered by save action persistence + normalized active domain assertions.
- (2026-03-03) Completed `T018`: CE advisory remove-domain behavior covered by save action deactivation/removal assertions.
- (2026-03-03) Completed `T019`: added EE runtime UI contract coverage asserting lifecycle claim rows render with status badges (`Pending`, `Verified`) from `listMspSsoDomainClaims` data.
- (2026-03-03) Validation run for `T019`:
  - `cd server && npx vitest run --coverage.enabled=false ../packages/integrations/src/components/settings/integrations/MspSsoLoginDomainsSettings.contract.test.tsx`
- (2026-03-03) Completed `T020`: EE settings test now asserts pending claims render DNS TXT verification instructions with host and value hints.
- (2026-03-03) Validation run for `T020`:
  - `cd server && npx vitest run --coverage.enabled=false ../packages/integrations/src/components/settings/integrations/MspSsoLoginDomainsSettings.contract.test.tsx`
- (2026-03-03) Completed `T021`: EE settings verification-failure path now checks neutral actionable error messaging and destructive feedback toast.
- (2026-03-03) Validation run for `T021`:
  - `cd server && npx vitest run --coverage.enabled=false ../packages/integrations/src/components/settings/integrations/MspSsoLoginDomainsSettings.contract.test.tsx`
- (2026-03-03) Completed `T022`: CE settings suite now asserts advisory-mode guidance copy is rendered for community deployments.
- (2026-03-03) Validation run for `T022`:
  - `cd server && npx vitest run --coverage.enabled=false src/test/unit/components/integrations/MspSsoLoginDomainsSettings.test.tsx`
- (2026-03-03) Completed `T023`: CE UI test now covers advisory add/remove controls end-to-end and asserts persisted payload ordering.
- (2026-03-03) Validation run for `T023`:
  - `cd server && npx vitest run --coverage.enabled=false src/test/unit/components/integrations/MspSsoLoginDomainsSettings.test.tsx`
- (2026-03-03) Completed `T024`: CE settings copy contract now explicitly asserts unmanaged-domain fallback to Nine Minds app-level providers.
- (2026-03-03) Validation run for `T024`:
  - `cd server && npx vitest run --coverage.enabled=false src/test/unit/components/integrations/MspSsoLoginDomainsSettings.test.tsx`
- (2026-03-03) Completed `T025`: discovery helper matrix tests now assert edition + claim lifecycle decisions drive `tenant` vs `app` source selection.
- (2026-03-03) Validation run for `T025`:
  - `cd server && npx vitest run --coverage.enabled=false ../packages/auth/src/lib/sso/mspSsoResolution.test.ts`
- (2026-03-03) Completed `T026`: EE discovery with verified claim and tenant Google credentials returns tenant source + google provider.
- (2026-03-03) Validation run for `T026`:
  - `cd server && npx vitest run --coverage.enabled=false ../packages/auth/src/lib/sso/mspSsoResolution.test.ts`
- (2026-03-03) Completed `T027`: EE discovery with verified claim and tenant Microsoft credentials returns tenant source + azure-ad provider.
- (2026-03-03) Validation run for `T027`:
  - `cd server && npx vitest run --coverage.enabled=false ../packages/auth/src/lib/sso/mspSsoResolution.test.ts`
- (2026-03-03) Completed `T028`: EE discovery with pending claim returns app-level fallback providers only.
- (2026-03-03) Validation run for `T028`:
  - `cd server && npx vitest run --coverage.enabled=false ../packages/auth/src/lib/sso/mspSsoResolution.test.ts`
- (2026-03-03) Completed `T029`: EE discovery with revoked claim returns app-level fallback providers only.
- (2026-03-03) Validation run for `T029`:
  - `cd server && npx vitest run --coverage.enabled=false ../packages/auth/src/lib/sso/mspSsoResolution.test.ts`
- (2026-03-03) Completed `T030`: EE discovery with ambiguous domain ownership returns app-level fallback providers only.
- (2026-03-03) Validation run for `T030`:
  - `cd server && npx vitest run --coverage.enabled=false ../packages/auth/src/lib/sso/mspSsoResolution.test.ts`
- (2026-03-03) Completed `T031`: CE discovery with advisory registered domain can return tenant-scoped provider eligibility.
- (2026-03-03) Validation run for `T031`:
  - `cd server && npx vitest run --coverage.enabled=false ../packages/auth/src/lib/sso/mspSsoResolution.test.ts`
- (2026-03-03) Completed `T032`: CE discovery with unregistered domain returns app-level fallback providers.
- (2026-03-03) Validation run for `T032`:
  - `cd server && npx vitest run --coverage.enabled=false ../packages/auth/src/lib/sso/mspSsoResolution.test.ts`
- (2026-03-03) Completed `T033`: Unresolved domain in both editions returns app-level fallback provider set.
- (2026-03-03) Validation run for `T033`:
  - `cd server && npx vitest run --coverage.enabled=false ../packages/auth/src/lib/sso/mspSsoResolution.test.ts`
- (2026-03-03) Completed `T034`: Discover endpoint invalid-email path returns invariant neutral schema.
- (2026-03-03) Validation run for `T034`:
  - `cd server && npx vitest run --coverage.enabled=false src/app/api/auth/msp/sso/discover/route.test.ts`
- (2026-03-03) Completed `T035`: Discover endpoint rate-limit path returns same neutral schema and behavior.
- (2026-03-03) Validation run for `T035`:
  - `cd server && npx vitest run --coverage.enabled=false src/app/api/auth/msp/sso/discover/route.test.ts`
- (2026-03-03) Completed `T036`: Discover endpoint logging excludes raw email and keeps only safe metadata.
- (2026-03-03) Validation run for `T036`:
  - `cd server && npx vitest run --coverage.enabled=false src/app/api/auth/msp/sso/discover/route.test.ts`
- (2026-03-03) Completed `T037`: Resolver in EE with verified claim context selects tenant credential source.
- (2026-03-03) Validation run for `T037`:
  - `cd server && npx vitest run --coverage.enabled=false ../packages/auth/src/lib/sso/mspSsoResolution.test.ts`
- (2026-03-03) Completed `T038`: Resolver in EE with non-verified claim context uses app fallback or generic failure per eligibility.
- (2026-03-03) Validation run for `T038`:
  - `cd server && npx vitest run --coverage.enabled=false ../packages/auth/src/lib/sso/mspSsoResolution.test.ts`
- (2026-03-03) Completed `T039`: Resolver denies provider attempts outside discovered allow-list with generic response.
- (2026-03-03) Validation run for `T039`:
  - `cd server && npx vitest run --coverage.enabled=false src/app/api/auth/msp/sso/resolve/route.test.ts`
- (2026-03-03) Completed `T040`: Resolver stale discovery context is revalidated and cannot force unauthorized tenant source.
- (2026-03-03) Validation run for `T040`:
  - `cd server && npx vitest run --coverage.enabled=false src/app/api/auth/msp/sso/resolve/route.test.ts`
- (2026-03-03) Completed `T041`: Resolver invalid payload, rate-limit, and source-failure responses remain externally indistinguishable.
- (2026-03-03) Validation run for `T041`:
  - `cd server && npx vitest run --coverage.enabled=false src/app/api/auth/msp/sso/resolve/route.test.ts`
- (2026-03-03) Completed `T042`: Discovery cookie payload remains signed, short-lived, and free of provider secrets.
- (2026-03-03) Validation run for `T042`:
  - `cd server && npx vitest run --coverage.enabled=false src/app/api/auth/msp/sso/discover/route.test.ts`
- (2026-03-03) Completed `T043`: Resolution cookie payload remains signed, short-lived, and free of provider secrets.
- (2026-03-03) Validation run for `T043`:
  - `cd server && npx vitest run --coverage.enabled=false ../packages/auth/src/lib/sso/mspSsoResolution.test.ts`
- (2026-03-03) Completed `T044`: MSP credentials login succeeds unchanged when domain claim states vary.
- (2026-03-03) Validation run for `T044`:
  - `cd server && npx vitest run --coverage.enabled=false ../packages/auth/src/components/MspCredentialsFlow.contract.test.ts`
- (2026-03-03) Completed `T045`: Client portal signin flow remains unchanged and does not call MSP discovery endpoints.
- (2026-03-03) Validation run for `T045`:
  - `cd server && npx vitest run --coverage.enabled=false ../packages/auth/src/components/ClientPortalAuthUnchanged.contract.test.ts`
- (2026-03-03) Completed `T046`: CE build wiring resolves MSP SSO entry to discovery-enabled provider buttons implementation.
- (2026-03-03) Validation run for `T046`:
  - `cd server && npx vitest run --coverage.enabled=false ../packages/auth/src/components/ssoProviderButtons.ceEeParity.test.ts`
- (2026-03-03) Completed `T047`: MSP login form passes normalized email prop into SSO discovery component in both editions.
- (2026-03-03) Validation run for `T047`:
  - `cd server && npx vitest run --coverage.enabled=false ../packages/auth/src/components/ssoProviderButtonsImport.test.ts`
- (2026-03-03) Completed `T048`: SSO buttons remain disabled for invalid email and while discovery is in flight.
- (2026-03-03) Validation run for `T048`:
  - `cd server && npx vitest run --coverage.enabled=false ../packages/auth/src/components/SsoProviderButtons.msp.test.tsx`
- (2026-03-03) Completed `T049`: SSO buttons enable only providers returned by discovery response and keep unsupported buttons disabled.
- (2026-03-03) Validation run for `T049`:
  - `cd server && npx vitest run --coverage.enabled=false ../packages/auth/src/components/SsoProviderButtons.msp.test.tsx`
- (2026-03-03) Completed `T050`: Disabled SSO button clicks never trigger resolver/start request.
- (2026-03-03) Validation run for `T050`:
  - `cd server && npx vitest run --coverage.enabled=false ../packages/auth/src/components/SsoProviderButtons.msp.test.tsx`
- (2026-03-03) Completed `T051`: Remembered provider preference is only applied when provider remains eligible after discovery.
- (2026-03-03) Validation run for `T051`:
  - `cd server && npx vitest run --coverage.enabled=false ../packages/auth/src/components/SsoProviderButtons.msp.test.tsx`
- (2026-03-03) Completed `T052`: Docs describe EE request-verify-revoke lifecycle and DNS ownership verification steps.
- (2026-03-03) Validation run for `T052`:
  - `cd server && npx vitest run --coverage.enabled=false ../packages/auth/src/lib/sso/mspSsoDocsContract.test.ts`
- (2026-03-03) Completed `T053`: Docs describe CE advisory registration behavior and non-blocking ownership model.
- (2026-03-03) Validation run for `T053`:
  - `cd server && npx vitest run --coverage.enabled=false ../packages/auth/src/lib/sso/mspSsoDocsContract.test.ts`
- (2026-03-03) Completed `T054`: Docs describe Nine Minds fallback provider prerequisites and unmanaged-domain behavior.
- (2026-03-03) Validation run for `T054`:
  - `cd server && npx vitest run --coverage.enabled=false ../packages/auth/src/lib/sso/mspSsoDocsContract.test.ts`
- (2026-03-03) Completed `T055`: DB-backed integration happy path: EE verified claim + tenant Microsoft credentials returns tenant source and ["azure-ad"].
- (2026-03-03) Validation run for `T055`:
  - `cd server && npx vitest run --coverage.enabled=false ../packages/auth/src/lib/sso/mspSsoResolution.test.ts`
- (2026-03-03) Completed `T056`: DB-backed integration guard: EE pending claim with tenant credentials still returns app fallback source.
- (2026-03-03) Validation run for `T056`:
  - `cd server && npx vitest run --coverage.enabled=false ../packages/auth/src/lib/sso/mspSsoResolution.test.ts`
- (2026-03-03) Completed `T057`: DB-backed integration guard: second EE tenant cannot verify takeover for already-verified domain.
- (2026-03-03) Validation run for `T057`:
  - `cd server && npx vitest run --coverage.enabled=false ../packages/integrations/src/actions/integrations/mspSsoDomainActions.test.ts`
- (2026-03-03) Completed `T058`: DB-backed integration guard: revoked EE claim no longer enables tenant takeover routing.
- (2026-03-03) Validation run for `T058`:
  - `cd server && npx vitest run --coverage.enabled=false ../packages/auth/src/lib/sso/mspSsoResolution.test.ts`
- (2026-03-03) Completed `T059`: DB-backed integration CE advisory path: registered advisory domain can route to tenant source when tenant credentials exist.
- (2026-03-03) Validation run for `T059`:
  - `cd server && npx vitest run --coverage.enabled=false ../packages/auth/src/lib/sso/mspSsoResolution.test.ts`
- (2026-03-03) Completed `T060`: Route contract preserves `/auth/msp/signin` entry path and callbackUrl passthrough under new lifecycle rules.
- (2026-03-03) Validation run for `T060`:
  - `cd server && npx vitest run --coverage.enabled=false ../packages/auth/src/components/MspSignInRoute.contract.test.ts`
