diff --git a/lib/core/dependencies.dart b/lib/core/dependencies.dart index c4f7402c..ecfe1324 100644 --- a/lib/core/dependencies.dart +++ b/lib/core/dependencies.dart @@ -84,6 +84,7 @@ import 'package:hmg_patient_app_new/services/logger_service.dart'; import 'package:hmg_patient_app_new/services/navigation_service.dart'; import 'package:hmg_patient_app_new/services/notification_service.dart'; import 'package:hmg_patient_app_new/services/permission_service.dart'; +import 'package:hmg_patient_app_new/core/services/turnstile_service.dart'; import 'package:hmg_patient_app_new/widgets/date_range_selector/viewmodel/date_range_calendar_model.dart'; import 'package:hmg_patient_app_new/widgets/date_range_selector/viewmodel/date_range_view_model.dart'; import 'package:local_auth/local_auth.dart'; @@ -155,6 +156,7 @@ class AppDependencies { ); getIt.registerLazySingleton(() => PermissionService()); + getIt.registerLazySingleton(() => TurnstileService(getIt())); // Repositories getIt.registerLazySingleton(() => CommonRepoImp(loggerService: getIt())); @@ -255,7 +257,8 @@ class AppDependencies { dialogService: getIt(), appState: getIt(), errorHandlerService: getIt(), - localAuthService: getIt()), + localAuthService: getIt(), + turnstileService: getIt()), ); getIt.registerLazySingleton(() => ProfileSettingsViewModel( diff --git a/lib/core/services/turnstile_service.dart b/lib/core/services/turnstile_service.dart new file mode 100644 index 00000000..f6665190 --- /dev/null +++ b/lib/core/services/turnstile_service.dart @@ -0,0 +1,56 @@ +import 'dart:async'; +import 'package:cloudflare_turnstile/cloudflare_turnstile.dart'; +import 'package:flutter/foundation.dart'; +import 'package:hmg_patient_app_new/services/logger_service.dart'; + +/// Service to handle Cloudflare Turnstile captcha verification +class TurnstileService { + final LoggerService _loggerService; + + // Replace with your Cloudflare Turnstile site key + static const String _siteKey = '0x4AAAAAAAkmbvEy63pcxBBt'; + + TurnstileService(this._loggerService); + + /// Get Turnstile token using invisible mode + /// Returns the token on success, null on failure + Future getTurnstileToken() async { + // Initialize an instance of invisible Cloudflare Turnstile with your site key + final turnstile = CloudflareTurnstile.invisible( + siteKey: _siteKey, + ); + + try { + // Get the Turnstile token + final token = await turnstile.getToken(); + _loggerService.logInfo('Turnstile token retrieved successfully'); + return token; // Return the token upon success + } on TurnstileException catch (e) { + // Handle Turnstile failure + _loggerService.logError('Turnstile challenge failed: ${e.message}'); + if (kDebugMode) { + print('Challenge failed: ${e.message}'); + } + } catch (e) { + // Handle any other exceptions + _loggerService.logError('Unexpected error during Turnstile challenge: $e'); + if (kDebugMode) { + print('Unexpected error: $e'); + } + } finally { + // Ensure the Turnstile instance is properly disposed of + turnstile.dispose(); + } + + // Return null if the token couldn't be generated + return null; + } + + /// Get the site key for Turnstile widget + String getSiteKey() => _siteKey; + + /// Validate if Turnstile is properly configured + bool isConfigured() { + return _siteKey.isNotEmpty && _siteKey != '0x4AAAAAAAkmbvEy63pcxBBt'; + } +} diff --git a/lib/features/authentication/authentication_repo.dart b/lib/features/authentication/authentication_repo.dart index c355819e..291e4a9b 100644 --- a/lib/features/authentication/authentication_repo.dart +++ b/lib/features/authentication/authentication_repo.dart @@ -147,6 +147,9 @@ class AuthenticationRepoImp implements AuthenticationRepo { bool isRegister = false, bool isFormFamilyFile = false, }) async { + + + int isOutKsa = (sendActivationCodeReq.zipCode == '966' || sendActivationCodeReq.zipCode == '+966') ? 0 : 1; sendActivationCodeReq.patientOutSA = isOutKsa; sendActivationCodeReq.isDentalAllowedBackend = false; diff --git a/lib/features/authentication/authentication_view_model.dart b/lib/features/authentication/authentication_view_model.dart index b51b36e4..6ec5d2bf 100644 --- a/lib/features/authentication/authentication_view_model.dart +++ b/lib/features/authentication/authentication_view_model.dart @@ -42,6 +42,7 @@ import 'package:hmg_patient_app_new/services/dialog_service.dart'; import 'package:hmg_patient_app_new/services/error_handler_service.dart'; import 'package:hmg_patient_app_new/services/localauth_service.dart'; import 'package:hmg_patient_app_new/services/navigation_service.dart'; +import 'package:hmg_patient_app_new/core/services/turnstile_service.dart'; import 'package:hmg_patient_app_new/widgets/date_range_selector/viewmodel/date_range_calendar_model.dart'; import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart'; import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart'; @@ -60,6 +61,7 @@ class AuthenticationViewModel extends ChangeNotifier { final DialogService _dialogService; final NavigationService _navigationService; final LocalAuthService _localAuthService; + final TurnstileService _turnstileService; AuthenticationViewModel({ required AppState appState, @@ -69,12 +71,14 @@ class AuthenticationViewModel extends ChangeNotifier { required NavigationService navigationService, required CacheService cacheService, required LocalAuthService localAuthService, + required TurnstileService turnstileService, }) : _navigationService = navigationService, _dialogService = dialogService, _errorHandlerService = errorHandlerService, _appState = appState, _authenticationRepo = authenticationRepo, - _localAuthService = localAuthService {} + _localAuthService = localAuthService, + _turnstileService = turnstileService {} final TextEditingController nationalIdController = TextEditingController(), phoneNumberController = TextEditingController(), dobController = TextEditingController(), @@ -974,6 +978,15 @@ class AuthenticationViewModel extends ChangeNotifier { // TODO: GET APP SMS SIGNATURE HERE request.sMSSignature = await getSignature(); + + // Get Turnstile token if configured + final turnstileToken = await _turnstileService.getTurnstileToken(); + if (turnstileToken != null) { + request.turnstileToken = turnstileToken; + } else { + request.turnstileToken = ""; + } + if (isNeedLoading) { LoaderBottomSheet.showLoader(); } diff --git a/lib/features/authentication/models/request_models/send_activation_request_model.dart b/lib/features/authentication/models/request_models/send_activation_request_model.dart index 2908f7ec..6e2ddb93 100644 --- a/lib/features/authentication/models/request_models/send_activation_request_model.dart +++ b/lib/features/authentication/models/request_models/send_activation_request_model.dart @@ -30,6 +30,7 @@ class SendActivationRequest { int? status; int? familyRegionID; bool? isPatientExcluded; + String? turnstileToken; SendActivationRequest( {this.patientMobileNumber, @@ -62,7 +63,8 @@ class SendActivationRequest { this.responseID, this.status, this.familyRegionID, - this.isPatientExcluded}); + this.isPatientExcluded, + this.turnstileToken}); SendActivationRequest.fromJson(Map json) { patientMobileNumber = json['PatientMobileNumber']; @@ -96,6 +98,7 @@ class SendActivationRequest { status = json['Status']; familyRegionID = json['FamilyRegionID']; isPatientExcluded = json['IsPatientExcluded']; + turnstileToken = json['TurnstileToken']; } Map toJson() { @@ -135,6 +138,9 @@ class SendActivationRequest { data['Status'] = status; data['FamilyRegionID'] = familyRegionID; data['IsPatientExcluded'] = isPatientExcluded; + if (turnstileToken != null) { + data['TurnstileToken'] = turnstileToken; + } return data; } } diff --git a/pubspec.yaml b/pubspec.yaml index a86f25ac..bdce8130 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -109,6 +109,7 @@ dependencies: flutter_amazonpaymentservices: ^0.0.13 screen_brightness: ^1.0.1 flutter_screenshot_blocker: ^1.0.4 + cloudflare_turnstile: ^3.7.2 dev_dependencies: flutter_test: