Skip to content

mega: OAuth-direction sume login (access/refresh) with API-key CI retained #15

Description

@chasehuh

Summary

Evolve sume login from today’s browser/device approval → long-lived API key flow toward a true OAuth-style credential model (access token + refresh token, CLI-managed refresh), aligned with peers like Railway CLI—while keeping API keys as a first-class path for CI/agents.

Why This Matters

  • Interactive human CLI login currently stores a durable API key in ~/.sume-com/config.json. That is simple and matches Stripe/OpenAI-style developer APIs, but it is a weaker secret-on-disk model than short-lived access + refresh.
  • Remote MCP Connect on sume-com is moving toward OAuth (DCR, AS, Clerk IdP). CLI auth and MCP auth do not have to be identical, but diverging forever (keys-only CLI vs OAuth MCP) increases user confusion (“I logged in on the web/MCP but CLI still wants a key”) and duplicate login UX.
  • Peers show both patterns are valid; this issue makes an explicit product decision and implementation path for OAuth-direction login, with evidence—not a vague “add OAuth someday.”

Conversation Context

Decisions / constraints from product discussion (2026-07-15):

  • Sume CLI today is correctly understood as: login UX mints an API key, not OAuth access/refresh for API calls. Nobody refreshes; key lives until revoke/logout.
  • Peer survey:
    • Railway CLI: real OAuth 2 (PKCE + device-code), stores accessToken + refreshToken, CLI itself refreshes before authenticated commands; CI uses separate env tokens (RAILWAY_TOKEN / RAILWAY_API_TOKEN).
    • Modal CLI: browser flow mints long-lived token_id/token_secret in ~/.modal.toml — closer to Sume’s current shape (no OAuth refresh loop).
    • Higgsfield CLI (@higgsfield/cli): browser OAuth PKCE via Clerk; short-lived sessions; public docs emphasize re-login on expiry more than silent refresh.
  • Product stance from discussion: for a developer API product, staying API-key-centric for agents/CI is still recommended; invest in full OAuth+refresh when interactive human CLI + short-lived secrets become important—or to align login UX with MCP OAuth.
  • This issue is the CLI-side tracking item for that OAuth direction. Platform endpoints may live in sumelabs/sume-com (web/API); call that out as a cross-repo dependency rather than inventing auth only inside the CLI binary.

Current Behavior

User-facing

sume login                 # opens app, device approval
sume login --no-browser    # print verification URL
sume auth setup --api-key  # manual key
export SUME_API_KEY=...    # env override

On success, CLI stores apiKey (+ authMode, URLs) under ~/.sume-com/config.json (mode 0600).

Protocol today

  1. POST {appBaseUrl}/api/cli/auth/device/startdevice_code, user_code, verification_uri(_complete), interval, expires_in
  2. User approves in browser on app origin (docs: app.sume.com; needs verification vs current www.sume.com branding)
  3. POST {appBaseUrl}/api/cli/auth/device/poll → on approved, returns api_key.key
  4. All subsequent public API calls use x-api-key: <key> (or Authorization: Bearer <key> if authMode: bearer) against https://api.sume.com/v1

Code / docs SoT (this repo)

  • src/lib/login-client.ts — device start/poll types; poll success shape is api_key, not OAuth tokens
  • src/lib/config.tsapiKey / authMode only; no refresh fields
  • src/lib/api-client.ts — HTTP boundary; API-key header modes
  • src/commands/auth.ts / login command wiring
  • docs/architecture.md § Auth And Config
  • README.md § Configure Auth
  • test/cli.test.ts — “logs in with the browser device flow and stores the returned API key”
  • Agent skill agent/sume-auth/SKILL.md — documents key-centric login

What this is not today

  • No access_token / refresh_token stored for API calls
  • No CLI-side token refresh loop
  • No PKCE authorization-code exchange against an AS token endpoint for CLI sessions

Desired Behavior

Product outcome

  1. sume login (interactive) can complete an OAuth-style flow and store credentials that are not a long-lived raw developer API key by default (access + refresh, or equivalently short-lived access with refresh).
  2. CLI refreshes access tokens automatically before authenticated API calls when expired/near-expiry (Railway-like ensure_valid_token), without prompting the user every hour.
  3. Automation remains first-class: SUME_API_KEY / sume auth setup --api-key continue to work for CI/agents (document precedence clearly).
  4. Logout / revoke clears local OAuth creds and documents how to revoke server-side.
  5. Docs + sume-auth skill updated so agents know which mode they are in (oauth vs api_key) without printing secrets.

