pingdotgg / pingdotgg/t3code

[Bug]: Android app does not trust user-installed CA certificates

Open
#5,639 0 comments 1 reaction 0 assignees View on GitHub

Nobody has claimed this yet.

bug needs-triage
Dominant language
TypeScript
Stars
23k
Forks
5.9k
Avg merge
11h 14m
Merged PRs (30d)
357

Description

Before submitting
  • I searched existing issues and did not find a duplicate.
  • I included enough detail to reproduce or investigate the problem.
Area

apps/mobile

Steps to reproduce
  1. Stand up a T3 Code server behind an internal CA (in my case stepCA), so it serves HTTPS with a cert chaining to a private root.
  2. Install that root cert on the Android device under Settings → Security → Encryption & credentials → Install a certificate → CA certificate. It lands in User credentials, not System.
  3. Confirm the cert is trusted by the system by opening https://private.ca.com/.well-known/t3/environment in Chrome on the same device. It loads without a warning.
  4. Open the T3 Code Android app → Add environment.
  5. Enter Host: https://private.ca.com and the pairing code.
  6. Tap + ADD ENVIRONMENT.
Expected behavior

The app should reach the environment endpoint and pair, the same way Chrome on the same device does, since the CA is installed and trusted at the OS level.

Actual behavior

Pairing fails immediately with:

Failed to fetch remote environment endpoint https://private.ca.com/.well-known/t3/environment (HttpClientError: Transport error (GET https://private.ca.com/.well-known/t3/environment)).

The error is generic, so it does not read as a TLS problem at first. From the app side there is nothing distinguishing a rejected chain from the host being unreachable.

Digging through the repo, my guess would be this is the stock Android trust policy rather than anything specific to the pairing flow:

  • There is no network_security_config.xml anywhere in apps/mobile, and no android:networkSecurityConfig attribute on the manifest <application> element.
  • With no config, API 24+ defaults to system CAs only. User-installed CAs are ignored for app traffic. RN/Expo min and target SDK are both in that range.
  • The only Android networking change in the config plugins is cleartext, which is a separate switch and does not affect trust anchors:
// apps/mobile/plugins/withAndroidCleartextTraffic.cjs
const { withAndroidManifest } = require("expo/config-plugins");
module.exports = function withAndroidCleartextTraffic(config) {
  return withAndroidManifest(config, (nextConfig) => {
    // ...
    application.$["android:usesCleartextTraffic"] = "true";
    return nextConfig;
  });
};
  • There is no custom TrustManager, OkHttp cert pinning, or custom SSL stack, so HTTPS/WSS goes React Native → OkHttp → platform TLS with the default anchors.

iOS does not have the same problem because NSAllowsArbitraryLoads: true is set there. Android has no equivalent flag for user CAs, it has to be done through a network security config.

So in practice: system or public CA works, http:// and ws:// work because cleartext is permitted, and a user-installed CA fails the handshake.

Impact

Major degradation or frequent failure

Version or commit

T3 Code Android 1.0.1, installed from Google Play

Environment

Samsung Android 15, stepCA-issued server cert, root installed under Settings → Security → User credentials

Logs or stack traces
Failed to fetch remote environment endpoint https://private.ca.com/.well-known/t3/environment (HttpClientError: Transport error (GET https://private.ca.com/.well-known/t3/environment)).


The same URL returns 200 in Chrome on the same device with the same cert installed.
Screenshots, recordings, or supporting files

No response

Workaround

Add a network security config that includes the user trust anchors, and wire it up through an Expo config plugin alongside the existing cleartext one:

<base-config cleartextTrafficPermitted="true">
  <trust-anchors>
    <certificates src="system" />
    <certificates src="user" />
  </trust-anchors>
</base-config>

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with apps/mobile/plugins/withAndroidCleartextTraffic.cjs and the Android manifest generated by the Expo config plugins. Add the Android network security configuration alongside the existing cleartext setting so user-installed and system CAs are trusted. Verify pairing against the private CA endpoint on Android while preserving public-CA and cleartext behavior.

Written by the indexing model from the issue text.

Assessment

Tech stack
android, react-native, typescript
Domain
mobile, networking, security
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
76/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.