Skip to content

fix(web): drop the Outlook presets — Microsoft refuses app passwords - #175

Merged
zaridan merged 3 commits into
mainfrom
fix/outlook-presets-require-oauth
Aug 2, 2026
Merged

fix(web): drop the Outlook presets — Microsoft refuses app passwords#175
zaridan merged 3 commits into
mainfrom
fix/outlook-presets-require-oauth

Conversation

@zaridan

@zaridan zaridan commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

🟢 SAFE TO MERGE

Gates green. Decisions approved by the maintainer. Review adjudicated.

CodeRabbit: 2 findings, 2 real, both fixed. It was rate-limited when this PR opened and produced nothing; it reviewed on a later push. Its earlier green check was a rate limit, not a verdict.

Codex (adversarial, run while CodeRabbit was unavailable): 3 findings, 3 real, all addressed.

CodeRabbit's findings

Severity Finding Resolution
Major The OAuth notice was hidden on the reconnect path (lockAddress), so a stored Outlook mailbox could be retried with no explanation Fixed — it renders on both paths now. That screen is where the retry is most likely, so hiding it there was backwards. Stored host/port values are deliberately left visible rather than cleared: reviewing them is what the screen is for
Minor The spec asserted Microsoft's policy without linking it Fixed — direct citations for Exchange Online and consumer Outlook.com, plus the five provider pages §3 was verified against

What the substitute pass found

Severity Finding Resolution
High The domain list cannot detect Microsoft 365 businessops@contoso.example matches nothing and the operator is invited to enter a credential Exchange Online refuses Accepted as a real limitation. List widened to consumer variants; the code and spec now state plainly that this is a hint, not detection
Medium The notice is advisory — submission is still permitted Deviation from spec §4's "disabled with an explanation", now recorded in the spec rather than left to drift
Low user@outlook.com. (trailing DNS dot) bypassed both maps — no preset and no warning Fixed: domain normalization strips it

Codex explicitly cleared the state machine: gmail.com → outlook.com → gmail.com correctly re-applies the Gmail preset.

The honest limit of this change: a fixed domain list narrows the gap and can never close it. A business tenant on its own domain is unrecognisable without an MX or Autodiscover lookup at type-time, which this screen does not do. Said so in the code and the spec instead of letting a longer list imply coverage it does not have.

Every connect-screen preset was checked against that provider's own published documentation. All hosts, ports and TLS values were correct. The finding is about who is listed, not how they were configured.

Decision provenance

Decision — in plain words Source
Remove the Outlook/Hotmail/Live presets entirely You, 2026-07-31: "remove them and show the OAuth message"
Typing an Outlook address shows "requires OAuth — this will not work" rather than silently offering nothing You, same message
The spec's "Outlook.com consumer: unresolved" row is now No Factual resolution against Microsoft's published docs, quoted below — not a product choice

What was wrong

Microsoft refuses app passwords for IMAP/POP/SMTP. From Exchange Online's deprecation doc (updated 2026-07-16):

Basic authentication is now disabled in all tenants… no one (you or Microsoft support) can re-enable Basic authentication in your tenant.

and:

The deprecation of basic authentication also prevents the use of app passwords.

Consumer Outlook.com is the same — Microsoft Support requires Modern Auth/OAuth2 for POP, IMAP and SMTP.

No host/port combination makes this work. The preset handed an operator a credential their provider will not issue for this purpose, then failed with an opaque auth error.

specs/mail/mailbox-connection.md §3 already said so — Microsoft 365 business listed as No, consumer Outlook.com as "unresolved — test before claiming support" — and the presets shipped regardless. The presets contradicted the spec that governs them. That row now reads No with the citation.

Verified, and correct as configured

Provider Source IMAP / SMTP
Gmail support.google.com 993 SSL / 465 SSL ✅
Yahoo help.yahoo.com 993 SSL / 465 SSL ✅
iCloud support.apple.com 993 SSL / 587 STARTTLS ✅
Fastmail fastmail.help 993 SSL / 465 SSL ✅
Zoho zoho.com 993 SSL / 465 SSL ✅

A defect this change introduced, and fixed

Typing @gmail.com and then @outlook.com left imap.gmail.com in the form underneath the Outlook warning — one provider's settings beneath another provider's warning. An OAuth-only domain now clears the connection fields.

