From 7a8bd54dba5cbd7698a78ad1e3454a78279d9413 Mon Sep 17 00:00:00 2001 From: cogwheel0 <172976095+cogwheel0@users.noreply.github.com> Date: Sat, 11 Oct 2025 13:16:31 +0530 Subject: [PATCH] refactor: remove self-signed certificate manager and streamline certificate handling - Deleted the SelfSignedCertificateManager and its associated files to simplify the certificate management process. - Updated ApiService and ConnectivityService to include self-signed certificate configuration directly, enhancing clarity and maintainability. - Adjusted comments to reflect the new approach to handling self-signed certificates, ensuring better understanding of security considerations. - Improved the application startup sequence by deferring unnecessary initializations, contributing to a more efficient first paint performance. --- lib/core/services/api_service.dart | 13 ++ lib/core/services/connectivity_service.dart | 13 ++ .../services/optimized_storage_service.dart | 11 -- .../self_signed_certificate_manager.dart | 23 ---- .../self_signed_certificate_manager_io.dart | 117 ------------------ .../self_signed_certificate_manager_stub.dart | 7 -- lib/main.dart | 8 +- 7 files changed, 27 insertions(+), 165 deletions(-) delete mode 100644 lib/core/services/self_signed_certificate_manager.dart delete mode 100644 lib/core/services/self_signed_certificate_manager_io.dart delete mode 100644 lib/core/services/self_signed_certificate_manager_stub.dart diff --git a/lib/core/services/api_service.dart b/lib/core/services/api_service.dart index ce2219e..3f0b622 100644 --- a/lib/core/services/api_service.dart +++ b/lib/core/services/api_service.dart @@ -128,6 +128,16 @@ class ApiService { } } + /// Configures this Dio instance to accept self-signed certificates. + /// + /// When [ServerConfig.allowSelfSignedCertificates] is enabled, this method + /// sets up a [badCertificateCallback] that trusts certificates from the + /// configured server's host and port. + /// + /// Security considerations: + /// - Only certificates from the exact host/port are trusted + /// - If no port is specified, all ports on the host are trusted + /// - Web platforms ignore this (browsers handle TLS validation) void _configureSelfSignedSupport() { if (kIsWeb || !serverConfig.allowSelfSignedCertificates) { return; @@ -149,12 +159,15 @@ class ApiService { final port = baseUri.hasPort ? baseUri.port : null; client.badCertificateCallback = (X509Certificate cert, String requestHost, int requestPort) { + // Only trust certificates from our configured server if (requestHost.toLowerCase() != host) { return false; } + // If no specific port configured, trust any port on this host if (port == null) { return true; } + // Otherwise, port must match exactly return requestPort == port; }; return client; diff --git a/lib/core/services/connectivity_service.dart b/lib/core/services/connectivity_service.dart index 5eff435..8a0a9eb 100644 --- a/lib/core/services/connectivity_service.dart +++ b/lib/core/services/connectivity_service.dart @@ -288,6 +288,16 @@ class ConnectivityService with WidgetsBindingObserver { } /// Configures the Dio instance to accept self-signed certificates. + /// + /// This method sets up a [badCertificateCallback] that trusts certificates + /// from the specified server's host and port for health check requests. + /// + /// Security considerations: + /// - Only certificates from the exact host/port are trusted + /// - If no port is specified in the URL, all ports on the host are trusted + /// - Web platforms ignore this (browsers handle TLS validation) + /// + /// This is called per-Dio-instance rather than using global HttpOverrides. static void configureSelfSignedCerts(Dio dio, String serverUrl) { if (kIsWeb) return; @@ -304,8 +314,11 @@ class ConnectivityService with WidgetsBindingObserver { client.badCertificateCallback = (X509Certificate cert, String requestHost, int requestPort) { + // Only trust certificates from our configured server if (requestHost.toLowerCase() != host) return false; + // If no specific port configured, trust any port on this host if (port == null) return true; + // Otherwise, port must match exactly return requestPort == port; }; diff --git a/lib/core/services/optimized_storage_service.dart b/lib/core/services/optimized_storage_service.dart index 73bec77..777e449 100644 --- a/lib/core/services/optimized_storage_service.dart +++ b/lib/core/services/optimized_storage_service.dart @@ -9,7 +9,6 @@ import '../persistence/hive_boxes.dart'; import '../persistence/persistence_keys.dart'; import '../utils/debug_logger.dart'; import 'secure_credential_storage.dart'; -import 'self_signed_certificate_manager.dart'; /// Optimized storage service backed by Hive for non-sensitive data and /// FlutterSecureStorage for credentials. @@ -197,9 +196,6 @@ class OptimizedStorageService { await _secureCredentialStorage.saveServerConfigs(jsonString); _cache['server_config_count'] = configs.length; _cacheTimestamps['server_config_count'] = DateTime.now(); - SelfSignedCertificateManager.instance - ..ensureInitialized() - ..updateTrustedServers(configs); DebugLogger.log( 'Server configs saved (${configs.length} entries)', scope: 'storage/optimized', @@ -219,9 +215,6 @@ class OptimizedStorageService { if (jsonString == null || jsonString.isEmpty) { _cache['server_config_count'] = 0; _cacheTimestamps['server_config_count'] = DateTime.now(); - SelfSignedCertificateManager.instance - ..ensureInitialized() - ..clearTrustedServers(); return const []; } @@ -231,16 +224,12 @@ class OptimizedStorageService { .toList(); _cache['server_config_count'] = configs.length; _cacheTimestamps['server_config_count'] = DateTime.now(); - SelfSignedCertificateManager.instance - ..ensureInitialized() - ..updateTrustedServers(configs); return configs; } catch (error) { DebugLogger.log( 'Failed to retrieve server configs: $error', scope: 'storage/optimized', ); - SelfSignedCertificateManager.instance.clearTrustedServers(); return const []; } } diff --git a/lib/core/services/self_signed_certificate_manager.dart b/lib/core/services/self_signed_certificate_manager.dart deleted file mode 100644 index 6bdefbd..0000000 --- a/lib/core/services/self_signed_certificate_manager.dart +++ /dev/null @@ -1,23 +0,0 @@ -import '../models/server_config.dart'; -import 'self_signed_certificate_manager_io.dart' - if (dart.library.html) 'self_signed_certificate_manager_stub.dart' - as platform; - -/// Coordinates opt-in trust for self-signed TLS certificates. -/// -/// On IO platforms we install an [HttpOverrides] that whitelists the servers -/// flagged in [ServerConfig.allowSelfSignedCertificates]. On web platforms the -/// helpers are no-ops because browsers manage TLS validation themselves. -class SelfSignedCertificateManager { - const SelfSignedCertificateManager._(); - - static const SelfSignedCertificateManager instance = - SelfSignedCertificateManager._(); - - void ensureInitialized() => platform.ensureInitialized(); - - void updateTrustedServers(Iterable configs) => - platform.updateTrustedServers(configs); - - void clearTrustedServers() => platform.clearTrustedServers(); -} diff --git a/lib/core/services/self_signed_certificate_manager_io.dart b/lib/core/services/self_signed_certificate_manager_io.dart deleted file mode 100644 index 9601f1a..0000000 --- a/lib/core/services/self_signed_certificate_manager_io.dart +++ /dev/null @@ -1,117 +0,0 @@ -import 'dart:io'; - -import '../models/server_config.dart'; - -final _IoSelfSignedCertificateManager _manager = - _IoSelfSignedCertificateManager(); - -void ensureInitialized() => _manager.ensureInitialized(); - -void updateTrustedServers(Iterable configs) => - _manager.updateTrustedServers(configs); - -void clearTrustedServers() => _manager.clearTrustedServers(); - -class _IoSelfSignedCertificateManager { - _IoSelfSignedCertificateManager(); - - _ConduitHttpOverrides? _overrides; - - void ensureInitialized() { - if (_overrides != null) return; - - final overrides = _ConduitHttpOverrides(); - HttpOverrides.global = overrides; - _overrides = overrides; - } - - void updateTrustedServers(Iterable configs) { - ensureInitialized(); - _overrides?.updateTrustedServers(configs); - } - - void clearTrustedServers() { - _overrides?.clearTrustedServers(); - } -} - -class _ConduitHttpOverrides extends HttpOverrides { - final Set<_TrustedEndpoint> _trustedEndpoints = {}; - - void updateTrustedServers(Iterable configs) { - _trustedEndpoints - ..clear() - ..addAll( - configs - .where((config) => config.allowSelfSignedCertificates) - .map((config) => _TrustedEndpoint.fromUrl(config.url)) - .whereType<_TrustedEndpoint>(), - ); - } - - void clearTrustedServers() { - _trustedEndpoints.clear(); - } - - bool _shouldTrust(String host, int port) { - for (final endpoint in _trustedEndpoints) { - if (endpoint.matches(host, port)) { - return true; - } - } - return false; - } - - @override - HttpClient createHttpClient(SecurityContext? context) { - final client = super.createHttpClient(context); - client.badCertificateCallback = - (X509Certificate cert, String host, int port) => - _shouldTrust(host, port); - return client; - } -} - -class _TrustedEndpoint { - const _TrustedEndpoint({required this.host, this.port}); - - final String host; - final int? port; - - static _TrustedEndpoint? fromUrl(String url) { - final uri = _normalizeUrl(url); - if (uri == null || uri.host.isEmpty) { - return null; - } - final normalizedHost = uri.host.toLowerCase(); - final normalizedPort = uri.hasPort ? uri.port : null; - return _TrustedEndpoint(host: normalizedHost, port: normalizedPort); - } - - static Uri? _normalizeUrl(String value) { - if (value.trim().isEmpty) { - return null; - } - Uri? parsed = Uri.tryParse(value.trim()); - if (parsed == null) { - return null; - } - if (!parsed.hasScheme) { - parsed = - Uri.tryParse('https://${value.trim()}') ?? - Uri.tryParse('http://${value.trim()}'); - } - return parsed; - } - - bool matches(String otherHost, int otherPort) { - final normalizedHost = otherHost.toLowerCase(); - if (normalizedHost != host) { - return false; - } - if (port == null) { - return true; - } - return port == otherPort; - } -} diff --git a/lib/core/services/self_signed_certificate_manager_stub.dart b/lib/core/services/self_signed_certificate_manager_stub.dart deleted file mode 100644 index 4abbafd..0000000 --- a/lib/core/services/self_signed_certificate_manager_stub.dart +++ /dev/null @@ -1,7 +0,0 @@ -import '../models/server_config.dart'; - -void ensureInitialized() {} - -void updateTrustedServers(Iterable configs) {} - -void clearTrustedServers() {} diff --git a/lib/main.dart b/lib/main.dart index 3ceb824..1fce419 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -11,7 +11,6 @@ import 'core/providers/app_providers.dart'; import 'core/persistence/hive_bootstrap.dart'; import 'core/persistence/persistence_migrator.dart'; import 'core/persistence/persistence_providers.dart'; -import 'core/services/self_signed_certificate_manager.dart'; import 'core/router/app_router.dart'; import 'features/auth/providers/unified_auth_providers.dart'; import 'core/auth/auth_state_manager.dart'; @@ -57,16 +56,11 @@ void main() { _startupTimeline!.start('app_startup'); _startupTimeline!.instant('bindings_initialized'); - // Defer edge-to-edge mode and certificate manager to post-frame to avoid - // impacting first paint + // Defer edge-to-edge mode to post-frame to avoid impacting first paint WidgetsBinding.instance.addPostFrameCallback((_) { // ignore: discarded_futures SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); _startupTimeline?.instant('edge_to_edge_enabled'); - - // Initialize certificate manager lazily after first frame - SelfSignedCertificateManager.instance.ensureInitialized(); - _startupTimeline?.instant('cert_manager_ready'); }); const secureStorage = FlutterSecureStorage(