DEV Community

Cover image for A browser I never opened logged me in — as the wrong person
Jun
Jun

Posted on Originally published at junueno.dev

A browser I never opened logged me in — as the wrong person

On one of my side projects, I wanted to put some Google Apps Script (GAS) under Git.

Until then, I'd been pasting code into the browser editor by hand. Which, it turns out, is its own little disaster. Paste a long comment line and the editor helpfully decides "the next line is probably a comment too, right?" and quietly breaks your code (thanks, that's very thoughtful of you). So I wanted to push from local using clasp, the official CLI. A perfectly reasonable plan.

It was supposed to be reasonable. Right up until I typed clasp login.

Wall #1: passkey verification demands a human finger

First, the work account lives on a company domain (Google Workspace). Somewhere in the sign-in flow, a passkey check drops in. Touch ID. Pressed. By a human.

I gave up on this one cleanly. Automation agents don't have fingers (sad, but true). "This one part I'll hand to a person," I decided, and had the actual human clear Touch ID. I figured that was the end of it.

Wall #2: somehow, login "succeeded" — as someone else

The passkey went through. The login flow ran. I peeked at the log, and it said:

"You are logged in as ○○ (personal account)"

...who? The one I wanted in was the work account. But somehow login had quietly completed with the personal account that was already signed in on my everyday browser.

If it were an error, I'd notice. Errors come in red. But this was a "success." A green check mark. The worst possible kind.

Why did a browser I never asked for do the work?

The cause was a race.

clasp's login works roughly like this:

  • it quietly stands up a tiny server on your machine (some arbitrary localhost port)
  • it opens Google's consent screen in a browser
  • once you consent, Google sends back an auth code to that local server
  • the server takes the code and swaps it for a token

The problem is that "opens in a browser" step — clasp does it in your default browser, on its own. I wanted the work account, so I was driving the consent screen in my automation browser. Meanwhile, behind my back, my everyday browser opened the same URL, and the personal account already signed in there finished consenting first.

The server grabs the first code that arrives. It doesn't care which browser it came from. First come, first served. And the one who lost was me (in a browser I set up myself).

The fix: delete the race itself

I did two things.

First, clasp logout. Wipe the wrong account's credentials.

Then log in again, this time in --no-localhost mode. In this mode, clasp doesn't run a server at all. Instead it waits and says "once you consent, paste the URL from your browser's address bar right here." Which means I get to name which browser's code gets used. Whatever the default browser does behind my back, it's irrelevant unless I paste it. Step off the first-come-first-served ring entirely.

...and here I tripped one more time.

The --no-localhost redirect target is a localhost with no server behind it. Naturally you land on a "this site can't be reached" error page. The code I want is sitting right there in that page's address bar. But you can't read a URL out of an error page programmatically (browsers make sure of that — dead end).

So in the end I stood up my own little capture server on that port. Run the OAuth through the browser I intended (the work account). This time the redirect lands on a server that's actually waiting. Grab the code reliably on the server side and feed it into clasp.

The log finally said:

"You are logged in as ○○ (work account)"

This time it was actually me.

Takeaways (for anyone who'd rather not repeat this)

  • When you automate an OAuth localhost flow, control which browser grabs the redirect first. A default browser's auto-launch will cut in front of you, out of pure good intentions.
  • --no-localhost plus your own capture server pins down which account gets used, exactly as you meant. Deleting the race is the fastest fix.
  • Passkeys are a human wall. The right move is to give up on automating that step and hand it to a person (don't try to force your way through).
  • CLI tools often hold exactly one global login per machine. On a project juggling multiple accounts, you need the habit of checking "which account am I in right now" before you start.

In login automation, the truly scary thing isn't the red failure. It's the green "success." Whose success is it, exactly? Make sure you look, every single time.

Top comments (0)