feat(auth): one approval link for device sign-in, and a page that says what it grants (#83)

The headless flow printed a short code to retype into a bare "Approve"
form. Now `bdrive login --device` prints a single link — the token lives in
the path (/auth/device/<token>), so there is nothing to read off one screen
and type into another.

The page it opens is a consent page rather than a text field: it names the
account the device would act as, offers Switch account (logout now honors
?next, so you land back here), and shows the device name, OS, and the
address the server observed. That matters because this flow's weakness is a
stranger sending you their pending link; an anonymous "Approve" gives you
nothing to notice with. Approval is still a POST from the page, so a link
alone cannot grant, and SameSite=Lax keeps a cross-site form out.

Also aligns the /auth/* pages with the app's tokens, which had drifted:
card #0c0e10 vs --color-card #15171b, 8px controls vs --radius-ctl 7px,
hand-picked #ff9b91/#6fd699 vs --color-del/--color-add. The style block now
declares the tw.css tokens by name and every rule uses them.

Older CLIs still print /auth/device?code=…, so that shape 303s to the path
form; a pre-0.13 hub returning no verify_url still gets the old
type-the-code instruction from the CLI.


Claude-Session: https://claude.ai/code/session_016aYntCWwdUhpzUfEk3ddyJ

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Snow W. Lee (Sungwon)
2026-07-30 10:41:27 +09:00
committed by GitHub
co-authored by Claude Opus 5
parent 2f68bbe92e
commit dfb9da3260
11 changed files with 198 additions and 70 deletions
@@ -30,8 +30,8 @@ bdrive login https://your-hub
This opens your browser, and the terminal finishes on its own. On a headless or
SSH machine login falls back to the device-code flow automatically (no TTY, or
no browser can open): it prints a short code you approve from any signed-in
browser. `bdrive login --device` forces that flow.
no browser can open): it prints one link you open in any signed-in browser and
approve — nothing to retype. `bdrive login --device` forces that flow.
`bdrive login --status` shows the current server and account.
+1 -1
View File
@@ -9,7 +9,7 @@ One binary, `bdrive` — the CLI, the sync daemon, and the web server.
| Command | Description |
|---|---|
| `bdrive login [server-url]` | Sign this device in. Browser flow; `--device` forces the code flow, and shells without a TTY (agents, CI, SSH) fall back to it automatically. Default server is beardrive.ai — the managed cloud, free personal workspace on signup; pass your hub URL to self-host. Switch hubs with `bdrive login <new-url>`. `--status` shows the current server and account |
| `bdrive login [server-url]` | Sign this device in. Browser flow; `--device` forces the approval-link flow, and shells without a TTY (agents, CI, SSH) fall back to it automatically. Default server is beardrive.ai — the managed cloud, free personal workspace on signup; pass your hub URL to self-host. Switch hubs with `bdrive login <new-url>`. `--status` shows the current server and account |
| `bdrive logout` | Sign this device out — clear the saved token and account. `--forget` also drops the remembered server |
| `bdrive init [folder]` | Create or connect a project and start syncing — the mount is always exactly the folder named. Interactive on a TTY; flags (`--name`, `--project`, `--server`, `--only`, `--yes`) for scripts. Also installs the agent skill, registers agent sync hooks for detected platforms (`--no-hooks` skips the hooks only), and prints the project's hub link. Re-run to resume |
| `bdrive stop [folder]` | Stop syncing — daemon and agent sync hooks both pause. Files stay on disk; `bdrive init` resumes |
@@ -64,8 +64,12 @@ to the CLI's loopback listener and the terminal finishes on its own, storing a
long-lived per-device token that is revocable server-side.
On headless or SSH machines login falls back to the device-code flow
automatically (no TTY, or no browser can open): it prints a short code to
approve from any signed-in browser. `bdrive login --device` forces that flow.
automatically (no TTY, or no browser can open): it prints one approval link to
open in any signed-in browser. The link carries the request, so there is no code
to retype; the page it opens names the account you would be granting, the device
asking, its OS, and the address it came from, so an unexpected approval request
is visible rather than anonymous. Approving is a POST from that page — a link
alone can't grant. `bdrive login --device` forces that flow.
Every sync and every `bdrive init` then authenticates with that token. The hub's
device registry records per-device name, OS, account, and the IP the server
+2 -1
View File
@@ -54,7 +54,8 @@ to set up BearDrive project <project-id> on <hub-url>. Ask me which folder to sy
```
The agent fetches that page and works through it — install the CLI, sign in
with a device code, connect the project, register the sync hooks. You copy one
(it prints a link you approve in the browser), connect the project, register
the sync hooks. You copy one
thing; the agent handles every deviation — already installed, no Homebrew,
browser sign-in, wrong folder. On BearDrive Cloud, drop `on <hub-url>`
sign-in defaults to beardrive.ai.