The SSO Autoclicker, and What MV3 Cost Me
I turned two Tampermonkey userscripts into a Manifest V3 Chrome extension that clicks through my company's SSO silently — and reimplemented TOTP from scratch because MV3 won't let you fetch a CDN.
At work, half my daily login flow is clicking buttons that only exist to be clicked: "Log in via SSO", "Continue", "Verify". I'd solved this years ago with a pair of Tampermonkey userscripts that fired on the right URLs and auto-clicked for me. They worked perfectly. Then Chrome's Manifest V3 landed, and the rug got pulled out from under every userscript that depends on fetching code at runtime.
I wanted the same behavior but as a proper extension — no popup, no options page, no UI at all. Just content scripts that silently do the clicking. Turned out MV3 had opinions about that plan, and the most interesting fight was the one I didn't expect: TOTP.
The setup
The extension matches a set of internal sites and clicks the SSO button on each:
- Argo CD and internal tool logins → click the Log in via SSO button
- The on-call / incident tool → click the SSO submit button
- The identity provider's own page → the real trick: auto-fill the TOTP passcode and click Verify
- GitHub and a few others → click Continue on SSO confirmations
All of it runs as content scripts, no UI. Install once, forget it exists, and the whole morning ritual becomes "type the TOTP code, hit enter" — or, on the one site that still needs a code, nothing at all.
MV3 ruined the easy version
The original userscript pulled the otpauth library from a CDN via @require, then computed RFC 6238 codes at runtime. Manifest V3's content security policy forbids loading remote code, so @require is gone, CDN scripts are gone, and any <script> you inject has to be a real file in the extension bundle.
The fix was to write TOTP from scratch. RFC 6238 is small: HMAC-SHA1 over a time counter, take 6 digits. The Web Crypto API gives me HMAC-SHA1 natively, and a tiny base32 decoder turns the secret into key material. Total: ~80 lines, zero dependencies, and the outputs verified byte-for-byte against the RFC 4226 test vectors. Same codes, no CDN, no network dependency — which is arguably more reliable than the original, since the extension no longer depends on the CDN being up.
The provenance and the honest caveat
Both scripts trace back to internal "autoclick boilerplate" scripts that had been passed around. The logic is verbatim; I only swapped the TOTP implementation. And here's the honest caveat: the TOTP secret is hardcoded in the extension source, exactly as it was in the userscript. That means this repo is sensitive and stays private. It's the same risk profile as the userscripts it replaces, but it's worth saying out loud — any extension that does this is holding your 2FA seed.
What I'd do differently
- Push the secret out of the bundle. For a work tool, the right design is a tiny background script that reads the secret from an encrypted store (or a one-time setup screen) rather than a hardcoded string. I shipped the pragmatic version; if I were doing it for more than myself, I'd do the proper one.
- Test TOTP against the RFC vectors, not against the real service. Writing TOTP blind and assuming it works because "it's 6 digits" is how you ship a broken autofill. The RFC 4226 test vectors are free and definitive.
- A login tool should have no UI, but it should have logs. The version with no popup is elegant until the button changes and it silently stops clicking. A small debug surface would have saved me a few confused mornings.
The extension is ~a few hundred lines of content scripts plus the TOTP module. It does exactly what the userscripts did, with zero runtime dependencies and zero UI. Manifest V3 took the easy path away; it forced the slightly harder path, which turned out to be the more robust one anyway.