bitwarden / bitwarden/clients

Desktop App: WebAuthn 2FA via file:// iframe requires weakening server-side security headers

Open
#20,745 1 comment 0 reactions 0 assignees View on GitHub
bug desktop
Dominant language
TypeScript
Stars
13.8k
Forks
2k
Avg merge
3d 9h
Merged PRs (30d)
397

Description

### Steps To Reproduce

## Desktop App: WebAuthn 2FA via file:// iframe requires weakening server-side security headers

### What's happening

The Desktop App (Electron) handles WebAuthn 2FA by loading `webauthn-connector.html` from the Vaultwarden/Bitwarden server inside an iframe. The parent page is a local `file://` URL:

```
parent=file:///C:/Users/.../AppData/Local/Programs/Bitwarden/resources/app.asar/index.html
```

For this to work, the server needs to:

1. Add `file://*` to the CSP `frame-ancestors` directive (via `ALLOWED_IFRAME_ANCESTORS` in Vaultwarden)
2. Not send `X-Frame-Options: SAMEORIGIN` (or any `X-Frame-Options` at all)

In practice this means anyone running a reverse proxy with standard security headers (which is pretty much everyone) has to weaken their config to make the Desktop App work. I ran into this with Traefik behind Pangolin — WebAuthn worked fine in the Web UI but the Desktop App failed silently until I disabled `frameDeny` and added the iframe ancestor exception.

### Why this is a security concern

Allowing `file://*` in `frame-ancestors` means any local HTML file on the user's machine can embed the Vaultwarden web vault in an iframe. If an attacker can drop or modify a local file (not that far-fetched on a shared workstation or through a compromised download), they could potentially:

- Frame the vault UI in a context the user doesn't expect
- Intercept or tamper with the WebAuthn ceremony by controlling the parent frame
- Phish credentials through a local page that looks legitimate

On top of that, the `file://` origin doesn't play well with the FIDO2/WebAuthn spec, which expects HTTPS origins. The whole approach works around Electron's WebAuthn limitations (see electron/electron#24573) but does so at the cost of the server's security posture.

### What would be better

Instead of routing the WebAuthn ceremony through an HTTPS iframe embedded in a `file://` parent, the Desktop App could use Electron's native WebAuthn support, which has improved significantly. On Windows, Chromium (and by extension Electron) supports `navigator.credentials.get()` with USB security keys. On macOS there's a gap (see electron/electron#24573), but native solutions like [[electron-webauthn-mac](https://github.com/vault12/electron-webauthn-mac)](https://github.com/vault12/electron-webauthn-mac) exist.

Moving to native WebAuthn calls would:

- Remove the need for `webauthn-connector.html` entirely
- Eliminate the `file://*` frame-ancestors requirement
- Let server admins keep their `X-Frame-Options` and CSP intact
- Align with how other Electron apps handle FIDO2

### Environment

- Bitwarden Desktop App on Windows (Electron)
- Self-hosted Vaultwarden behind Traefik/Pangolin reverse proxy
- Token2 FIDO2 security key
- WebAuthn works perfectly in the Web UI through the same server and proxy config

### Related

- electron/electron#24573 — long-standing WebAuthn support issue in Electron
- bitwarden/clients#18013 — macOS Desktop WebAuthn failures
- [[Vaultwarden Discussion #2111](https://github.com/dani-garcia/vaultwarden/discussions/2111)](https://github.com/dani-garcia/vaultwarden/discussions/2111) — same iframe/framing issue documented since 2021
- [[vault12/electron-webauthn-mac](https://github.com/vault12/electron-webauthn-mac)](https://github.com/vault12/electron-webauthn-mac) — native WebAuthn polyfill for Electron on macOS

### Expected Result

The Desktop App should handle WebAuthn 2FA natively through Electron's built-in WebAuthn/FIDO2 APIs without requiring an iframe to the server. Server admins should not need to modify their CSP `frame-ancestors` or remove `X-Frame-Options` headers to make 2FA work in the Desktop App. The same reverse proxy security configuration that works for the Web UI should work for the Desktop App.

### Actual Result

The Desktop App loads `webauthn-connector.html` from the server in an iframe with a `file://` parent origin. This fails by default on any server that follows security best practices `(X-Frame-Options: SAMEORIGIN` or restrictive CSP `frame-ancestors)`, because `file://` is not the same origin as `https://`. To make WebAuthn 2FA work in the Desktop App, server admins must:

Remove `X-Frame-Options` headers from their reverse proxy (weakens clickjacking protection for all endpoints)
Add `file://*` to `frame-ancestors` via `ALLOWED_IFRAME_ANCESTORS` (allows any local HTML file to embed the vault)

Neither of these workarounds is necessary for the Web UI, mobile apps, or browser extensions — only the Desktop App requires them.

### Screenshots or Videos

_No response_

### Additional Context

_No response_

### Operating System

Windows

### Operating System Version

_No response_

### Installation method

Direct Download (from bitwarden.com)

### Build Version

2026.3.1

### Issue Tracking Info

- [x] I understand that work is tracked outside of GitHub. A PR will be linked to this issue should one be opened to address it, but Bitwarden doesn't use fields like "assigned", "milestone", or "project" to track progress.

Contributor guide

Open the contributing guide

Research direction

Start by locating the Desktop App's webauthn-connector.html integration and the Electron WebAuthn entry points. Review Electron's native WebAuthn support and the referenced macOS polyfill, then define platform coverage; done means the app no longer requires a server iframe or weakened CSP and X-Frame-Options headers.

Written by the indexing model from the issue text.

Assessment

Tech stack
electron
Domain
authentication, desktop, security
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.