{"_id":"@a-cube-io/expo-mutual-tls","_rev":"7-4b9b0220eb7e21644d0f01512a15eba0","name":"@a-cube-io/expo-mutual-tls","dist-tags":{"latest":"1.0.5"},"versions":{"0.1.0":{"name":"@a-cube-io/expo-mutual-tls","version":"0.1.0","keywords":["react-native","expo","expo-mutual-tls","ExpoMutualTls"],"author":{"url":"https://github.com/a-cube-io","name":"A-Cube S.r.l."},"license":"MIT","_id":"@a-cube-io/expo-mutual-tls@0.1.0","maintainers":[{"name":"acube-io","email":"anders.jipwouo@a-cube.io"}],"homepage":"https://github.com/a-cube-io/expo-mutual-tls#readme","bugs":{"url":"https://github.com/a-cube-io/expo-mutual-tls/issues"},"dist":{"shasum":"20b769d2eeb300b9b049938dec8be8d29a9c3367","tarball":"https://registry.npmjs.org/@a-cube-io/expo-mutual-tls/-/expo-mutual-tls-0.1.0.tgz","fileCount":33,"integrity":"sha512-B6N1zWO7PgTNdzAD8ZQ8mqIFBSQo9uKQ0HqNdDUY1JZdyNJXMUVajtnKyCIHNLGl30dl4KGJKuYIiBh8NQ/dnw==","signatures":[{"sig":"MEUCIQCGM6keXZH0q9+AxgmYqrXe7+gU0mR3Je9/nCHTJ/zy/QIgOSLc0ciuHxpHLEgPg8CIdsU0D7pnzQLT8vU6qjXojcg=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":166284},"main":"build/index.js","types":"build/index.d.ts","gitHead":"4b1bf41e689dbd05195e20605d3b036990650d47","scripts":{"lint":"expo-module lint","test":"expo-module test","build":"expo-module build","clean":"expo-module clean","prepare":"expo-module prepare","open:ios":"xed example/ios","expo-module":"expo-module","open:android":"open -a \"Android Studio\" example/android","prepublishOnly":"expo-module prepublishOnly"},"_npmUser":{"name":"acube-io","email":"anders.jipwouo@a-cube.io"},"repository":{"url":"git+https://github.com/a-cube-io/expo-mutual-tls.git","type":"git"},"_npmVersion":"10.9.2","description":"Expo Mutual TLS module for iOS and Android.","directories":{},"_nodeVersion":"22.17.1","_hasShrinkwrap":false,"devDependencies":{"expo":"~53.0.0","@types/react":"~19.0.0","react-native":"0.79.1","expo-module-scripts":"^4.1.10"},"peerDependencies":{"expo":"*","react":"*","react-native":"*"},"_npmOperationalInternal":{"tmp":"tmp/expo-mutual-tls_0.1.0_1756476524814_0.8156851974864694","host":"s3://npm-registry-packages-npm-production"}},"1.0.0":{"name":"@a-cube-io/expo-mutual-tls","version":"1.0.0","keywords":["react-native","expo","expo-mutual-tls","ExpoMutualTls","@a-cube-io/expo-mutual-tls","mutual-tls","@a-cube-io"],"author":{"url":"https://github.com/a-cube-io","name":"A-Cube S.r.l."},"license":"MIT","_id":"@a-cube-io/expo-mutual-tls@1.0.0","maintainers":[{"name":"acube-io","email":"anders.jipwouo@a-cube.io"}],"homepage":"https://github.com/a-cube-io/expo-mutual-tls#readme","bugs":{"url":"https://github.com/a-cube-io/expo-mutual-tls/issues"},"dist":{"shasum":"17a6cb852c808b39d912e4e3b95d200decc2f439","tarball":"https://registry.npmjs.org/@a-cube-io/expo-mutual-tls/-/expo-mutual-tls-1.0.0.tgz","fileCount":38,"integrity":"sha512-eZONk8ru2+jNnuFQOxn4l3ejUq314CP/41MLaF2KkK0CJjFslvYJdnijvNHbqtbOpmor5cFzBab9YItNAIj+OQ==","signatures":[{"sig":"MEYCIQCVjJQb9jMQo2JYXj7XE0Mf/mFM5zH9moJl433R84blmwIhAMETFXURw57r3cSqAX3wb7P03Xgx53R9uIuZ7Hpr/Jjc","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":172751},"main":"build/index.js","types":"build/index.d.ts","gitHead":"f8d1d7e1fa1bdcec248bf56f909940bad1dc6c65","scripts":{"lint":"expo-module lint","test":"expo-module test","build":"expo-module build","clean":"expo-module clean","prepare":"expo-module prepare","open:ios":"xed example/ios","expo-module":"expo-module","open:android":"open -a \"Android Studio\" example/android","release:major":"./scripts/release.sh major","release:minor":"./scripts/release.sh minor","release:patch":"./scripts/release.sh patch","prepublishOnly":"expo-module prepublishOnly"},"_npmUser":{"name":"acube-io","email":"anders.jipwouo@a-cube.io"},"repository":{"url":"git+https://github.com/a-cube-io/expo-mutual-tls.git","type":"git"},"_npmVersion":"10.9.2","description":"Expo Mutual TLS module for iOS and Android.","directories":{},"_nodeVersion":"22.17.1","_hasShrinkwrap":false,"devDependencies":{"expo":"~53.0.0","@types/react":"~19.0.0","react-native":"0.79.1","expo-module-scripts":"^4.1.10"},"peerDependencies":{"expo":"*","react":"*","react-native":"*"},"_npmOperationalInternal":{"tmp":"tmp/expo-mutual-tls_1.0.0_1756480858331_0.7958798991436917","host":"s3://npm-registry-packages-npm-production"}},"1.0.1":{"name":"@a-cube-io/expo-mutual-tls","version":"1.0.1","keywords":["react-native","expo","expo-mutual-tls","ExpoMutualTls","@a-cube-io/expo-mutual-tls","mutual-tls","@a-cube-io"],"author":{"url":"https://github.com/a-cube-io","name":"A-Cube S.r.l."},"license":"MIT","_id":"@a-cube-io/expo-mutual-tls@1.0.1","maintainers":[{"name":"acube-io","email":"anders.jipwouo@a-cube.io"}],"homepage":"https://github.com/a-cube-io/expo-mutual-tls#readme","bugs":{"url":"https://github.com/a-cube-io/expo-mutual-tls/issues"},"dist":{"shasum":"2b18d7d710d14732e260425f8fb78409ece0db6f","tarball":"https://registry.npmjs.org/@a-cube-io/expo-mutual-tls/-/expo-mutual-tls-1.0.1.tgz","fileCount":38,"integrity":"sha512-+e1WaMoAx3uavJSCQpPNKXKorqBSLoiYYI9UoXFHDI7ocYTsikEw5Hv6oz/zar+GXzd0u2S5XeO+5qmSYzNP8w==","signatures":[{"sig":"MEYCIQCsfjRi53Gie3zfVM4oUAi+HLI1PLnE46jay3Kmea+zUQIhAKrjgO8uCQaBlK2FqGZXiT5141jVIXTmbiTL3sF7GnrV","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":173047},"main":"build/index.js","types":"build/index.d.ts","gitHead":"8c74e50c3b5716282f50ca792ce4653436b4b966","scripts":{"lint":"expo-module lint","test":"expo-module test","build":"expo-module build","clean":"expo-module clean","prepare":"expo-module prepare","open:ios":"xed example/ios","expo-module":"expo-module","open:android":"open -a \"Android Studio\" example/android","release:major":"./scripts/release.sh major","release:minor":"./scripts/release.sh minor","release:patch":"./scripts/release.sh patch","prepublishOnly":"expo-module prepublishOnly"},"_npmUser":{"name":"acube-io","email":"anders.jipwouo@a-cube.io"},"repository":{"url":"git+https://github.com/a-cube-io/expo-mutual-tls.git","type":"git"},"_npmVersion":"10.9.2","description":"Expo Mutual TLS module for iOS and Android.","directories":{},"_nodeVersion":"22.17.1","_hasShrinkwrap":false,"devDependencies":{"expo":"~53.0.0","@types/react":"~19.0.0","react-native":"0.79.1","expo-module-scripts":"^4.1.10"},"peerDependencies":{"expo":"*","react":"*","react-native":"*"},"_npmOperationalInternal":{"tmp":"tmp/expo-mutual-tls_1.0.1_1756481261950_0.01887587043985861","host":"s3://npm-registry-packages-npm-production"}},"1.0.2":{"name":"@a-cube-io/expo-mutual-tls","version":"1.0.2","keywords":["react-native","expo","expo-mutual-tls","ExpoMutualTls","@a-cube-io/expo-mutual-tls","mutual-tls","@a-cube-io"],"author":{"url":"https://github.com/a-cube-io","name":"A-Cube S.r.l."},"license":"MIT","_id":"@a-cube-io/expo-mutual-tls@1.0.2","maintainers":[{"name":"acube-io","email":"anders.jipwouo@a-cube.io"}],"homepage":"https://github.com/a-cube-io/expo-mutual-tls#readme","bugs":{"url":"https://github.com/a-cube-io/expo-mutual-tls/issues"},"dist":{"shasum":"643d1871a4db66dd5f1095915f65b37b99bf1777","tarball":"https://registry.npmjs.org/@a-cube-io/expo-mutual-tls/-/expo-mutual-tls-1.0.2.tgz","fileCount":38,"integrity":"sha512-GGje05PvgP0XEDDsSpA+QlA9JTTv6Zv34txWiHNOhNUK+QWOSHUV7709h2fO7q6YzXK74YPN1Og2Vn8DNRctKw==","signatures":[{"sig":"MEUCIQCdQPl2xDOS051u3MZqIP8jvanNwQlk56fbCGEzbWfgOwIgNSpJI/lMIr8IYPaLP7Y692g4IypXmhPJN6Vri4gIf/Q=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":173081},"main":"build/index.js","types":"build/index.d.ts","gitHead":"dbf039d0d73a6b3e098919cc564a7484418f7dfe","scripts":{"lint":"expo-module lint","test":"expo-module test","build":"expo-module build","clean":"expo-module clean","prepare":"expo-module prepare","open:ios":"xed example/ios","expo-module":"expo-module","open:android":"open -a \"Android Studio\" example/android","release:major":"./scripts/release.sh major","release:minor":"./scripts/release.sh minor","release:patch":"./scripts/release.sh patch","prepublishOnly":"expo-module prepublishOnly"},"_npmUser":{"name":"acube-io","email":"anders.jipwouo@a-cube.io"},"repository":{"url":"git+https://github.com/a-cube-io/expo-mutual-tls.git","type":"git"},"_npmVersion":"10.9.2","description":"Expo Mutual TLS module for iOS and Android.","directories":{},"_nodeVersion":"22.17.1","_hasShrinkwrap":false,"devDependencies":{"expo":"~53.0.0","@types/react":"~19.0.0","react-native":"0.79.1","expo-module-scripts":"^4.1.10"},"peerDependencies":{"expo":"*","react":"*","react-native":"*"},"_npmOperationalInternal":{"tmp":"tmp/expo-mutual-tls_1.0.2_1756481822380_0.21092999017990843","host":"s3://npm-registry-packages-npm-production"}},"1.0.3":{"name":"@a-cube-io/expo-mutual-tls","version":"1.0.3","keywords":["react-native","expo","expo-mutual-tls","ExpoMutualTls","@a-cube-io/expo-mutual-tls","mutual-tls","@a-cube-io"],"author":{"url":"https://github.com/a-cube-io","name":"A-Cube S.r.l."},"license":"MIT","_id":"@a-cube-io/expo-mutual-tls@1.0.3","maintainers":[{"name":"acube-io","email":"anders.jipwouo@a-cube.io"}],"homepage":"https://github.com/a-cube-io/expo-mutual-tls#readme","bugs":{"url":"https://github.com/a-cube-io/expo-mutual-tls/issues"},"dist":{"shasum":"be56550881c316fc7dd12facbcfd76bbe8804b59","tarball":"https://registry.npmjs.org/@a-cube-io/expo-mutual-tls/-/expo-mutual-tls-1.0.3.tgz","fileCount":38,"integrity":"sha512-zH5qQVKjOlQP1KC9px6i0cklQP5SDgeHyNHurfcNPum1y8WbiAtXKedOrxh4OhN97AmKQnjoyvnf2GwEmpezhw==","signatures":[{"sig":"MEUCICSlBedtUzLzA3NiYBsDpfblsZQbVRVYBp7dD0+n4zsyAiEAz3fVig9c4in9D5YsyWeD88SjWL2DnvLpb4UXNXfXY+w=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":174704},"main":"build/index.js","types":"build/index.d.ts","gitHead":"a2156bc62aa7573db7c4de2beaffa4efd5bf02fc","scripts":{"lint":"expo-module lint","test":"expo-module test","build":"expo-module build","clean":"expo-module clean","prepare":"expo-module prepare","open:ios":"xed example/ios","expo-module":"expo-module","open:android":"open -a \"Android Studio\" example/android","release:major":"./scripts/release.sh major","release:minor":"./scripts/release.sh minor","release:patch":"./scripts/release.sh patch","prepublishOnly":"expo-module prepublishOnly"},"_npmUser":{"name":"acube-io","email":"anders.jipwouo@a-cube.io"},"repository":{"url":"git+https://github.com/a-cube-io/expo-mutual-tls.git","type":"git"},"_npmVersion":"10.9.2","description":"Expo Mutual TLS module for iOS and Android.","directories":{},"_nodeVersion":"22.17.1","_hasShrinkwrap":false,"devDependencies":{"expo":"~53.0.0","@types/react":"~19.0.0","react-native":"0.79.1","expo-module-scripts":"^4.1.10"},"peerDependencies":{"expo":"*","react":"*","react-native":"*"},"_npmOperationalInternal":{"tmp":"tmp/expo-mutual-tls_1.0.3_1759849841546_0.43218305513044863","host":"s3://npm-registry-packages-npm-production"}},"1.0.5":{"name":"@a-cube-io/expo-mutual-tls","version":"1.0.5","keywords":["react-native","expo","expo-mutual-tls","ExpoMutualTls","@a-cube-io/expo-mutual-tls","mutual-tls","@a-cube-io"],"author":{"url":"https://github.com/a-cube-io","name":"A-Cube S.r.l."},"license":"MIT","_id":"@a-cube-io/expo-mutual-tls@1.0.5","maintainers":[{"name":"acube-io","email":"anders.jipwouo@a-cube.io"}],"homepage":"https://github.com/a-cube-io/expo-mutual-tls#readme","bugs":{"url":"https://github.com/a-cube-io/expo-mutual-tls/issues"},"dist":{"shasum":"cafc19278433c66aac93f257f318bc60c4f246c3","tarball":"https://registry.npmjs.org/@a-cube-io/expo-mutual-tls/-/expo-mutual-tls-1.0.5.tgz","fileCount":38,"integrity":"sha512-e1JMYPkgaYjfsm+0CJlhD5S7rVthDss34BxsyTiqFXJ1/HKao3OZ/opqNyz48INPwstcoUQFZVsnMfhwBKnNIA==","signatures":[{"sig":"MEUCIQC8KFwQJVW+AqgX+t8y8v1WrgJankyTE8fhg8lmkUz66AIgLqnUl4I1gUzPYbR25JE8vSyvyhNwgZmk0X6VS3nRTJM=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":206905},"main":"build/index.js","types":"build/index.d.ts","gitHead":"eabbf0ee045e9fafbef1a9f4f1d7312f500d0eae","scripts":{"lint":"expo-module lint","test":"expo-module test","build":"expo-module build","clean":"expo-module clean","prepare":"expo-module prepare","open:ios":"xed example/ios","expo-module":"expo-module","open:android":"open -a \"Android Studio\" example/android","release:major":"./scripts/release.sh major","release:minor":"./scripts/release.sh minor","release:patch":"./scripts/release.sh patch","prepublishOnly":"expo-module prepublishOnly"},"_npmUser":{"name":"acube-io","email":"anders.jipwouo@a-cube.io"},"repository":{"url":"git+https://github.com/a-cube-io/expo-mutual-tls.git","type":"git"},"_npmVersion":"10.9.2","description":"Expo Mutual TLS module for iOS and Android.","directories":{},"_nodeVersion":"22.17.1","_hasShrinkwrap":false,"devDependencies":{"expo":"~53.0.0","@types/react":"~19.0.0","react-native":"0.79.1","expo-module-scripts":"^4.1.10"},"peerDependencies":{"expo":"*","react":"*","react-native":"*"},"_npmOperationalInternal":{"tmp":"tmp/expo-mutual-tls_1.0.5_1761050979317_0.8060687599560912","host":"s3://npm-registry-packages-npm-production"}}},"time":{"created":"2025-08-29T14:08:44.704Z","modified":"2026-02-23T15:13:15.656Z","0.1.0":"2025-08-29T14:08:45.008Z","1.0.0":"2025-08-29T15:20:58.543Z","1.0.1":"2025-08-29T15:27:42.190Z","1.0.2":"2025-08-29T15:37:02.579Z","1.0.3":"2025-10-07T15:10:41.762Z","1.0.5":"2025-10-21T12:49:39.499Z"},"bugs":{"url":"https://github.com/a-cube-io/expo-mutual-tls/issues"},"author":{"url":"https://github.com/a-cube-io","name":"A-Cube S.r.l."},"license":"MIT","homepage":"https://github.com/a-cube-io/expo-mutual-tls#readme","keywords":["react-native","expo","expo-mutual-tls","ExpoMutualTls","@a-cube-io/expo-mutual-tls","mutual-tls","@a-cube-io"],"repository":{"url":"git+https://github.com/a-cube-io/expo-mutual-tls.git","type":"git"},"description":"Expo Mutual TLS module for iOS and Android.","maintainers":[{"email":"paolo.casellati@a-cube.io","name":"paolo.casellati"},{"email":"filippo.antonielli@a-cube.io","name":"antox2"},{"email":"anders.jipwouo@a-cube.io","name":"acube-io"}],"readme":"# expo-mutual-tls\n\n🔒 **Production-ready Mutual TLS (mTLS) client certificate authentication for Expo/React Native applications**\n\n[![Platform - iOS](https://img.shields.io/badge/Platform-iOS-blue?logo=apple&logoColor=white)](https://developer.apple.com/ios/)\n[![Platform - Android](https://img.shields.io/badge/Platform-Android-green?logo=android&logoColor=white)](https://developer.android.com/)\n[![React Native](https://img.shields.io/badge/React_Native-blue?logo=react&logoColor=white)](https://reactnative.dev/)\n[![Expo](https://img.shields.io/badge/Expo-black?logo=expo&logoColor=white)](https://expo.dev/)\n\n## Overview\n\nThis Expo module provides secure, hardware-backed mTLS client certificate authentication for mobile applications. It supports both **P12 (PKCS#12)** and **PEM** certificate formats with enterprise-grade security features.\n\n### Key Features\n\n- 🔐 **Hardware-backed Security**: iOS Keychain & Android Keystore integration\n- 📱 **Cross-platform**: Native iOS (Swift) and Android (Kotlin) implementations\n- 🎯 **Simple API**: Easy-to-use utility functions for common operations\n- 📋 **Multiple Formats**: Support for P12/PKCS#12 and PEM certificate formats\n- 🔒 **Biometric Auth**: Optional biometric/device credential requirements\n- 📊 **Rich Events**: Debug logging, error handling, and certificate expiry warnings\n- ⚡ **Performance**: Optimized for production workloads\n- 🛡️ **Enterprise Ready**: Comprehensive certificate validation and security\n\n## Quick Start\n\n### Installation\n\n```bash\nnpx expo install '@a-cube-io/expo-mutual-tls'\n```\n\n### Basic Usage\n\n```typescript\nimport ExpoMutualTls from '@a-cube-io/expo-mutual-tls';\n\n// Configure for P12 certificates\nawait ExpoMutualTls.configureP12('my-keychain-service', true);\n\n// Store P12 certificate\nawait ExpoMutualTls.storeP12(p12Base64Data, 'certificate-password');\n\n// Make authenticated mTLS request\nconst response = await ExpoMutualTls.request('https://api.example.com/secure', {\n  method: 'POST',\n  headers: { 'Content-Type': 'application/json' },\n  body: JSON.stringify({ data: 'example' })\n});\n```\n\n## API Reference\n\n### Configuration Methods\n\n#### `configureP12(keychainService?, enableLogging?)`\n\nConfigure the module for P12/PKCS#12 certificate format.\n\n```typescript\nconst result = await ExpoMutualTls.configureP12(\n  'my-p12-service',  // Optional: keychain service name (default: 'client.p12')\n  true               // Optional: enable debug logging (default: false)\n);\n```\n\n#### `configurePEM(certService?, keyService?, enableLogging?)`\n\nConfigure the module for PEM certificate format.\n\n```typescript\nconst result = await ExpoMutualTls.configurePEM(\n  'cert-service',    // Optional: certificate service name\n  'key-service',     // Optional: private key service name  \n  true               // Optional: enable debug logging\n);\n```\n\n### Certificate Management\n\n#### `storeP12(p12Base64, password)`\n\nStore a P12/PKCS#12 certificate in secure storage.\n\n```typescript\nawait ExpoMutualTls.storeP12(\n  'MIIKXgIBAzCCCh...',  // Base64-encoded P12 data\n  'my-certificate-password'\n);\n```\n\n#### `storePEM(certificate, privateKey, passphrase?)`\n\nStore PEM certificate and private key in secure storage.\n\n```typescript\nawait ExpoMutualTls.storePEM(\n  '-----BEGIN CERTIFICATE-----\\n...',  // PEM certificate\n  '-----BEGIN PRIVATE KEY-----\\n...',   // PEM private key\n  'optional-passphrase'                 // Optional: passphrase for encrypted key\n);\n```\n\n#### `hasCertificate()`\n\nCheck if certificates are stored.\n\n```typescript\nconst hasStoredCert = await ExpoMutualTls.hasCertificate();\n```\n\n#### `removeCertificate()`\n\nRemove stored certificates from secure storage.\n\n```typescript\nawait ExpoMutualTls.removeCertificate();\n```\n\n### Network Operations\n\n#### `request(url, options?)`\n\nMake an authenticated mTLS request.\n\n```typescript\nconst result = await ExpoMutualTls.request('https://api.example.com', {\n  method: 'POST',\n  headers: { 'Authorization': 'Bearer token' },\n  body: JSON.stringify({ key: 'value' })\n});\n\nconsole.log('Status:', result.statusCode);\nconsole.log('TLS Version:', result.tlsVersion);\nconsole.log('Response:', result.body);\n```\n\n#### `testConnection(url)`\n\nTest mTLS connection to a URL (HEAD request).\n\n```typescript\nconst result = await ExpoMutualTls.testConnection('https://secure-api.example.com');\n```\n\n### State Management\n\n#### `isConfigured` (getter)\n\nCheck if the module is configured.\n\n```typescript\nif (ExpoMutualTls.isConfigured) {\n  // Module is ready for certificate operations\n}\n```\n\n#### `currentState` (getter)\n\nGet the current module state.\n\n```typescript\nconsole.log('Current state:', ExpoMutualTls.currentState);\n// Possible values: 'notConfigured', 'configured', 'error'\n```\n\n### Event Handling\n\nThe module provides comprehensive event utilities for monitoring mTLS operations, debugging, and certificate lifecycle management.\n\n#### `onDebugLog(callback)`\n\nListen for debug log events including network requests, certificate operations, and system information.\n\n```typescript\nconst debugSubscription = ExpoMutualTls.onDebugLog(event => {\n  console.log(`[${event.type}] ${event.message}`);\n  \n  // Access additional event data\n  if (event.method) console.log('HTTP Method:', event.method);\n  if (event.url) console.log('Request URL:', event.url);\n  if (event.statusCode) console.log('Status Code:', event.statusCode);\n  if (event.duration) console.log('Duration:', event.duration + 'ms');\n});\n\n// Remember to remove the listener when done\ndebugSubscription.remove();\n```\n\n**Event Types:**\n- `certificate_storage` - Certificate store/retrieve operations\n- `network_request` - HTTP/HTTPS requests\n- `keychain_operation` - Keychain access operations\n- `tls_handshake` - TLS/SSL handshake information\n\n#### `onError(callback)`\n\nListen for error events from all module operations.\n\n```typescript\nconst errorSubscription = ExpoMutualTls.onError(event => {\n  console.error('mTLS Error:', event.message);\n  \n  // Handle specific error codes\n  if (event.code) {\n    switch (event.code) {\n      case 'CERTIFICATE_NOT_FOUND':\n        console.log('Action: Store a certificate first');\n        break;\n      case 'SSL_HANDSHAKE_FAILED':\n        console.log('Action: Check certificate validity');\n        break;\n      case 'KEYCHAIN_ACCESS_DENIED':\n        console.log('Action: Check app permissions');\n        break;\n      default:\n        console.error('Error Code:', event.code);\n    }\n  }\n});\n\n// Remove listener when done\nerrorSubscription.remove();\n```\n\n#### `onCertificateExpiry(callback)`\n\nListen for certificate expiry warnings and notifications.\n\n```typescript\nconst expirySubscription = ExpoMutualTls.onCertificateExpiry(event => {\n  const expiryDate = new Date(event.expiry);\n  \n  console.warn('Certificate Expiry Warning:');\n  console.warn('Subject:', event.subject);\n  console.warn('Expires:', expiryDate.toLocaleDateString());\n  \n  if (event.alias) {\n    console.warn('Alias:', event.alias);\n  }\n  \n  if (event.warning) {\n    console.warn('⚠️ Certificate expires soon!');\n  }\n  \n  // Calculate days until expiry\n  const daysUntilExpiry = Math.ceil((event.expiry - Date.now()) / (1000 * 60 * 60 * 24));\n  console.warn(`Days until expiry: ${daysUntilExpiry}`);\n});\n\n// Remove listener when done  \nexpirySubscription.remove();\n```\n\n#### `removeAllListeners()`\n\nRemove all active event listeners at once.\n\n```typescript\n// Remove all event listeners\nExpoMutualTls.removeAllListeners();\n```\n\n### Complete Event Handling Example\n\n```typescript\nimport { useEffect } from 'react';\nimport ExpoMutualTls from '@a-cube-io/expo-mutual-tls';\n\nexport default function MyComponent() {\n  useEffect(() => {\n    // Set up all event listeners\n    const debugSubscription = ExpoMutualTls.onDebugLog((event) => {\n      const message = event.message || '';\n      const method = event.method ? ` [${event.method}]` : '';\n      const url = event.url ? ` ${event.url}` : '';\n      const statusCode = event.statusCode ? ` (${event.statusCode})` : '';\n      const duration = event.duration ? ` ${event.duration}ms` : '';\n      \n      console.log(`🔍 Debug [${event.type}]: ${message}${method}${url}${statusCode}${duration}`);\n    });\n\n    const errorSubscription = ExpoMutualTls.onError((event) => {\n      const code = event.code ? ` [${event.code}]` : '';\n      console.error(`❌ Error: ${event.message}${code}`);\n      \n      // Show user-friendly error messages\n      if (event.code === 'CERTIFICATE_NOT_FOUND') {\n        alert('Please store a certificate first');\n      }\n    });\n\n    const expirySubscription = ExpoMutualTls.onCertificateExpiry((event) => {\n      const expiryDate = new Date(event.expiry).toLocaleDateString();\n      const alias = event.alias ? ` (${event.alias})` : '';\n      const warning = event.warning ? ' ⚠️' : '';\n      \n      console.warn(`📅 Certificate Expiry${warning}: ${event.subject}${alias} - expires ${expiryDate}`);\n      \n      if (event.warning) {\n        alert(`Certificate expiring soon: ${event.subject}`);\n      }\n    });\n\n    // Cleanup all listeners on unmount\n    return () => {\n      debugSubscription.remove();\n      errorSubscription.remove();\n      expirySubscription.remove();\n    };\n  }, []);\n\n  // Component JSX...\n}\n```\n\n## Advanced Configuration\n\n### Complete Configuration Options\n\nFor advanced use cases, you can use the raw module interface:\n\n```typescript\nimport { ExpoMutualTlsModuleRaw, MutualTlsConfig } from '@a-cube-io/expo-mutual-tls';\n\nconst config: MutualTlsConfig = {\n  certificateFormat: 'p12',\n  keychainServiceForP12: 'custom.p12.service',\n  keychainServiceForPassword: 'custom.password.service',\n  enableLogging: true,\n  requireUserAuthentication: true,      // Require biometric/device auth\n  userAuthValiditySeconds: 300,         // Auth validity duration\n  expiryWarningDays: 30                 // Days before expiry to warn\n};\n\nconst result = await ExpoMutualTlsModuleRaw.configure(config);\n```\n\n### Security Features\n\n#### Biometric Authentication\n\nEnable biometric or device credential authentication:\n\n```typescript\nconst config: MutualTlsConfig = {\n  certificateFormat: 'p12',\n  requireUserAuthentication: true,\n  userAuthValiditySeconds: 300,  // 5 minutes\n  // ... other options\n};\n```\n\n#### Certificate Validation\n\nThe module performs comprehensive certificate validation:\n\n- ✅ Certificate expiry checking\n- ✅ Extended Key Usage (EKU) validation for client authentication\n- ✅ Private key/certificate pairing verification\n- ✅ Certificate chain validation\n- ✅ Hardware-backed key storage\n\n## Platform Implementation\n\n### iOS Implementation\n\n- **Security Framework**: Uses iOS Security Framework APIs\n- **Keychain Integration**: Secure keychain storage with hardware backing\n- **Certificate Parsing**: Native PEM and P12 parsing\n- **TLS Integration**: URLSession with custom SSL context\n\n### Android Implementation  \n\n- **Android Keystore**: Hardware-backed key storage when available\n- **BouncyCastle**: PEM certificate parsing and cryptographic operations\n- **OkHttp Integration**: mTLS-enabled HTTP client\n- **Biometric Support**: Android Biometric API integration\n\n## Error Handling\n\nThe module provides detailed error information:\n\n```typescript\ntry {\n  await ExpoMutualTls.request('https://api.example.com');\n} catch (error) {\n  console.error('Request failed:', error.message);\n  // Handle specific error types\n  if (error.code === 'CERTIFICATE_NOT_FOUND') {\n    // Certificate is not stored\n  } else if (error.code === 'SSL_HANDSHAKE_FAILED') {\n    // mTLS handshake failed\n  }\n}\n```\n\n### Common Error Codes\n\n| Code | Description | Solution |\n|------|-------------|----------|\n| `NOT_CONFIGURED` | Module not configured | Call configure method first |\n| `CERTIFICATE_NOT_FOUND` | No certificate stored | Store certificate before making requests |\n| `INVALID_CERTIFICATE_FORMAT` | Certificate format invalid | Verify certificate data and format |\n| `SSL_HANDSHAKE_FAILED` | mTLS handshake failed | Check certificate validity and server configuration |\n| `KEYCHAIN_ACCESS_DENIED` | Keychain access denied | Check app permissions or retry with authentication |\n\n## Example Apps\n\n### Complete P12 Example with Event Handling\n\n```typescript\nimport React, { useEffect, useState } from 'react';\nimport ExpoMutualTls from '@a-cube-io/expo-mutual-tls';\nimport { Asset } from 'expo-asset';\nimport * as FileSystem from 'expo-file-system';\n\nexport default function App() {\n  const [logs, setLogs] = useState<string[]>([]);\n  const [status, setStatus] = useState('Ready');\n\n  const addLog = (message: string) => {\n    const timestamp = new Date().toLocaleTimeString();\n    setLogs(prev => [`[${timestamp}] ${message}`, ...prev.slice(0, 19)]);\n  };\n\n  // Comprehensive event listeners setup\n  useEffect(() => {\n    // Debug logging with detailed information\n    const debugSubscription = ExpoMutualTls.onDebugLog((event) => {\n      const message = event.message || '';\n      const method = event.method ? ` [${event.method}]` : '';\n      const url = event.url ? ` ${event.url}` : '';\n      const statusCode = event.statusCode ? ` (${event.statusCode})` : '';\n      const duration = event.duration ? ` ${event.duration}ms` : '';\n      \n      addLog(`🔍 Debug [${event.type}]: ${message}${method}${url}${statusCode}${duration}`);\n      console.log(`Debug [${event.type}]:`, message, { \n        method: event.method, \n        url: event.url, \n        statusCode: event.statusCode, \n        duration: event.duration \n      });\n    });\n\n    // Error handling with user-friendly messages\n    const errorSubscription = ExpoMutualTls.onError((event) => {\n      const code = event.code ? ` [${event.code}]` : '';\n      addLog(`❌ Error: ${event.message}${code}`);\n      console.error('mTLS Error:', event.message, event.code ? `Code: ${event.code}` : '');\n      \n      // Provide user guidance based on error codes\n      if (event.code === 'CERTIFICATE_NOT_FOUND') {\n        setStatus('Please store a certificate first');\n      } else if (event.code === 'SSL_HANDSHAKE_FAILED') {\n        setStatus('Certificate validation failed');\n      }\n    });\n\n    // Certificate expiry monitoring\n    const expirySubscription = ExpoMutualTls.onCertificateExpiry((event) => {\n      const expiryDate = new Date(event.expiry).toLocaleDateString();\n      const alias = event.alias ? ` (${event.alias})` : '';\n      const warning = event.warning ? ' ⚠️' : '';\n      \n      addLog(`📅 Certificate Expiry${warning}: ${event.subject}${alias} - expires ${expiryDate}`);\n      console.warn('Certificate expiry warning:', {\n        subject: event.subject,\n        alias: event.alias,\n        expiry: expiryDate,\n        warning: event.warning\n      });\n      \n      if (event.warning) {\n        setStatus(`Certificate expires soon: ${event.subject}`);\n      }\n    });\n\n    // Cleanup listeners on unmount\n    return () => {\n      debugSubscription.remove();\n      errorSubscription.remove();\n      expirySubscription.remove();\n    };\n  }, []);\n\n  const setupP12Certificate = async () => {\n    try {\n      setStatus('Setting up P12 certificate...');\n      \n      // Configure for P12 with logging enabled\n      await ExpoMutualTls.configureP12('demo-service', true);\n      addLog('✅ P12 configuration completed');\n      \n      // Load P12 certificate from assets\n      const [asset] = await Asset.loadAsync(require('./assets/client.p12'));\n      const p12Data = await FileSystem.readAsStringAsync(asset.localUri!, {\n        encoding: FileSystem.EncodingType.Base64,\n      });\n      \n      // Store certificate\n      await ExpoMutualTls.storeP12(p12Data, 'certificate-password');\n      addLog('✅ P12 certificate stored successfully');\n      \n      // Test connection\n      const result = await ExpoMutualTls.request('https://secure-api.example.com', {\n        method: 'GET',\n        headers: { 'Accept': 'application/json' }\n      });\n      \n      if (result.success) {\n        addLog(`✅ Connection successful! Status: ${result.statusCode}, TLS: ${result.tlsVersion}`);\n        setStatus(`Connected successfully (${result.statusCode})`);\n      } else {\n        addLog('❌ Connection failed');\n        setStatus('Connection failed');\n      }\n      \n    } catch (error) {\n      addLog(`❌ Setup failed: ${error}`);\n      setStatus('Setup failed');\n      console.error('Setup failed:', error);\n    }\n  };\n\n  return (\n    <div>\n      <h1>mTLS P12 Demo</h1>\n      <p>Status: {status}</p>\n      <button onClick={setupP12Certificate}>Setup P12 Certificate</button>\n      \n      <h2>Activity Logs</h2>\n      <div style={{ height: '200px', overflow: 'auto', border: '1px solid #ccc' }}>\n        {logs.map((log, index) => (\n          <div key={index} style={{ fontSize: '12px', fontFamily: 'monospace' }}>\n            {log}\n          </div>\n        ))}\n      </div>\n      \n      <button onClick={() => ExpoMutualTls.removeAllListeners()}>\n        Clear All Event Listeners\n      </button>\n    </div>\n  );\n}\n```\n\n### Complete PEM Example with Event Handling\n\n```typescript\nimport React, { useEffect } from 'react';\nimport ExpoMutualTls from '@a-cube-io/expo-mutual-tls';\nimport { Asset } from 'expo-asset';\nimport * as FileSystem from 'expo-file-system';\n\nconst PEMCertificateDemo = () => {\n  useEffect(() => {\n    // Set up comprehensive event monitoring\n    const debugSubscription = ExpoMutualTls.onDebugLog((event) => {\n      console.log(`🔍 [${event.type}] ${event.message}`);\n      if (event.url) console.log(`   URL: ${event.url}`);\n      if (event.duration) console.log(`   Duration: ${event.duration}ms`);\n    });\n\n    const errorSubscription = ExpoMutualTls.onError((event) => {\n      console.error(`❌ mTLS Error: ${event.message}`);\n      if (event.code) console.error(`   Code: ${event.code}`);\n    });\n\n    const expirySubscription = ExpoMutualTls.onCertificateExpiry((event) => {\n      console.warn(`📅 Certificate \"${event.subject}\" expires on ${new Date(event.expiry).toLocaleDateString()}`);\n    });\n\n    return () => {\n      debugSubscription.remove();\n      errorSubscription.remove();\n      expirySubscription.remove();\n    };\n  }, []);\n\n  const setupPEMCertificates = async () => {\n    try {\n      // Configure for PEM with debug logging\n      console.log('Configuring PEM certificate format...');\n      await ExpoMutualTls.configurePEM('cert-service', 'key-service', true);\n      \n      // Load PEM files from assets\n      console.log('Loading PEM certificate files...');\n      const [certAsset, keyAsset] = await Asset.loadAsync([\n        require('./assets/client.pem'),\n        require('./assets/client.key')\n      ]);\n      \n      const certificate = await FileSystem.readAsStringAsync(certAsset.localUri!);\n      const privateKey = await FileSystem.readAsStringAsync(keyAsset.localUri!);\n      \n      // Store certificates\n      console.log('Storing PEM certificates...');\n      await ExpoMutualTls.storePEM(certificate, privateKey);\n      \n      // Verify certificates are stored\n      const hasCerts = await ExpoMutualTls.hasCertificate();\n      console.log('Certificate verification:', hasCerts ? '✅ Present' : '❌ Missing');\n      \n      if (hasCerts) {\n        // Make authenticated request\n        console.log('Making authenticated mTLS request...');\n        const response = await ExpoMutualTls.request('https://api.example.com/data', {\n          method: 'POST',\n          headers: { \n            'Accept': 'application/json',\n            'Content-Type': 'application/json'\n          },\n          body: JSON.stringify({ action: 'getData', timestamp: Date.now() })\n        });\n        \n        if (response.success) {\n          console.log('✅ API Request successful!');\n          console.log(`   Status: ${response.statusCode} ${response.statusMessage}`);\n          console.log(`   TLS Version: ${response.tlsVersion}`);\n          console.log(`   Cipher Suite: ${response.cipherSuite}`);\n          console.log('   Response:', JSON.parse(response.body));\n        } else {\n          console.log('❌ API Request failed');\n        }\n      }\n      \n    } catch (error) {\n      console.error('❌ PEM setup failed:', error);\n      \n      // Handle specific error scenarios\n      if (error.code === 'INVALID_CERTIFICATE_FORMAT') {\n        console.error('   Solution: Check PEM file format and encoding');\n      } else if (error.code === 'KEYCHAIN_ACCESS_DENIED') {\n        console.error('   Solution: Check app keychain permissions');\n      }\n    }\n  };\n\n  return (\n    <div>\n      <h1>mTLS PEM Demo</h1>\n      <button onClick={setupPEMCertificates}>\n        Setup PEM Certificates & Test\n      </button>\n    </div>\n  );\n};\n\nexport default PEMCertificateDemo;\n```\n\n## Troubleshooting\n\n### Common Issues\n\n**iOS Build Errors:**\n- Ensure iOS deployment target is 11.0 or higher\n- Add required iOS frameworks in your app configuration\n\n**Android Build Errors:**\n- Verify Android API level 24 (Android 7.0) or higher\n- Ensure BouncyCastle dependencies are properly resolved\n\n**Certificate Issues:**\n- Verify certificate format and encoding\n- Check certificate expiry dates\n- Ensure a private key matches a certificate public key\n\n**Network Issues:**\n- Verify server supports mTLS client certificate authentication\n- Check server certificate authority trust chain\n- Ensure proper network connectivity\n\n### Debug Logging\n\nEnable comprehensive logging:\n\n```typescript\n// Enable debug logging during configuration\nawait ExpoMutualTls.configureP12('service', true);\n\n// Listen for debug events\nExpoMutualTls.onDebugLog(event => {\n  console.log(`[${event.type}] ${event.message}`);\n  if (event.url) console.log(`URL: ${event.url}`);\n  if (event.statusCode) console.log(`Status: ${event.statusCode}`);\n  if (event.duration) console.log(`Duration: ${event.duration}ms`);\n});\n```\n\n## Security Considerations\n\n### Certificate Storage\n- Certificates are stored in hardware-backed secure storage when available\n- iOS: Uses iOS Keychain with hardware encryption\n- Android: Uses Android Keystore with hardware security module (HSM)\n\n### Best Practices\n- Enable biometric authentication for sensitive applications\n- Use short authentication validity periods\n- Implement certificate rotation procedures\n- Monitor certificate expiry dates\n- Validate server certificates properly\n\n### Compliance\n- Supports enterprise security requirements\n- Hardware-backed cryptographic operations\n- Audit-friendly debug logging\n- Secure credential lifecycle management\n\n## Migration Guide\n\n### From v0.0.x to v0.1.x\n\nThe v0.1.x release introduces simplified utility functions:\n\n**Before (v0.0.x):**\n```typescript\nimport ExpoMutualTlsModule, { MutualTlsConfig } from '@a-cube-io/expo-mutual-tls';\n\nconst config: MutualTlsConfig = {\n  certificateFormat: 'p12',\n  keychainServiceForP12: 'service',\n  enableLogging: true\n};\nawait ExpoMutualTlsModule.configure(config);\n```\n\n**After (v0.1.x):**\n```typescript\nimport ExpoMutualTls from '@a-cube-io/expo-mutual-tls';\n\nawait ExpoMutualTls.configureP12('service', true);\n```\n\nThe raw module interface is still available for advanced use cases via `ExpoMutualTlsModuleRaw`.\n\n## Contributing\n\nContributions are very welcome!\nPlease refer to guidelines described in the [contributing guide]( https://github.com/a-cube-io/expo-mutual-tls#contributing).\n\n### Development Setup\n\n1. Clone the repository\n2. Install dependencies: `npm install`\n3. Build the module: `npm run build`\n4. Run example app: `cd example && npx expo run:ios`\n\n## License\n\nMIT License - see [LICENSE](LICENSE) file for details.\n\n## Support\n\n- 📋 [GitHub Issues](https://github.com/a-cube-io/expo-mutual-tls/issues)\n- 📖 [Documentation](https://github.com/a-cube-io/expo-mutual-tls)\n---\n\n**Made with ❤️ for secure mobile applications**\n","readmeFilename":"README.md"}