# SEC-004 canonical Microsoft identity verification Date: 2026-08-02 Status: `IMPLEMENTED — NOT VERIFIED`. Repository implementation, synthetic controller tests, frontend component tests, build, migration SQL and disposable SQLite upgrade checks pass. Real Microsoft, browser, SMTP, MariaDB execution and production legacy inventory remain blocked. ## Implemented trust path - Microsoft account ownership is the normalized GUID pair (`MicrosoftTenantId`, `MicrosoftObjectId`) with a unique composite database index. - Exchange and conflict lookup use only that pair. `MicrosoftSubject` is no longer written and provider email is metadata only; a matching local email returns a safe conflict instead of a link. - New Microsoft registration stores the pair immediately, leaves application email unconfirmed, and obeys the verification-required 202/no-session gate. - Explicit link requires the authenticated local user's current password. Link and unlink revoke all sessions/trusted devices; passwordless unlink is refused as a last-credential guard. - Legacy fields remain null-canonical evidence. A single eligible candidate receives a purpose-bound proof at its confirmed application email. Confirmation requires that proof plus a fresh Microsoft token for the exact same (`tid`, `oid`) pair. Multiple or unconfirmed candidates require operator-assisted recovery. - Frontend MSAL authority uses the same `AUTH_MICROSOFT_TENANT` value passed at build time. ## Evidence - Focused auth controller tests: 34/34 passed. - Full backend suite: 507/507 passed. - Full frontend suite: 44 suites, 152/152 passed; production build passed. - SQLite migration SQL uses two nullable `TEXT` columns and a unique composite index. - MariaDB migration SQL uses two nullable `varchar(36)` columns and the same unique index. - Disposable SQLite legacy rehearsal: - two rows with duplicate legacy subject/email values remained unchanged and null-canonical; - migration applied without backfill; - first canonical pair assignment succeeded; - duplicate pair assignment failed with SQLite unique-constraint exit 19. - Production Compose interpolation includes the same tenant mode in backend configuration and the frontend build argument. ## Blocked checks and residual risk - No real Microsoft token/account was used. Issuer/tenant/signature behavior is covered by the SEC-003 signed-token tests; exchange/link/recovery use mocked tenant-qualified principals. - The existing browser policy blocker was not retried. No browser workflow, popup, accessibility, mobile or screenshot claim is made. - No SMTP sink was available, so the complete emailed recovery link was not exercised end to end. - MariaDB SQL generation passed, but no disposable MariaDB execution environment was available. - Production legacy counts and collision groups are unknown. Microsoft production enablement must remain gated until the counts-only inventory in `deploy/README.md` and migration/version-skew checks pass. - Legacy rows with multiple candidates or unavailable/unconfirmed app email deliberately require operator verification; no automated merge is attempted. ## Rollback Disable Microsoft sign-in by clearing its client ID, roll back application binaries, and retain the additive columns and legacy evidence. Do not restore subject-only or email auto-linking. The migration `Down` is appropriate only before canonical pair data is relied upon.