import 'push_secure_storage.dart'; /// Persists the bookkeeping produced by a successful push registration: /// the Nextcloud device identifier, the per-user server public key (needed to /// verify incoming push signatures), the FCM token the registration was made /// with (so a token refresh can be detected) and the endpoints it was bound to /// (so an endpoint switch in the dev tools can be detected). class PushRegistrationStore { static const _deviceIdentifierKey = 'push_device_identifier'; static const _serverPublicKeyKey = 'push_server_public_key_pem'; static const _registeredTokenKey = 'push_registered_fcm_token'; static const _proxyServerKey = 'push_registered_proxy_server'; static const _ncBaseUrlKey = 'push_registered_nc_base_url'; // Native-only context: the iOS AppDelegate answers Talk notification actions // (reply / mark-as-read) directly via URLSession while the Flutter engine is // not guaranteed to run. It needs the Nextcloud username and base URL from the // shared (group-scoped) keychain; the app password already lives there // (AccountData writes `nextcloud_app_password` group-scoped). static const _usernameKey = 'nextcloud_username'; static const _baseUrlKey = 'nextcloud_base_url'; const PushRegistrationStore(); Future save({ required String deviceIdentifier, required String serverPublicKeyPem, required String fcmToken, required String proxyServer, required String ncBaseUrl, }) async { await pushSecureStorage.write( key: _deviceIdentifierKey, value: deviceIdentifier, ); await pushSecureStorage.write( key: _serverPublicKeyKey, value: serverPublicKeyPem, ); await pushSecureStorage.write(key: _registeredTokenKey, value: fcmToken); await pushSecureStorage.write(key: _proxyServerKey, value: proxyServer); await pushSecureStorage.write(key: _ncBaseUrlKey, value: ncBaseUrl); } /// Persists the username and Nextcloud base URL group-scoped so the native /// iOS Talk action handler (AppDelegate) can authenticate OCS calls. The base /// URL is a full origin like `https://cloud.marianum-fulda.de` (domain + /// optional path, no trailing slash). Future saveNativeAuthContext({ required String username, required String baseUrl, }) async { await pushSecureStorage.write(key: _usernameKey, value: username); await pushSecureStorage.write(key: _baseUrlKey, value: baseUrl); } Future deviceIdentifier() => pushSecureStorage.read(key: _deviceIdentifierKey); Future serverPublicKeyPem() => pushSecureStorage.read(key: _serverPublicKeyKey); Future registeredFcmToken() => pushSecureStorage.read(key: _registeredTokenKey); /// Proxy-server URL the current registration was made with. Future registeredProxyServer() => pushSecureStorage.read(key: _proxyServerKey); /// Nextcloud base URL the current registration was made against. Future registeredNcBaseUrl() => pushSecureStorage.read(key: _ncBaseUrlKey); /// True when a registration has been persisted (used by the cold-start /// self-heal to decide whether to (re-)register). Future isRegistered() async => (await registeredFcmToken())?.isNotEmpty ?? false; Future clear() async { await pushSecureStorage.delete(key: _deviceIdentifierKey); await pushSecureStorage.delete(key: _serverPublicKeyKey); await pushSecureStorage.delete(key: _registeredTokenKey); await pushSecureStorage.delete(key: _proxyServerKey); await pushSecureStorage.delete(key: _ncBaseUrlKey); await pushSecureStorage.delete(key: _usernameKey); await pushSecureStorage.delete(key: _baseUrlKey); } }