feat(auth): detect "IMAP disabled" AUTHENTICATE failures and prompt the user to enable IMAP #430

Merged
JMR-dev merged 3 commits from feat-390-imap-disabled-detection into main 2026-07-08 05:43:40 +00:00
3 Commits
Author SHA1 Message Date
mergify[bot] d6665fe8c3 Merge branch 'main' into feat-390-imap-disabled-detection 2026-07-08 05:23:50 +00:00
JMR-dev 8f7926886c Merge origin/main into feat-390-imap-disabled-detection (resolve additive strings.xml conflict; keep both #390 imap_disabled_* and #426 outlook_imap_* strings) 2026-07-08 00:00:39 -05:00
JMR-dev 60f822a63c feat(auth): detect "IMAP disabled" AUTHENTICATE failures and prompt to enable IMAP
When account setup obtains a valid credential but the IMAP AUTHENTICATE step is
rejected because IMAP access is switched off for the mailbox, surface an
actionable "turn on IMAP" dialog (with the provider's enable-IMAP help link)
instead of the opaque generic auth error (#390).

- ImapAuthError.isImapDisabled classifies the failure on two signals: explicit
  provider "IMAP is disabled/not enabled" server text (e.g. Gmail's "not enabled
  for IMAP use"), and -- for the Outlook XOAUTH2 path -- a valid-token
  AUTHENTICATE rejection, which outlook.office365.com reports only as a generic
  "AUTHENTICATE failed". Ordinary wrong-password / expired-token / network errors
  are deliberately not matched, so they keep the generic error.
- imapDisabledPromptFor resolves a provider-aware prompt (brand via
  MailProvider.brandFor; Outlook + Gmail enable-IMAP help URLs, generic
  otherwise).
- Shared ImapDisabledDialog reused by the Outlook picker, the app-password form,
  and manual setup -- the three points where the auth failure surfaces. The
  reactive complement to the pre-auth Outlook notice (#411/#426).
- PII-free AppLog breadcrumbs at the classification/prompt points (accountLogRef
  only; never the email/host/token).

Tests: ImapAuthErrorTest (provider text + OAuth inference + a real GreenMail
wrong-password negative), ImapDisabledPromptTest (brand/URL resolution),
ImapDisabledDialogJvmTest (Robolectric), per-view-model + per-screen wiring
tests, and an instrumented AppPasswordSetupScreenTest case driving the failure
end to end.

Closes #390
2026-07-07 23:54:57 -05:00