1const fs = require("node:fs");
2const path = require("node:path");
3const { withAndroidManifest, withDangerousMod } = require("expo/config-plugins");
4
5// Environments behind a private CA installed in the user store must work like they do in Chrome.
6// Android ignores usesCleartextTraffic once a network security config exists, so permit it here.
7const NETWORK_SECURITY_CONFIG = `<?xml version="1.0" encoding="utf-8"?>
8<network-security-config>
9 <base-config cleartextTrafficPermitted="true">
10 <trust-anchors>
11 <certificates src="system" />
12 <certificates src="user" />
13 </trust-anchors>
14 </base-config>
15</network-security-config>
16`;
17
18function withNetworkSecurityConfigManifest(config) {
19 return withAndroidManifest(config, (nextConfig) => {
20 const application = nextConfig.modResults.manifest.application?.[0];
21
22 if (application == null) {
23 throw new Error(
24 "AndroidManifest.xml is missing the application element required for network security configuration.",
25 );
26 }
27
28 application.$ ??= {};
29 application.$["android:networkSecurityConfig"] = "@xml/network_security_config";
30
31 return nextConfig;
32 });
33}
34
35function withNetworkSecurityConfigResource(config) {
36 return withDangerousMod(config, [
37 "android",
38 async (config) => {
39 const xmlDir = path.join(
40 config.modRequest.platformProjectRoot,
41 "app",
42 "src",
43 "main",
44 "res",
45 "xml",
46 );
47 fs.mkdirSync(xmlDir, { recursive: true });
48 fs.writeFileSync(path.join(xmlDir, "network_security_config.xml"), NETWORK_SECURITY_CONFIG);
49 return config;
50 },
51 ]);
52}
53
54module.exports = function withAndroidNetworkSecurityConfig(config) {
55 return withNetworkSecurityConfigResource(withNetworkSecurityConfigManifest(config));
56};