Observable login success (OAuth mode)

sume auth status --json should report something like:

{
  "authenticated": true,
  "mode": "oauth",
  "has_access_token": true,
  "has_refresh_token": true,
  "access_expires_at": "2026-07-15T12:00:00.000Z",
  "api_base_url": "https://api.sume.com/v1"
}

(Exact field names can match existing status schema; do not print token values.)

API calls (OAuth mode)

Authorization: Bearer <access_token>

against the public API (or whatever audience/resource the platform AS defines for CLI). Needs verification with sume-com whether CLI tokens share MCP mcp:read audience or a dedicated cli / api scope.

Source Of Truth

Internal (sumelabs/cli)

  • src/lib/login-client.ts — replace or extend device poll response to OAuth tokens or add parallel OAuth PKCE client
  • src/lib/config.ts — persist accessToken, refreshToken, tokenExpiresAt, authMode: "oauth" | "x-api-key" | "bearer"
  • src/lib/api-client.ts — attach Bearer access token; call ensure-valid before requests
  • docs/architecture.md, README.md, agent/sume-auth/SKILL.md
  • test/cli.test.ts — login + refresh + precedence tests

Platform dependency (sumelabs/sume-com) — Needs verification / likely companion work

  • Existing CLI device routes under app: /api/cli/auth/device/* (mint API keys today)
  • MCP/OAuth AS work on web/API (packages/mcp-oauth, www authorize, possible future mcp.* DCR/token). CLI OAuth should reuse platform AS where possible rather than inventing a third auth island.
  • Domain note: CLI docs still say app.sume.com; production branding may be www.sume.com — implementers must verify live login origins.

External peer evidence

Peer Pattern Refresh owner Automation
Railway CLI + login docs OAuth PKCE + device-code; ~/.railway/config.json access+refresh CLI Env project/account tokens
Modal CLI Browser mints long-lived token pair in ~/.modal.toml None (durable secret) MODAL_TOKEN_ID / MODAL_TOKEN_SECRET
Higgsfield CLI (@higgsfield/cli) OAuth PKCE (Clerk) Weak / re-login per public docs Unclear
Sume CLI today Device/browser → API key None SUME_API_KEY

Railway is the primary reference for desired interactive OAuth+refresh. Modal/Sume-today is the reference for why API keys must remain.

Proposed Credential / Config Schema

Local config (~/.sume-com/config.json) — OAuth mode

{
  "authMode": "oauth",
  "accessToken": "<redacted-in-docs>",
  "refreshToken": "<redacted-in-docs>",
  "tokenExpiresAt": "2026-07-15T12:00:00.000Z",
  "baseUrl": "https://api.sume.com/v1",
  "appBaseUrl": "https://www.sume.com"
}

Local config — API key mode (unchanged)

{
  "authMode": "x-api-key",
  "apiKey": "<redacted-in-docs>",
  "baseUrl": "https://api.sume.com/v1"
}

Precedence (proposed)

  1. SUME_API_KEY (forces API-key mode for that process)
  2. Explicit OAuth tokens in config when authMode=oauth
  3. Config apiKey
  4. Unauthenticated → doctor/login guidance

Validation Rules

  • OAuth mode must never log access/refresh tokens (agent redaction paths included).
  • Refresh failures that require user interaction should surface a clear sume login next action (not infinite spin).
  • Device-code path must remain for headless (--no-browser), either as OAuth device grant or as “mint key” legacy—product choice below.
  • Backward compatibility: existing apiKey configs keep working without forced re-login on upgrade (migration path documented).

Implementation Notes

Decision gate (implementer must pick and document in PR)

Option A — Railway-like (preferred for “OAuth direction”)
Interactive sume login uses AS authorization-code + PKCE (browser) and/or OAuth device-code; store access+refresh; CLI refreshes; API keys remain for CI only.

Option B — Hybrid mint (closer to Modal)
Keep minting API keys via improved device flow, but add optional short-lived session tokens later. Does not fully satisfy “OAuth direction” alone—only accept if platform cannot ship CLI OAuth AS endpoints soon.

Option C — Thin wrapper over MCP user OAuth
Reuse the same AS/scopes as remote MCP tokens for CLI HTTP calls. Highest alignment with MCP Connect; requires confirmed API acceptance of those bearer tokens for /v1/*.

Default recommendation from conversation: Option A or C, not B, if this issue is explicitly “oauth 방향으로 변경”.

Likely files to modify (CLI)

  • src/lib/login-client.ts — OAuth start/exchange/refresh helpers
  • src/lib/config.ts — token fields + migration from key-only
  • src/lib/api-client.tsensureValidAccessToken() before requests
  • src/commands/auth.ts / login command — flags: --api-key-mode escape hatch?
  • docs/architecture.md, README.md, agent/sume-auth/**
  • test/cli.test.ts — mock AS token + refresh

Likely platform work (sume-com) — link companion issue if split

  • CLI-oriented OAuth client (PKCE public client) + redirect/device support
  • Token audience/scopes for public API calls
  • Revocation endpoint behavior
  • Deprecation policy for “device poll returns api_key” (keep for compat or sunset)

Flow (Option A target)

  1. sume login → register/discover AS (or use configured authorize URL)
  2. Browser PKCE or device-code → user authenticates (Clerk behind platform AS)
  3. Token endpoint → access + refresh → write config
  4. sume avatars list → ensure valid access → Authorization: Bearer …api.sume.com/v1
  5. On 401/expiry → refresh → retry once → else ask re-login

Tests

  • Login stores oauth fields, never prints secrets in --json agent mode
  • Refresh rotated before expiry; concurrent requests don’t stampede refresh (Needs verification of locking strategy)
  • SUME_API_KEY overrides oauth config
  • Existing key-config users still authenticate after upgrade
  • Headless --no-browser path covered

Edge Cases And Risks

  • Security: refresh token on disk is still high value—file mode 0600, redact in doctor/mcp logs, document revoke.
  • Platform lag: CLI cannot fake OAuth if sume-com still only mints API keys from device poll—ship platform endpoints first or behind feature flag.
  • Domain drift: app.sume.com vs www.sume.com login origins must match live AS.
  • MCP confusion: users may expect one login for CLI + Cursor MCP; document whether tokens are shared or separate.
  • Agents: long-lived keys are easier for agents; oauth refresh must work non-interactively after first login or agents will flap.
  • Breaking change risk: forcing oauth-only login without key escape hatch breaks CI—non-goal to remove keys.

Non-Goals

  • Removing SUME_API_KEY / sume auth setup --api-key
  • Replacing remote MCP OAuth / DCR work (that stays in sume-com; coordinate only)
  • Building a full desktop auth UI inside the CLI beyond opening the system browser
  • Changing public API product auth away from API keys for SDK users
  • Higgsfield-parity Clerk embedding inside the CLI binary

Acceptance Criteria

  • Interactive sume login can authenticate via OAuth-direction flow and persist access (+ refresh if issued) without treating that credential as a dashboard-minted long-lived API key by default
  • CLI auto-refreshes access tokens for subsequent commands when refresh is available
  • SUME_API_KEY and manual API-key setup still work and take documented precedence for automation
  • sume auth status --json / sume doctor --agent --json report mode safely (no token leakage)
  • sume auth logout clears oauth (+ key) local creds
  • README + architecture + sume-auth skill updated
  • Unit/integration tests for login, refresh, precedence, redaction
  • Cross-repo note: platform endpoints documented or companion sume-com issue linked

QA Plan

  1. Unit: mocked token endpoint + refresh in pnpm test / existing cli test harness
  2. Manual interactive: sume login against dev AS → sume auth status --jsonsume doctor → one read command (sume health/me/catalog as applicable)
  3. Expiry simulation: force near-expiry / invalidate access → next command refreshes silently
  4. CI path: SUME_API_KEY=… sume doctor --agent --json still passes without oauth config
  5. Negative: revoke refresh server-side → CLI prompts re-login cleanly

Suggested PR Scope

L if platform AS endpoints must be built in the same change set.
M if platform already exposes CLI OAuth token endpoints and this repo is client-only.

Suggested split:

  1. sume-com: CLI OAuth AS support (PKCE/device + scopes + token audience) — companion mega-issue if not already covered
  2. sumelabs/cli: config + api-client refresh + login UX + tests + docs

Ship behind a flag (sume login --oauth or env SUME_LOGIN_OAUTH=1) until stable, then make oauth the default interactive path.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions