Borrowing the Browser for a Moment — the Story Behind Terminal Login
The story I put off in one paragraph last time. How we routed around the fecit:// scheme with loopback — the old trick gh and gcloud use — to bring the token back to the terminal.
Borrowing the Browser for a Moment — the Story Behind Terminal Login
Last time I introduced Fecit’s terminal client, the CLI. There, login got brushed off in a single paragraph — “you pick your way in, browser or terminal.” But inside that one paragraph there was a small adventure. That’s today’s story.
The Easy Path and the Blocked One
Getting in with an ID and password isn’t hard. Take the input, throw it at /api/auth/login, store the token you get back. We masked the password with asterisks, and that was that.
What was blocked was one sentence: “I want to log in with the browser.” You can’t run a Google or Apple login inside a terminal, so you have to pop a browser — but the real problem came next. Once the login finished in the browser, how do you hand the result back to the terminal?
Where fecit:// Stood in the Way
It turned out Fecit already ran a browser login page — the one the desktop app uses. The flow went like this: log in with Google in the browser → token issued → redirect to fecit://auth/callback?access_token=.... That fecit:// at the end is a custom address the operating system has promised to hand to the desktop app. The desktop app snatches it and pulls out the token.
For the CLI, that was a closed door. fecit:// is already claimed by the desktop app, and having a bare executable register itself with the OS as the handler for that address is fiddly and prone to conflicts. The token would come right up to the doorstep and slip off somewhere else.
How to Borrow a Browser for a Moment
The answer was, of all things, an old trick — the one gh, gcloud, and vercel all use: loopback.
The CLI opens a tiny web server inside your own machine, very briefly — on a random port of http://127.0.0.1. Then it opens the login page in the browser, but adds a note: “when you’re done, don’t go to fecit:// — send the token to this address I just opened.” When the user finishes logging in, the page hands the token back to that localhost address. A round trip that never left the machine.
The Backend, in Just One Place
The backend needed exactly one change: let the login page read where to send the token back from the URL.
But it can’t send anywhere. Someone could slip you a link carrying ?redirect=evil-address and siphon another person’s freshly-issued token to their own server. So we allowed only 127.0.0.1 and localhost. That validation lives inside the page we serve, so all an attacker controls is the value in the URL. On top of that, we tuck a random value (a state nonce) into each request and check it against what comes back, to confirm the response belongs to the request we sent.
Whichever Path, the Same Channel
Once we got here, that login page became more useful. It already had Google and Apple buttons side by side, so we added ID-and-password fields too. Now when you type fecit login, the browser opens and you pick any of the three — Google, Apple, or ID and password — right there on that screen. Whichever path you take, the token comes back through the same localhost channel. The terminal waits quietly, and greets you in one line when the token arrives.
A Lingering Thought
At first it felt like browser login would mean tearing the backend apart. But once it was solved, we’d borrowed the existing login page almost untouched — all that changed was a few lines of HTML reading where to send things back, and one localhost server that blinks into existence and vanishes.
Borrowing what’s already there usually carries you further than building anew. The browser, and the login page — we just borrowed them for a moment, and gave them back.