feat(cli): browser session login + session auth on most routes#189
Merged
Conversation
Let `rift login` authenticate via the browser (reusing the existing magic-link + OAuth sign-in) and accept a session token OR an API key on most routes — so humans don't need to paste a long-lived `rl_live_` key. API keys remain the path for CI/automation (`rift login --api-key`). Server (builds on #185's composable credential flags): - Add SESSION to require_auth(...) on links, domains, apps, webhooks, analytics, affiliates, sources CRUD, publishable-keys, users. x402/anon untouched. secret-keys create/confirm stay key-only by design (the email-code teammate-provisioning flow; sessions use /secret-keys/issue). - New GET /v1/auth/cli/start (public, loopback-validated → bounces the browser to the dashboard) and POST /v1/auth/cli/authorize (session-gated; mints a `rift-cli`-tagged session, returns the token). Dashboard: new /cli/authorize page reuses /signin?next= (magic-link + OAuth), then POSTs to mint and hands the token to the CLI's loopback listener. CLI: `rift login` opens the browser with a one-shot loopback listener + state nonce; `--api-key` preserves the paste flow. New SessionToken credential, backward-compatible config (secret_key/session_token both optional), logout revokes the session, credential-agnostic 401 message. Metering unchanged: session calls are already billed via the service-layer QuotaService; the middleware usage-log stays key/anon-only. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Contributor
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What & why
Today the CLI authenticates only with a pasted
rl_live_secret key. This lets a human runrift login, authenticate in the browser (reusing the existing magic-link and OAuth sign-in), and have the CLI use a session token — so most routes accept a session token OR an API key. API keys stay the path for CI/automation viarift login --api-key.Builds on #185's composable credential-flags engine (
require_auth(SESSION | SECRET | …)), so enabling sessions per route is a one-flag policy edit.Server
SESSIONadded torequire_auth(...)on: links, domains (both groups), apps, webhooks, analytics, affiliates, sources CRUD, publishable-keys, users. x402/anon flags untouched.secret-keyscreate/confirm intentionally left key-only — documented inline as the email-code "provision a key for a teammate" flow; sessions already have/v1/auth/secret-keys/issue.api/auth/sessions/):GET /v1/auth/cli/start— public; validates a loopbackredirect_uri, bounces the browser to the dashboard/cli/authorize.POST /v1/auth/cli/authorize—require_auth(SESSION); mints arift-cli-tagged session viaissue_session, returns{ token }. CORS restricts who can read it.Dashboard (
marketing/)/cli/authorizepage +cli-authorize.tsx: reuses/signin?next=(magic-link + OAuth) for not-signed-in users, then an Approve button POSTs to mint and navigates the browser to the CLI's loopback listener with the token.CLI
rift login→ browser flow: one-shot127.0.0.1listener +statenonce, opens/v1/auth/cli/start, captures the token, confirms via/v1/auth/me, persists.rift login --api-key→ preserves today's paste-and-verify flow (headless/CI).SessionTokencredential; backward-compatible config (secret_key/session_tokenboth optional,skip_serializing_if);logoutbest-effort revokes the session; credential-agnostic 401 messaging.Metering
No change. Billable quota is enforced in the service-layer
QuotaService(shared by all transports), so session/CLI calls are already counted. The middleware usage-log stays key/anon-only by design (its only consumer is the anonymous IP limit).Security
redirect_uriis loopback-only (validated on both endpoints) — the token can only ever be handed to127.0.0.1/localhost/[::1].statenonce binds the browser round-trip to the CLI instance.rift-clisession is minted (independently revocable); the browser's cookie token is never exposed; the token only ever appears in a loopback URL.Tests / checks
fmt+clippy --all-targets -D warnings+ tests (incl. architecture tests and newis_loopback_redirectcases).fmt+clippy+ tests (new session-config round-trip + legacy-config back-compat).tsc --noEmit+eslintclean on touched/new files.Try it (local)
Set
MARKETING_URLto your Next dev URL, run the server +npm run devinmarketing/, thencargo run -- login --base-url http://localhost:3000→ sign in → Approve → terminal unblocks.rift logoutrevokes.🤖 Generated with Claude Code