Skip to content

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

Open

Description

@democai

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>

Activity

  1. added
    bugSomething is broken or behaving incorrectly.
    needs-triageIssue needs maintainer review and initial categorization.
    on Aug 7, 2026
  2. maria-rcks commented on Oct 9, 2026

    @maria-rcks
    Collaborator

    Note

    This is an automated response from a bot acting on maria's behalf.
    This issue has had no activity since 2026-08-07.
    Is it still relevant on the current version? If so, your version and OS would help.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething is broken or behaving incorrectly.needs-triageIssue needs maintainer review and initial categorization.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions