feat(cli): add 'qr' command for transferring API token to mobile apps - #545
Merged
Conversation
Contributor
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
Contributor
Author
|
@junhoyeo @IvGolovach Could you check this PR? It would simplify login for mobile apps. |
…dact plaintext
The hand-rolled `format!(r#"{{"token":"{}","username":"{}"}}"#, ...)` payload
broke or injected fields whenever a username or token contained `"` or `\`.
Build the JSON via `serde_json::to_string` so embedded quotes/backslashes can't
escape the structure.
The previous flow also printed the bearer token to the terminal twice — once in
the QR and once in plaintext below it — with no confirmation. Anyone able to
see the screen (screen share, recorded demo, office camera) could replay the
token. The token is now only inside the QR (no plaintext echo), and rendering
is gated on a `[y/N]` prompt unless `--yes` is passed. Non-TTY stdin without
`--yes` aborts instead of dumping the QR into a log stream.
Constraint: QR command was already a published surface; --yes flag preserves scripted use
Rejected: drop --yes entirely | breaks every existing automation that pipes from this command
Confidence: high
Scope-risk: narrow
Directive: do NOT re-add a plaintext token echo below the QR — the QR is the *only* place the token should appear on screen
Not-tested: interactive confirmation flow (would need a pty harness); covered manually
junhoyeo
force-pushed
the
feature/qr-login
branch
from
May 24, 2026 16:12
f4a15ae to
87e47b2
Compare
This was referenced May 25, 2026
junhoyeo
added a commit
that referenced
this pull request
May 25, 2026
…rift (#593) Schema: - Add 0010_submit_count_safety.sql to ALTER TABLE submissions ADD COLUMN IF NOT EXISTS submit_count. The column is referenced by schema.ts and written on every /api/submit call, but no earlier .sql migration adds it; prod has it because somebody once ran drizzle-kit push directly, so a fresh drizzle-kit migrate on CI/staging/dev currently breaks /api/submit. Verified locally against postgres:16. API: - GET /api/users/[username]/devices: aggregated per-device totals (tokens, cost, input/output, active days) for a user's submission devices, sourced from submitted_devices LEFT JOIN daily_breakdown. - GET /api/users/[username]/devices/[deviceId]: per-device detail with day-by-day contributions. Cross-user device ids silently 404. - PATCH /api/settings/devices/[deviceId]: session-authenticated rename (or clear, via name: null). Validates length and rejects control characters; ownership enforced in the same WHERE. Helpers: - src/lib/devices/shared.ts: deviceDisplayLabel() and toIsoString() shared across the three routes so the public label format cannot drift. Local dev: - scripts/seed-dev.ts: idempotent synthetic seed (3 users × 2 devices × 14 days + 1 group). Refuses any non-localhost DATABASE_URL. Ported from #389 onto main's submitted_devices model (#517) rather than that PR's submissions.source_id schema rewrite. #389's CLI source-id lockfile and profile Devices tab UI are deliberately not in this PR; they want separate decisions (CLI lock vs main's existing device.id payload; UI as a styled-components review pass). Constraint: do not regress main's submitted_devices schema or rehash the migrated multi-machine model Rejected: include #389's index-cleanup migration (drop idx_submissions_user_id et al.) | needs a fresh pg_stat_user_indexes audit on current prod before dropping anything in a single transaction Rejected: bundle #389's CLI source-id lockfile | main already sends device.id via #517/#545; the lockfile is a behavior change, not a port Confidence: high Scope-risk: narrow Directive: /api/users/[username]/devices is public — match it to /api/users/[username]'s visibility model when extending; the rename endpoint is the only auth-gated piece Not-tested: interaction with the existing rank cache (rankTotal in embed routes is unaffected)
Owner
|
@eugenn this has been merged to v3.0.0: https://github.com/junhoyeo/tokscale/releases/tag/v3.0.0 thanks for the contribution! |
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.
Summary
Adds a new
tokscale qrsubcommand that renders the locally saved API token (from~/.config/tokscale/credentials.json) as a QR code directly in the terminal. The QR payload is a JSON blob containing both the token and the username, so a companion app can authenticate and identify the user from a single scan.Why
Tokscale is desktop-first today, but the account/leaderboard is a natural fit for a companion mobile app (on-the-go leaderboard, weekly summary push notifications, badge sharing, etc.). Mobile apps need a frictionless way to log in — typing a 50+ char
tt_xxxtoken on a phone keyboard, or running the browser device-code flow a second time on mobile, is exactly the kind of paper-cut that kills a companion-app funnel.Today's options are all unergonomic:
tt_tokens are 50+ characters; mobile keyboards make this miserable and error-prone.A QR code displayed by the already-trusted CLI is the de-facto standard for this exact problem (Discord, Signal, WhatsApp Web, GitHub CLI device flow all do variants of it):
tokscale qron the machine where they already authenticated viatokscale login.JSON (not bare token, not URL) was chosen for the payload so mobile apps can also display the username next to the avatar before activating the session — i.e. show a
Sign in as @eugenn?confirmation step, which is a meaningful safety check when the QR may have been displayed on a screen the user no longer fully controls.What changed
tokscale qrsubcommand wired incrates/tokscale-cli/src/main.rs(added to theCommandsenum and routed toauth::show_qr).auth::show_qr()incrates/tokscale-cli/src/auth.rs:load_credentials()helper. If the user isn't logged in, prints the same hint thatwhoamiuses and exits cleanly (no crash, no error).{"token":"tt_xxx","username":"<name>"}and feeds it toqrcode::QrCode::new.unicode::Dense1x2(two modules per terminal row → compact, square-aspect output). Colors are inverted so the QR is visible on a typical dark terminal background.Token:andUser:plaintext below the QR for fallback / debugging.qrcode = "0.14"added undercrates/tokscale-cli/Cargo.toml. Default features only; no FFI; MIT/Apache-2.0 dual-licensed.No server-side, frontend, or shared-protocol changes. The mobile-app contract is just "scan, parse JSON, use
tokenas bearer" — exactly the auth the CLI itself uses against/api/auth/token.Usage
Decoded QR contents:
{"token":"tt_5be17ed8c9b94...","username":"eugenn"}Validation
cargo build -p tokscale-cli(debug + release) — clean, no new warnings.tokscale qragainst a real credentials file. QR scans cleanly with iOS Camera and yields the expected JSON payload. The "not logged in" branch was exercised by temporarily movingcredentials.jsonout of the way — prints the same hint aswhoami, exits 0.login,logout,whoami,submitetc. are byte-for-byte unchanged.Risk
tokscale qrare entirely unaffected.~/.config/tokscale/credentials.json— no new secret material is created or exposed by this PR. The QR is only ever rendered to the user's own terminal output; there is no networking and no file write.qrcode0.14): pure Rust, no FFI, ~1.5k LOC, widely used.Follow-ups (not in this PR)
/api/auth/tokenvalidator before persisting the token, mirroringlogin_with_tokeninauth.rs.tokscale://login?token=...&username=...once mobile apps register a URL scheme, so iOS/Android can deep-link straight from the system camera without an in-app QR reader.Summary by cubic
Adds a
tokscale qrcommand to show your saved API token and username as a QR code for fast mobile sign-in. Secured with a confirmation prompt and safer JSON encoding, and the token is only inside the QR.New Features
tokscale qrwith--yesto skip the prompt for scripts.whoamiand exits.{"token":"tt_xxx","username":"..."}encoded viaserde_jsonto safely handle quotes and backslashes.Dense1x2and inverted colors; showsUserbelow the QR only.--yesis passed.Dependencies
qrcode0.14 totokscale-cli(pure Rust).Written for commit 87e47b2. Summary will update on new commits. Review in cubic