-
Notifications
You must be signed in to change notification settings - Fork 658
Description
Summary
The human verification page at verify.proton.me uses window.parent.postMessage() to return CAPTCHA tokens. This works for web apps embedding the page in an iframe, but makes it impossible for CLI and API clients to receive the verification token through any standard mechanism.
We are building Proton Git LFS — a Git LFS custom transfer adapter that stores objects on Proton Drive with end-to-end encryption. During implementation, we hit this limitation and exhausted all workarounds. This issue documents what we tried, why each approach fails, and proposes solutions.
What We Tried
1. Direct URL + retry without token
Show the user verify.proton.me URL, let them complete CAPTCHA, retry login.
Result: API still returns code 9001 (CAPTCHA required). Completing the CAPTCHA at verify.proton.me does not allowlist the IP or session for subsequent API requests.
2. iframe embedding from localhost
Start a local HTTP server, embed verify.proton.me in an iframe, listen for postMessage.
Result: Blocked by CSP frame-ancestors — Firefox: "To protect your security, verify.proton.me will not allow Firefox to display the page if another site has embedded it."
3. Reverse proxy with script injection
Fetch the CAPTCHA HTML server-side, inject a postMessage listener, serve from localhost.
Result: Functional, but fragile. The browser address bar shows http://localhost:PORT which looks untrustworthy on a verification page. Also at risk of hCaptcha rejecting solutions from a localhost origin (site key domain mismatch). Depends on the CAPTCHA page HTML structure not changing.
4. Manual token extraction (upstream approach)
The upstream project we forked documents this as a known limitation: "if a CAPTCHA is required it must be solved in the browser and the token manually copied back" — requiring the user to open DevTools, find the postMessage payload, and paste it into the CLI.
None of these provide a viable UX for non-technical users.
Why This Matters
Every CLI or API client integrating with Proton authentication is affected:
- Git LFS adapters — Our use case: Git operations trigger auth, CAPTCHA blocks automated workflows
- Backup tools — Scripts that run
proton-drive uploadcan't handle CAPTCHA non-interactively - proton-python-client — Python API clients face the same limitation
- go-proton-api — Go API clients have no CAPTCHA flow
- CI/CD pipelines — Any automation using Proton APIs is broken by CAPTCHA with no recovery path
The current postMessage-only mechanism was designed for Proton's own web apps. As the ecosystem of third-party tools grows (and Proton encourages it via open-source API clients), a CLI-compatible verification flow becomes essential.
Proposed Solutions
Any of the following would unblock all CLI/API clients:
Option A: Redirect callback (lowest friction)
Add a redirect_uri parameter to the verification page:
https://verify.proton.me/?methods=captcha&token=CHALLENGE&redirect_uri=http://localhost:PORT/callback
After CAPTCHA completion, redirect the browser to:
http://localhost:PORT/callback?verification_token=FULL_TOKEN
This is the standard pattern used by GitHub CLI (gh auth login), Anthropic Claude Code, Stripe CLI, Terraform, and virtually every other CLI authentication flow. It requires minimal changes to the verification page — just an optional redirect after sendToken() instead of (or in addition to) postMessage.
Option B: Polling endpoint (no localhost server needed)
Add an API endpoint to check CAPTCHA completion:
GET /core/v4/captcha/status?token=CHALLENGE_TOKEN
# Before completion:
{ "completed": false }
# After completion:
{ "completed": true, "verificationToken": "..." }This follows RFC 8628 (OAuth Device Authorization Grant), used by GitHub's device flow. The CLI would:
- Print: "Open https://verify.proton.me/... and complete the CAPTCHA"
- Poll the status endpoint every few seconds
- Receive the token automatically
No localhost server, no browser automation, no address bar concerns. The user sees only the trusted verify.proton.me URL.
Option C: Relax frame-ancestors for localhost
Allow verify.proton.me to be embedded in iframes from localhost / 127.0.0.1 origins. This would let CLI tools use the existing iframe + postMessage pattern without requiring a reverse proxy.
Technical Context
- Proton API error code
9001: ReturnsHumanVerificationToken,HumanVerificationMethods, andWebUrlinDetails - Proton API error code
2028: May also containHumanVerificationTokeninDetails(abuse detection) - Verification page:
verify.proton.me/?methods=captcha&token=... - Token flow: hCaptcha solution →
sendToken(prefix1 + prefix2 + response)→window.parent.postMessage(token, targetOrigin) ForceWebMessaging=1parameter controls postMessage vs. redirect behavior
Our Project
Proton Git LFS provides encrypted Git LFS storage on Proton Drive. The adapter authenticates via SRP, manages sessions with token refresh, and handles file upload/download with E2E encryption. CAPTCHA verification is the last remaining blocker for a smooth first-login experience.
We would be happy to test and provide feedback on any proposed solution.