bitwarden / bitwarden/ios

[PM-39573] mTLS client certificate authentication fails when the reverse proxy trusts only the root CA

Open
#2,832 10 comments 0 reactions 0 assignees View on GitHub
app:password-manager bug
Dominant language
Swift
Stars
684
Forks
154
Avg merge
7d 2h
Merged PRs (30d)
32

Description

### Steps To Reproduce

1. Set up a self-hosted server behind a reverse proxy that requires mutual TLS (client certificate authentication).
2. Configure the proxy to trust **only the root CA** — i.e. the client-CA bundle contains the root certificate but **not** the intermediate CA.
3. Issue a client certificate whose chain is `leaf → intermediate → root`, and export it as a `.p12` containing the leaf, its private key, and the intermediate.
4. In the iOS app, set the self-hosted server URL and import the `.p12` client certificate.
5. Attempt to log in / connect to the server.

### Expected Result

The app authenticates successfully, the same way a web browser and the Android app do with the identical client certificate and reverse-proxy configuration.

### Actual Result

The TLS handshake fails and the app shows:

> This is not a recognized Bitwarden server. You may need to check with your provider or update your server.

Disabling the client-certificate requirement on the proxy (or adding the intermediate CA to the proxy's client-CA bundle) lets login succeed, confirming the failure is specific to the mTLS handshake.

### Screenshots or Videos

_No response_

### Additional Context

**Root cause:** When answering the client-certificate TLS challenge, the app presents **only the leaf certificate** and not the intermediate chain (the credential is built with no additional certificates). When the reverse proxy trusts only the root CA, it cannot build `leaf → intermediate → root` from the leaf alone, so it rejects the handshake.

- Browsers and the Android app send the **entire chain** from the `.p12`, which is why the same certificate and server work there but the iOS app fails.
- This affects both API requests and vault item icon loading, since both go through the client-certificate challenge.
- **Workaround:** add the intermediate CA(s) to the reverse proxy's client-CA bundle (e.g. nginx `ssl_client_certificate`) so the server can complete the chain itself.

The misleading "not a recognized Bitwarden server" message also sends users toward checking the server URL rather than the certificate chain, which makes this harder to diagnose.

### Build Version

2026.5.1 (also affects earlier versions that support client-certificate authentication)

### What server are you connecting to?

Self-host

### Self-host Server Version

Reproduces with a reverse proxy in front of a self-hosted server (verified against Vaultwarden 1.36.0); applies to any self-host setup that terminates mTLS at a proxy trusting only the root CA.

### Environment Details

- Device: iPhone (any)
- OS Version: iOS 18+

### 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

Trace the client-certificate TLS challenge used by API requests and vault item icon loading, starting with the .p12 credential construction described in the issue. Confirm where only the leaf is supplied and preserve the intermediate certificates from the imported chain; validate with a proxy trusting only the root CA and verify both request paths authenticate successfully.

Written by the indexing model from the issue text.

Assessment

Tech stack
ios, swift
Domain
authentication, networking, security
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
55/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.