I wanted the Higgsfield CLI on my server — a terminal front-end to a pile of image and video generation models, which makes it scriptable and cron-able in a way the web UI never will be. The install was one line. Then it asked me to log in, and a specific, recurring failure showed up that has nothing to do with Higgsfield and everything to do with a word that means two different things at once.
The word is localhost.
The server is headless: no monitor, no browser, reached only over SSH from a laptop. That detail is the whole story.
The flow that assumes a lap
Higgsfield authenticates with OAuth 2.0 PKCE, the way most modern CLIs do, and the polite version goes like this: the CLI opens a small HTTP listener on localhost:<port>, prints a login URL, and waits. You open the URL in a browser, approve, and the identity provider (Higgsfield fronts theirs with Clerk) redirects your browser to http://localhost:<port>/callback?code=.... The listener catches that request, reads the code out of it, trades it for a token, and you're in.
On a laptop this is seamless because the browser and the listener are the same machine. localhost resolves to the same place for both halves of the handshake. The whole design quietly depends on that.
On a headless server it falls apart, because the two halves are now on different machines. The listener is on the server. The browser is on my laptop. When Clerk redirects the browser to http://localhost:<port>/callback, the browser dutifully connects to the laptop's localhost — where nothing is listening — and shows:
Unable to connect
The login succeeded. The callback failed. The code was issued — it just landed on the wrong machine's front door.
A loopback OAuth flow assumes the browser and the listener share a host. That assumption is invisible on a laptop and load-bearing on a headless box.
The tunnel, and the wall the spec put in front of it
The reflex fix is an SSH tunnel. Forward the callback port from the laptop to the server, so the laptop's localhost:<port> actually reaches the server's listener:
ssh -L 8799:localhost:8799 you@server
Sound. It works. But I hit a wall trying to pin the port, and the wall is worth understanding because it's not a bug — it's the security model doing its job.
higgsfield auth login has a --port flag, so I told it to use 8799 to match my tunnel. Clerk rejected the whole login:
error: invalid_request
The 'redirect_uri' parameter does not match any of the
Client's pre-registered redirect URIs.
Here's the thing PKCE doesn't remove: the redirect URI allowlist. PKCE lets a CLI skip shipping a client secret — the code challenge proves the token request came from whoever started the login — but the provider still refuses to send a code to any callback URL the app didn't register in advance. That allowlist is what stops a malicious page from pointing your authenticated redirect at its server. Higgsfield registered a specific handful of ports with Clerk, and those are the only ones that work. 8799 wasn't in the club.
The redirect URI is a registered constant, not a free parameter. You don't get to pick the port; you get to pick from the ports the app's developer already blessed. Drop the --port flag and the CLI falls back to its own default and it works — but now your tunnel has to match a port you don't fully control, and if that port is already busy on the server (mine collided with another service on the default), the CLI silently falls back again to a different registered port, and your tunnel is aimed at the wrong one.
The tunnel isn't wrong. It's just more moving parts than the problem actually needs.
The callback is just data
The realization that made this easy: the callback is not a handshake, it's a message. It's a plain HTTP GET whose entire payload is visible in the URL — ?code=<the code>&state=<the anti-forgery token>. The listener doesn't care who delivers that message. It cares that the message arrives with a state matching the one it issued.
So I stopped trying to route the browser to the listener at all. I ran higgsfield auth login on the server (letting it pick its own registered port — it landed on 8766), opened the printed URL in my laptop browser, approved, and let the redirect fail — Unable to connect, as expected. Then I copied the dead URL straight out of the browser's address bar:
http://localhost:8766/callback?code=OWVH...F1L&state=kg6P...TCY
That URL is the whole message. The listener is sitting on the server's own localhost, exactly where the CLI is watching. So I replayed it there:
curl "http://localhost:8766/callback?code=OWVH...F1L&state=kg6P...TCY"
The listener saw a request on its port, checked the state, matched it, exchanged the code for a token, and printed Successfully authenticated. No tunnel. The browser did the one thing only a browser with my logged-in Higgsfield session could do — prove I'm me — and then I hand-carried the resulting code the last hop the browser couldn't make.
Two things make this safe to lean on, and both are worth saying out loud:
-
The
stateparameter is the check that makes replay legitimate. The listener issued that random token at the start and refuses any callback that doesn't echo it. I'm not bypassing a security control by replaying the URL — I'm satisfying the exact one the flow was built around. - The code is short-lived and single-use. An OAuth authorization code is a password with a fuse — good for one exchange, expiring in a minute or two. Copy it, replay it promptly, and don't paste it anywhere it'll be logged. It's not a token you're storing; it's a token you're spending immediately.
What I'd carry to any headless login
-
localhostis a per-machine word. Any auth flow that redirects tolocalhostis quietly assuming the browser and the service are the same box. On a headless server they aren't, and that mismatch — not the tool — is your bug. -
Redirect URIs are an allowlist, not a setting. PKCE drops the client secret but keeps the registered-callback rule. If pinning a port throws
invalid_request, you're fighting the security model, not a config typo. Use the app's own default port. -
A loopback callback is replayable data. It's a URL carrying a code and a state. You don't need to route the browser to the listener; you can let the redirect die and deliver the URL yourself with
curlon the machine that's listening. -
stateis what makes hand-delivery honest. It's the flow's own anti-forgery check. Replaying a URL that carries the rightstateisn't a workaround around the security — it's the security working.
The tunnel is the textbook answer, and some days you'll want it. But when a login on a headless machine tells you it can't connect to localhost, remember that it already succeeded — the code exists — and the last hop is just a URL you're allowed to carry across yourself. This worked cleanly for Higgsfield; it'll work for any CLI whose login is a loopback redirect, which by now is most of them.
Top comments (0)