Found by driving the form in a browser. web/ has no test infrastructure (#172), so nothing automated would have caught it.

Verification

Gate Result
npm run typecheck exit 0
npm run -w web typecheck exit 0
npm run -w web build exit 0
npm run lint exit 0
npm test exit 0 — 87 files, 1,742 tests
Codex adversarial pass 3 findings, all addressed

Live browser check, against a dev server:

Typed Result
someone@gmail.com Gmail preset applies, Advanced collapsed
someone@outlook.com OAuth notice shown, host/port fields empty
unrecognized domain Advanced opens for manual entry (unchanged)

Unresolved — recorded, not guessed at

Apple publishes different usernames per leg for iCloud: the local part (johnappleseed) for IMAP, the full address for SMTP. ImapConnectInput carries one username for both — the same "one field, two independent connections" flaw as the TLS flag. Apple's guidance is to try the full address if the short form fails, so the preset may work as written, but it is untested and the data model cannot express the split. Added to #173.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Added OAuth-only guidance for Outlook.com, Hotmail, Live, and Microsoft 365 domains.
    • Added warnings when app-password connections are unavailable for detected domains.
    • Improved provider presets and clarified supported app-password providers and SMTP security settings.
  • Bug Fixes
    • Corrected hostname handling and removed trailing dots from domain detection.
    • Prevented incompatible connection settings from remaining selected for OAuth-only domains.
  • Documentation
    • Updated mailbox connection guidance for consumer Outlook accounts and custom business domains.

Every preset was checked against its provider's own published documentation.
All hosts, ports and TLS values were correct. The finding was who is listed,
not what they were configured with.

**Microsoft cannot work at all.** Exchange Online states plainly that "Basic
authentication is now disabled in all tenants… no one (you or Microsoft
support) can re-enable" it, and that the deprecation "also prevents the use of
app passwords". Consumer Outlook.com likewise requires Modern Auth/OAuth2 for
POP, IMAP and SMTP. No host/port combination makes an app password work, so
offering `outlook.com`/`hotmail.com`/`live.com` presets handed the operator a
credential their provider will refuse and an opaque auth failure to debug.

specs/mail/mailbox-connection.md §3 already recorded Microsoft 365 business as
**No** and consumer Outlook.com as *unresolved — test before claiming support*.
The presets contradicted the spec that governs them. That row is now resolved
to **No** with the citation, so the two agree.

Typing such an address now says why:

  Outlook does not allow app passwords for IMAP or SMTP — it requires OAuth.
  Connecting this address here will not work.

Also fixes a defect introduced by that notice: typing gmail.com and then
outlook.com left Gmail's hosts in the form underneath the Outlook warning —
one provider's settings beneath another provider's warning. An OAuth-only
domain now clears the connection fields. Found by driving the form; `web/` has
no tests (#172), so nothing else would have caught it.

Verified live in the browser: gmail.com applies its preset, outlook.com shows
the notice with empty fields, an unrecognized domain still opens Advanced.

Unresolved and NOT guessed at: Apple publishes different usernames per leg for
iCloud — the local part for IMAP, the full address for SMTP — and
`ImapConnectInput` carries one `username` for both. Same "one field, two
independent connections" shape as the TLS flag; recorded on #173.

Gates: typecheck, web typecheck, web build, lint all exit 0; 1742 tests pass.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Aug 1, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The mailbox connection form removes Microsoft app-password presets, detects recognized OAuth-only domains, clears incompatible connection settings, and displays a warning. Documentation describes OAuth requirements, advisory detection, and unsupported custom business domains.

Changes

OAuth-only mailbox support

Layer / File(s) Summary
Provider presets and OAuth domain contract
web/src/components/ConnectInboxForm.tsx, specs/mail/mailbox-connection.md
Provider documentation now identifies app-password support and TLS behavior. Microsoft consumer presets were removed. Microsoft OAuth-only domains and detection limits are documented.
Domain detection and form response
web/src/components/ConnectInboxForm.tsx
Domain parsing removes trailing dots. Recognized OAuth-only domains clear IMAP and SMTP settings, reset preset state, and show an accessible warning.

Estimated code review effort: 2 (Simple) | ~10 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Operator
  participant ConnectInboxForm
  participant OAUTH_ONLY_DOMAINS
  participant ConnectionFields
  Operator->>ConnectInboxForm: Enter mailbox domain
  ConnectInboxForm->>ConnectInboxForm: Normalize domain
  ConnectInboxForm->>OAUTH_ONLY_DOMAINS: Check OAuth-only domain
  OAUTH_ONLY_DOMAINS-->>ConnectInboxForm: Return domain match
  ConnectInboxForm->>ConnectionFields: Clear IMAP/SMTP settings
  ConnectInboxForm-->>Operator: Display OAuth-only warning
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: removing Outlook presets because Microsoft does not support app-password authentication.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/outlook-presets-require-oauth

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Adversarial Codex pass, run in place of CodeRabbit (rate limited, zero review
objects on this head). Three findings, all real.

**The list cannot detect Microsoft, and the previous doc let a reader think it
could.** A Microsoft 365 business tenant uses its own domain — `ops@contoso.
example` contains no Microsoft-shaped string — so it matches nothing, gets no
notice, and the operator is invited to enter an app password Exchange Online
will refuse. Subdomains miss for the same reason. Real detection needs MX or
Autodiscover discovery at type-time, which this screen does not do. The list is
now documented as an exact-match hint for well-known CONSUMER domains, with the
gap stated plainly: adding domains narrows it and never closes it.

Widened to the consumer variants a real operator would type: hotmail.co.uk/de/
fr/it/es, live.co.uk/ca/com.au, outlook.co.uk, msn.com, passport.com.

**A trailing DNS dot bypassed both maps.** `user@outlook.com.` is a legal
fully-qualified name resolving to the same host, but matched neither the preset
map nor the OAuth-only map — no preset AND no warning. Domain normalization now
strips it.

**Spec §4's "disabled with an explanation" is only partially satisfied**, and
that is now recorded in the spec rather than left to drift: the notice is
advisory rather than a disabled control (so a diagnostic attempt stays
possible), and a business tenant on its own domain is not recognised at all.
Both deviations are deliberate and neither is silent.

Codex cleared the state machine: gmail.com → outlook.com → gmail.com correctly
re-applies the Gmail preset, because the OAuth-only branch resets
`appliedPresetDomain`.

Gates: typecheck, web typecheck, web build, lint all exit 0; 1742 tests pass.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@specs/mail/mailbox-connection.md`:
- Around line 116-118: Update the Outlook.com resolution section in the
mailbox-connection specification to include direct Microsoft Learn or Microsoft
Support citations for both claims: Exchange Online disabling Basic
authentication and consumer Outlook.com requiring OAuth2 for POP, IMAP, and
SMTP. Place the links beside the relevant resolution text without changing the
documented behavior or claims.

In `@web/src/components/ConnectInboxForm.tsx`:
- Around line 309-320: Update the locked-address initialization in
ConnectInboxForm so OAuth-only domains from initialConfig also clear IMAP/SMTP
hosts and ports and cannot retain stored password-auth settings; preserve the
existing handleAddressChange behavior for editable addresses. Adjust the OAuth
advisory rendering near the existing notice condition so it remains visible when
lockAddress is true for an OAuth-only reconnect.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 8095fc36-3b92-4b5d-bf70-6b1a4d5e9b9e

📥 Commits

Reviewing files that changed from the base of the PR and between 6c0fad5 and 65da70a.

📒 Files selected for processing (2)
  • specs/mail/mailbox-connection.md
  • web/src/components/ConnectInboxForm.tsx

Comment thread specs/mail/mailbox-connection.md Outdated
Comment on lines +116 to +118
| Outlook.com consumer | **No** | Resolved 2026-07-31: Microsoft requires OAuth2 for POP/IMAP/SMTP on consumer accounts too; app passwords are refused |

**Resolution of the Outlook.com row (2026-07-31).** The row above read *unresolved — test before claiming support* while the connect screen shipped `outlook.com`/`hotmail.com`/`live.com` presets regardless. Microsoft's own documentation settles it in both directions: Exchange Online states "Basic authentication is now disabled in all tenants… no one (you or Microsoft support) can re-enable" it, and notes the deprecation "also prevents the use of app passwords"; the consumer Outlook.com guidance likewise requires Modern Auth/OAuth2 for POP, IMAP and SMTP. Those presets are removed, and typing such an address now shows an explicit "requires OAuth" notice instead of offering settings that cannot authenticate.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n -i -C 2 \
  'learn\.microsoft\.com|support\.microsoft\.com|Basic authentication is now disabled|Modern Auth|OAuth2' \
  specs/mail/mailbox-connection.md

Repository: Helpthread/helpthread

Length of output: 1250


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '--- references/links in file ---\n'
rg -n -i '\[(Basic|Modern Auth|OAuth|OAuth2|POP|IMAP|SMTP|app password) [^\]]+\]\(|https?://|learn\.microsoft\.com|support\.microsoft\.com' specs/mail/mailbox-connection.md || true

printf '\n--- nearby sections ---\n'
sed -n '1-180p' specs/mail/mailbox-connection.md

Repository: Helpthread/helpthread

Length of output: 250


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- references/links in file ---'
rg -n -i '\[.*\]\(|https?://|learn\.microsoft\.com|support\.microsoft\.com' specs/mail/mailbox-connection.md || true

printf '%s\n' ''
printf '%s\n' '--- nearby sections ---'
sed -n '1,180p' specs/mail/mailbox-connection.md

Repository: Helpthread/helpthread

Length of output: 18395


Add direct Microsoft citations to the Outlook.com resolution.

This section quotes Microsoft policy and claims both Exchange Online and consumer-outreach guidance, but specs/mail/mailbox-connection.md contains no learn.microsoft.com or support.microsoft.com links for these claims. Add direct references beside the resolution so Agents can validate the auth-block rationale.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@specs/mail/mailbox-connection.md` around lines 116 - 118, Update the
Outlook.com resolution section in the mailbox-connection specification to
include direct Microsoft Learn or Microsoft Support citations for both claims:
Exchange Online disabling Basic authentication and consumer Outlook.com
requiring OAuth2 for POP, IMAP, and SMTP. Place the links beside the relevant
resolution text without changing the documented behavior or claims.

Comment on lines +309 to +320
// An OAuth-only domain clears whatever a previous preset left behind.
// Without this, typing gmail.com and then outlook.com leaves Gmail's hosts
// sitting underneath an "Outlook requires OAuth" warning — the form would
// be showing one provider's settings while warning about another's.
if (nextDomain !== null && OAUTH_ONLY_DOMAINS[nextDomain] !== undefined) {
setImapHost('')
setImapPort('')
setSmtpHost('')
setSmtpPort('')
setAppliedPresetDomain(null)
return
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Handle locked OAuth-only reconnects.

handleAddressChange does not run when lockAddress is true. An existing Outlook mailbox from initialConfig retains its stored IMAP/SMTP settings. Line 479 also hides the OAuth notice in this path. After password entry, the reconnect form enables an unsupported app-password attempt.

Initialize OAuth-only locked addresses with cleared connection fields. Render the advisory for locked reconnects too.

Proposed fix
+  const initialDomain = domainFromAddress(initialConfig?.address ?? '')
+  const initialIsOauthOnly =
+    initialDomain !== null && OAUTH_ONLY_DOMAINS[initialDomain] !== undefined
+
   const [address, setAddress] = useState(initialConfig?.address ?? '')
   const [password, setPassword] = useState('')
-  const [imapHost, setImapHost] = useState(initialConfig?.imapHost ?? '')
+  const [imapHost, setImapHost] = useState(initialIsOauthOnly ? '' : initialConfig?.imapHost ?? '')
   const [imapPort, setImapPort] = useState(
-    initialConfig !== undefined ? String(initialConfig.imapPort) : '',
+    initialConfig !== undefined && !initialIsOauthOnly ? String(initialConfig.imapPort) : '',
   )
-  const [smtpHost, setSmtpHost] = useState(initialConfig?.smtpHost ?? '')
+  const [smtpHost, setSmtpHost] = useState(initialIsOauthOnly ? '' : initialConfig?.smtpHost ?? '')
   const [smtpPort, setSmtpPort] = useState(
-    initialConfig !== undefined ? String(initialConfig.smtpPort) : '',
+    initialConfig !== undefined && !initialIsOauthOnly ? String(initialConfig.smtpPort) : '',
   )
...
-        {oauthOnlyLabel !== undefined && !lockAddress && (
+        {oauthOnlyLabel !== undefined && (
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/src/components/ConnectInboxForm.tsx` around lines 309 - 320, Update the
locked-address initialization in ConnectInboxForm so OAuth-only domains from
initialConfig also clear IMAP/SMTP hosts and ports and cannot retain stored
password-auth settings; preserve the existing handleAddressChange behavior for
editable addresses. Adjust the OAuth advisory rendering near the existing notice
condition so it remains visible when lockAddress is true for an OAuth-only
reconnect.

CodeRabbit reviewed this head (its earlier silence was a rate limit, not a
verdict). Two findings, both real.

**The reconnect path hid the reason.** The notice was gated on `!lockAddress`,
copied from the preset hint above it — but a reconnect screen is precisely
where an operator is most likely to retry a stored Outlook mailbox that has
never been able to authenticate, and hiding the explanation there is backwards.
It now renders on both paths, with wording matched to each.

The stored host/port values are deliberately NOT cleared on the locked path.
That screen exists to review an existing configuration; blanking it would
destroy the context it is for. Clearing stays scoped to the case where the
operator is actively typing a new address.

**The spec asserted Microsoft's policy without citing it.** It quoted the
documentation but linked nothing, so the claim could not be checked by whoever
reads it next. Direct links added for both Exchange Online and consumer
Outlook.com, plus the five provider pages the rest of §3 was verified against.

Gates: typecheck, web typecheck, web build, lint all exit 0.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@zaridan
zaridan merged commit 6673354 into main Aug 2, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant