docs: Phase 3 decision — Gmail via app password; OAuth2 deferred
Evaluated OAuth2 (SPEC §10) and chose not to build it this phase. A self-built, unverified OAuth app suffers Google's 7-day refresh-token expiry in Testing status (or the unverified-warning + restricted-scope verification cost in Production). For a single-user personal tool, a Gmail App Password (2FA) is strictly simpler and reuses the IMAP/SMTP password auth from Phases 1–2. Validated live against a real Gmail account over app-password auth: list/get/ search, send, and a full SMTP-out → IMAP-in round-trip. No code changes were required; the speculative OAuth store fields started mid-session were reverted. OAuth2 remains a clean future addition (schema columns already present). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,79 @@
|
|||||||
|
# emcli — Phase 3 Status Report
|
||||||
|
|
||||||
|
**Date:** 2026-06-22
|
||||||
|
**Branch:** `main`
|
||||||
|
**Phase 3 (as specified):** OAuth2 — Gmail loopback consent + token refresh (SPEC §10).
|
||||||
|
|
||||||
|
## Outcome: OAuth2 deferred; Gmail handled via app password on the existing auth path
|
||||||
|
|
||||||
|
After evaluating the OAuth2 design against the actual use case (a personal, self-built CLI for
|
||||||
|
one Gmail account), we chose **not** to build the OAuth2 machinery in this phase. Gmail is instead
|
||||||
|
accessed with a **Google App Password** over emcli's existing password auth (Phases 1–2) — and it
|
||||||
|
was validated live, end-to-end. No new code was required.
|
||||||
|
|
||||||
|
### Why OAuth2 was deferred (not just "skipped")
|
||||||
|
|
||||||
|
A self-built tool cannot replicate the "Log in with Google" experience of verified clients
|
||||||
|
(Mailspring, Thunderbird, Aerion) without owning a **verified, published** OAuth app. For a
|
||||||
|
personal embed the options are all poor:
|
||||||
|
|
||||||
|
- **Unverified app in "Testing" publishing status** → Google expires refresh tokens **every 7
|
||||||
|
days**, forcing weekly re-consent. Unusable for an unattended tool.
|
||||||
|
- **Unverified app in "Production" status** → refresh tokens are long-lived, but every login shows
|
||||||
|
the "Google hasn't verified this app" warning, and `https://mail.google.com/` is a **restricted
|
||||||
|
scope** (full verification needs a paid security assessment). Workable, but heavy setup for one
|
||||||
|
user.
|
||||||
|
- **App Password (chosen)** → requires only that 2-Step Verification is enabled. Yields a permanent
|
||||||
|
16-character credential usable with standard IMAP/SMTP password auth. No consent screen, no
|
||||||
|
7-day expiry, no client ID/secret, no Google Cloud project. Revoked only on manual revoke,
|
||||||
|
main-password change, or disabling 2FA.
|
||||||
|
|
||||||
|
For a single-user personal tool, the App Password is strictly simpler and more robust, and it
|
||||||
|
reuses the IMAP/SMTP password path already shipped and tested in Phases 1–2.
|
||||||
|
|
||||||
|
> OAuth2 remains a clean future addition (SPEC §10, schema columns already present) if emcli ever
|
||||||
|
> needs multi-user/provider support or a verified app. The mechanism would be: x/oauth2 loopback
|
||||||
|
> consent + an XOAUTH2 `sasl.Client` (go-sasl ships OAUTHBEARER but not XOAUTH2) wired into the
|
||||||
|
> existing `mail.Dial`/`SendSMTP` auth branch. Nothing in Phases 1–2 blocks it.
|
||||||
|
|
||||||
|
## Live validation (real Gmail account, app password)
|
||||||
|
|
||||||
|
A real personal Gmail account over verified TLS (`imap.gmail.com:993`, `smtp.gmail.com:465`),
|
||||||
|
authenticating with a Gmail **app password** via the existing password auth:
|
||||||
|
|
||||||
|
- **`list`:** returned real INBOX headers (newest-first), `has_attachments` correct.
|
||||||
|
- **`get`:** returned the full decoded plain-text body of a Gmail message.
|
||||||
|
- **`search`:** `--from`/`--subject-contains` narrowed correctly server-side.
|
||||||
|
- **`send`:** delivered a message to `me@stevecliff.com` — `{"sent":true}`, exit 0.
|
||||||
|
- **Round-trip:** a self-addressed send arrived and was found via IMAP `search` on the first poll
|
||||||
|
— full SMTP-out → IMAP-in proof.
|
||||||
|
- All over emcli's existing code; the app password is sealed at rest in the encrypted DB.
|
||||||
|
|
||||||
|
## Gmail setup (app password)
|
||||||
|
|
||||||
|
1. Enable **2-Step Verification** on the Google account (required — the app-passwords page is
|
||||||
|
hidden otherwise).
|
||||||
|
2. Create a 16-character app password at <https://myaccount.google.com/apppasswords> (name it
|
||||||
|
e.g. "emcli").
|
||||||
|
3. Enable IMAP in Gmail: **Settings → Forwarding and POP/IMAP → Enable IMAP**.
|
||||||
|
4. Add the account (app password is entered as the password; spaces are stripped):
|
||||||
|
```
|
||||||
|
emcli account add --name gmail --mode RW \
|
||||||
|
--imap-host imap.gmail.com --imap-port 993 --imap-security tls \
|
||||||
|
--smtp-host smtp.gmail.com --smtp-port 465 --smtp-security tls \
|
||||||
|
--username you@gmail.com --password '<16-char-app-password>'
|
||||||
|
```
|
||||||
|
|
||||||
|
App passwords are revoked by a main-password change or disabling 2FA; regenerate and re-run
|
||||||
|
`account add` if that happens.
|
||||||
|
|
||||||
|
## Code changes
|
||||||
|
|
||||||
|
**None.** The speculative OAuth store fields/tests started early in the session were reverted; the
|
||||||
|
working tree is byte-for-byte the Phase 2 code plus this document. Full suite remains green.
|
||||||
|
|
||||||
|
## Known limitations / deferred
|
||||||
|
|
||||||
|
- **OAuth2 (SPEC §10)** — deferred as above; revisit for multi-user/provider or a verified app.
|
||||||
|
- **Phase 4 — admin TUI + `doctor`** — unchanged; next up.
|
||||||
|
- Carry-over Minor items from Phase 1 remain open.
|
||||||
Reference in New Issue
Block a user