Fix Outlook sign-in crash and complete the OAuth login flow #2

Merged
JMR-dev merged 1 commits from fix-outlook-login-flow into main 2026-06-30 04:26:09 +00:00
JMR-dev commented 2026-06-30 04:19:17 +00:00 (Migrated from github.com)

Summary

"Sign in with Microsoft" (Outlook) crashed on the redirect back from the browser and never completed a login. This fixes three layered bugs plus some hardening in the account-setup flow. Verified end-to-end on a real Outlook account on a physical device (redirect → token exchange → account added → inbox sync).

Root causes & fixes (in flow order)

  1. Redirect crash (the reported symptom) — AppAuth's RedirectUriReceiverActivity extends AppCompatActivity, so it needs a Theme.AppCompat theme. This app is pure Compose (MainActivity : ComponentActivity, framework Theme.Material), and AppAuth declares that activity with no theme of its own, so it inherited the Material app theme and threw "You need to use a Theme.AppCompat theme" the instant Android launched it to deliver the redirect. Fix: give it the translucent AppCompat theme AppAuth itself applies to AuthorizationManagementActivity. Build-type independent — not an R8 issue.
  2. Token exchange rejected with AADSTS70011 ("must include a 'scope' input parameter") — one consent spans two Microsoft resources (Graph for send, Exchange Online for IMAP), so Microsoft mints one token per resource and the code→token exchange must name a single resource. AppAuth's createTokenExchangeRequest() sends no scope. Fix: build the exchange request explicitly with a single-resource scope.
  3. "Invalid ID Token" / nonce mismatch — hand-building the exchange request for (2) must replicate every field createTokenExchangeRequest() sets, including the nonce AppAuth validates the id_token against (and the PKCE code verifier). Fix: carry the nonce + verifier over.

Hardening (same flow): guard the previously unguarded createAuthIntent() launch (AppAuth throws ActivityNotFoundException when no browser is available) so it surfaces an error instead of crashing; dispose the AuthorizationService that createAuthIntent() leaked; log sign-in failures via Log.d (auto-stripped from release builds by the existing ProGuard rule).

Test plan

  • ./gradlew :app:assembleDebug green
  • ./gradlew :app:testDebugUnitTest green
  • Manual: real Outlook sign-in on a physical device completes without crashing; account added, inbox syncs
  • Not yet exercised: sending Outlook mail (Graph sendMail + SMTP fallback)

🤖 Generated with Claude Code

## Summary "Sign in with Microsoft" (Outlook) crashed on the redirect back from the browser and never completed a login. This fixes three layered bugs plus some hardening in the account-setup flow. **Verified end-to-end on a real Outlook account on a physical device** (redirect → token exchange → account added → inbox sync). ## Root causes & fixes (in flow order) 1. **Redirect crash (the reported symptom)** — AppAuth's `RedirectUriReceiverActivity` extends `AppCompatActivity`, so it needs a `Theme.AppCompat` theme. This app is pure Compose (`MainActivity : ComponentActivity`, framework `Theme.Material`), and AppAuth declares that activity with no theme of its own, so it inherited the Material app theme and threw _"You need to use a Theme.AppCompat theme"_ the instant Android launched it to deliver the redirect. **Fix:** give it the translucent AppCompat theme AppAuth itself applies to `AuthorizationManagementActivity`. Build-type independent — not an R8 issue. 2. **Token exchange rejected with `AADSTS70011` ("must include a 'scope' input parameter")** — one consent spans two Microsoft resources (Graph for send, Exchange Online for IMAP), so Microsoft mints one token per resource and the code→token exchange must name a single resource. AppAuth's `createTokenExchangeRequest()` sends no scope. **Fix:** build the exchange request explicitly with a single-resource scope. 3. **"Invalid ID Token" / nonce mismatch** — hand-building the exchange request for (2) must replicate every field `createTokenExchangeRequest()` sets, including the nonce AppAuth validates the id_token against (and the PKCE code verifier). **Fix:** carry the nonce + verifier over. **Hardening (same flow):** guard the previously unguarded `createAuthIntent()` launch (AppAuth throws `ActivityNotFoundException` when no browser is available) so it surfaces an error instead of crashing; dispose the `AuthorizationService` that `createAuthIntent()` leaked; log sign-in failures via `Log.d` (auto-stripped from release builds by the existing ProGuard rule). ## Test plan - [x] `./gradlew :app:assembleDebug` green - [x] `./gradlew :app:testDebugUnitTest` green - [x] Manual: real Outlook sign-in on a physical device completes without crashing; account added, inbox syncs - [ ] Not yet exercised: sending Outlook mail (Graph `sendMail` + SMTP fallback) 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Sign in to join this conversation.