Skip to content

Commit

Permalink
Optimize the usage of OTP
Browse files Browse the repository at this point in the history
Feature:#88 - Optimize the usage of OTP.
Users are restricted from requesting OTP more than 3 times within a 60-second timeframe. If the limit is exceeded, users will need to wait for 60 seconds before being allowed to retry OTP.
  • Loading branch information
lijogeorgep authored and georgepadayatti committed Dec 14, 2023
1 parent 6954dd1 commit defcafd
Show file tree
Hide file tree
Showing 6 changed files with 184 additions and 77 deletions.
78 changes: 56 additions & 22 deletions lib/app/modules/login/controllers/login_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,25 @@ import 'package:flutter/material.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:get/get.dart';
import 'package:intl_phone_number_input/intl_phone_number_input.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:uuid/uuid.dart';

import '../../../data/model/login/LoginResponse.dart';
import '../../../data/repository/user_repository_impl.dart';
import '/app/core/base/base_controller.dart';

String? sessionToken;
const int timeWindow = 1 * 60 * 1000; // 1 minute time window
const int maxMessagesPerWindow = 3;
class LoginController extends BaseController {
PhoneNumber number = PhoneNumber(isoCode: 'SE');
final UserRepositoryImpl _impl = UserRepositoryImpl();
final TextEditingController phoneNumberController = TextEditingController();
var sharePhoneNumber = "".obs;
var isControl = true.obs;
String? isdCode;

int currentTime = 0;
int lastMessageTime = 0;
int messageCount = 0;
loginUser() async {
String pattern = r'(^(?:[+0]9)?[0-9]{10,12}$)';
RegExp regExp = RegExp(pattern);
Expand All @@ -31,29 +37,57 @@ class LoginController extends BaseController {
GetSnackToast(message:appLocalization.loginValidPhoneNumberValidationText);
}
else{
sharePhoneNumber.value = isdCode! + phoneNumberController.text;
debugPrint("shared number:" + sharePhoneNumber.value);
LoginRequest request =
LoginRequest(mobile_number: isdCode! + phoneNumberController.text);
try {
LoginResponse response = await _impl.login(request);
if (response.msg == "OTP sent") {
hideLoading();
phoneNumberController.clear();
isControl.value = true;
Get.to(OtpView());
phoneNumberController.clear();
}
} catch (e) {
// showToast((e as ApiException).message);
GetSnackToast(message: (e as ApiException).message);

hideLoading();
final SharedPreferences prefs = await SharedPreferences.getInstance();
int currentTime = DateTime.now().millisecondsSinceEpoch;
sessionToken = generateSessionToken(phoneNumberController.text); // Generate session token
lastMessageTime = prefs.getInt('last_message_time_$sessionToken') ?? 0;
messageCount = prefs.getInt('message_count_$sessionToken') ?? 0;
// Maximum messages allowed per time window
if (currentTime - lastMessageTime >= timeWindow) {
// Reset the rate limit if the time window has elapsed
messageCount = 0;
lastMessageTime = currentTime;
}
finally{
if (messageCount >= maxMessagesPerWindow) {
// Rate limit exceeded, display an error message or take appropriate action
hideLoading();
GetSnackToast(
message: appLocalization.otpLimitExceeded,);
}
else{
// Within the rate limit, proceed with OTP request
// Increment message count and update last message time
messageCount++;
lastMessageTime = currentTime;
await prefs.setInt('message_count_$sessionToken', messageCount);
await prefs.setInt('last_message_time_$sessionToken', lastMessageTime);
// Execute the code to request OTP
sharePhoneNumber.value = isdCode! + phoneNumberController.text;
debugPrint("shared number:" + sharePhoneNumber.value);
LoginRequest request =
LoginRequest(mobile_number: isdCode! + phoneNumberController.text);
try {
LoginResponse response = await _impl.login(request);
if (response.msg == "OTP sent") {
hideLoading();
phoneNumberController.clear();
isControl.value = true;
Get.to(OtpView());
phoneNumberController.clear();
}
} catch (e) {
GetSnackToast(message: (e as ApiException).message);
hideLoading();
}
finally{
hideLoading();
}
}
}

}
String generateSessionToken(String phoneNumber) {
return Uuid()
.v5(Uuid.NAMESPACE_URL, phoneNumber)
.toString(); // Generate session token using UUID v5
}
}
106 changes: 73 additions & 33 deletions lib/app/modules/otp/controllers/otp_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -60,49 +60,89 @@ class OtpController extends BaseController {
}

void resendLoginOTP() async {
debugPrint(
"shared Login Resend number:" + loginController.sharePhoneNumber.value);
LoginRequest request =
LoginRequest(mobile_number: loginController.sharePhoneNumber.value);
try {
LoginResponse response = await _impl.login(request);
if (response.msg == "OTP send") {
final SharedPreferences prefs = await SharedPreferences.getInstance();
final int currentTime = DateTime.now().millisecondsSinceEpoch;
int lastMessageTime = prefs.getInt('last_message_time_$sessionToken') ?? 0;
int messageCount = prefs.getInt('message_count_$sessionToken') ?? 0;
if (currentTime - lastMessageTime >= timeWindow) {
// Reset the rate limit if the time window has elapsed
messageCount = 0;
lastMessageTime = currentTime;
}
if (messageCount >= maxMessagesPerWindow) {
// Rate limit exceeded, display an error message or take appropriate action
hideLoading();
GetSnackToast(
message: "OTP request limit exceeded. Please try again after sometime.");
}
else{
messageCount++;
lastMessageTime = currentTime;

await prefs.setInt('message_count_$sessionToken', messageCount);
await prefs.setInt('last_message_time_$sessionToken', lastMessageTime);
LoginRequest request =
LoginRequest(mobile_number: loginController.sharePhoneNumber.value);
try {
LoginResponse response = await _impl.login(request);
if (response.msg == "OTP send") {
hideLoading();
}
} catch (e) {
GetSnackToast(message: (e as ApiException).message);
hideLoading();
} finally {
hideLoading();
}
} catch (e) {
GetSnackToast(message: (e as ApiException).message);

hideLoading();
} finally {
hideLoading();
}
}

void resendRegisterOTP() async {
debugPrint("shared Register Resend number:" +
registerController.sharePhoneNumber.value);
debugPrint(
"shared Register firstname:" + registerController.shareFirstName.value);
debugPrint(
"shared Register lastname:" + registerController.shareLastName.value);
SharedPreferences _prefs = await SharedPreferences.getInstance();
var userId = _prefs.getString('privacyDashboarduserId');
RegisterRequest request = RegisterRequest(
firstname: registerController.shareFirstName.value,
lastname: userId,
mobile_number: registerController.sharePhoneNumber.value);
try {
RegisterResponse response = await _impl.register(request);
final SharedPreferences prefs = await SharedPreferences.getInstance();
final int currentTime = DateTime.now().millisecondsSinceEpoch;
int lastMessageTime = prefs.getInt('last_message_time_$sessionToken') ?? 0;
int messageCount = prefs.getInt('message_count_$sessionToken') ?? 0;
if (currentTime - lastMessageTime >= timeWindow) {
// Reset the rate limit if the time window has elapsed
messageCount = 0;
lastMessageTime = currentTime;
}
if (messageCount >= maxMessagesPerWindow) {
// Rate limit exceeded, display an error message or take appropriate action
hideLoading();
GetSnackToast(
message: "OTP request limit exceeded. Please try again after sometime.");
}else{
messageCount++;
lastMessageTime = currentTime;
await prefs.setInt('message_count_$sessionToken', messageCount);
await prefs.setInt('last_message_time_$sessionToken', lastMessageTime);
debugPrint("shared Register Resend number:" +
registerController.sharePhoneNumber.value);
debugPrint(
"shared Register firstname:" + registerController.shareFirstName.value);
debugPrint(
"shared Register lastname:" + registerController.shareLastName.value);
SharedPreferences _prefs = await SharedPreferences.getInstance();
var userId = _prefs.getString('privacyDashboarduserId');
RegisterRequest request = RegisterRequest(
firstname: registerController.shareFirstName.value,
lastname: userId,
mobile_number: registerController.sharePhoneNumber.value);
try {
RegisterResponse response = await _impl.register(request);

if (response.msg == "OTP sent") {
hideLoading();
}
} catch (e) {
GetSnackToast(message: (e as ApiException).message);

if (response.msg == "OTP sent") {
hideLoading();
} finally {
hideLoading();
}
} catch (e) {
GetSnackToast(message: (e as ApiException).message);

hideLoading();
} finally {
hideLoading();
}
}
}
70 changes: 50 additions & 20 deletions lib/app/modules/register/controllers/register_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import 'package:flutter/services.dart';
import 'package:get/get.dart';
import 'package:intl_phone_number_input/intl_phone_number_input.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:uuid/uuid.dart';
import 'package:webview_flutter/webview_flutter.dart';
import '../../login/controllers/login_controller.dart';
import '/app/core/base/base_controller.dart';
Expand Down Expand Up @@ -73,32 +74,56 @@ class RegisterController extends BaseController {
if (responseMap['optIn'] == true) {
// Handle success
showLoading();
final SharedPreferences prefs = await SharedPreferences.getInstance();
final int currentTime = DateTime.now().millisecondsSinceEpoch;
shareFirstName.value = firstNameController.text;
//shareLastName.value = lastNameController.text;
shareLastName.value = "";
sharePhoneNumber.value = isdCode! + mobileNumberController.text;
RegisterRequest request = RegisterRequest(
firstname: firstNameController.text,
lastname: userId,
mobile_number: isdCode! + mobileNumberController.text);
try {
RegisterResponse response = await _impl.register(request);

if (response.msg == "OTP sent") {
hideLoading();
loginController.isControl.value = false;
Get.off(OtpView());
sessionToken = generateSessionToken(sharePhoneNumber.value);
int lastMessageTime = prefs.getInt('last_message_time_$sessionToken') ?? 0;
int messageCount = prefs.getInt('message_count_$sessionToken') ?? 0;
if (currentTime - lastMessageTime >= timeWindow) {
// Reset the rate limit if the time window has elapsed
messageCount = 0;
lastMessageTime = currentTime;
}
if (messageCount >= maxMessagesPerWindow) {
// Rate limit exceeded, display an error message or take appropriate action
hideLoading();
GetSnackToast(
message: appLocalization.otpLimitExceeded);
}else{
// Within the rate limit, proceed with OTP request
// Increment message count and update last message time
messageCount++;
lastMessageTime = currentTime;

await prefs.setInt('message_count_$sessionToken', messageCount);
await prefs.setInt('last_message_time_$sessionToken', lastMessageTime);
RegisterRequest request = RegisterRequest(
firstname: firstNameController.text,
lastname: userId,
mobile_number: isdCode! + mobileNumberController.text);
try {
RegisterResponse response = await _impl.register(request);

if (response.msg == "OTP sent") {
hideLoading();
loginController.isControl.value = false;
Get.off(OtpView());
firstNameController.clear();
mobileNumberController.clear();
}
} catch (e) {
firstNameController.clear();
mobileNumberController.clear();
}
} catch (e) {
firstNameController.clear();
mobileNumberController.clear();
GetSnackToast(message: (e as ApiException).message);
GetSnackToast(message: (e as ApiException).message);

hideLoading();
} finally {
hideLoading();
hideLoading();
} finally {
hideLoading();
}
}
} else {
GetSnackToast(message: 'Something went wrong');
Expand All @@ -108,6 +133,11 @@ class RegisterController extends BaseController {
}
}

String generateSessionToken(String phoneNumber) {
return Uuid()
.v5(Uuid.NAMESPACE_URL, phoneNumber)
.toString(); // Generate session token using UUID v5
}
Future<void> showDataAgreement() async {
// if (Platform.isAndroid) {
switch (selectedIndex.value) {
Expand Down Expand Up @@ -255,7 +285,7 @@ class RegisterController extends BaseController {
curve: Curves.ease,
);
} else {
GetSnackToast(message: 'Something went wrong');
GetSnackToast(message: 'Something went wrong.Please try again');
}
hideLoading();
} catch (e) {
Expand Down
3 changes: 2 additions & 1 deletion lib/l10n/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@
"insightsDexcomTitle":"Dexcom Login",
"insightsDexcomContent":"Please login to Dexcom to get your estimated glucose values",
"insightsOk":"Ok",
"insightsCancel":"Cancel"
"insightsCancel":"Cancel",
"otpLimitExceeded":"OTP request limit exceeded. Please try again after sometime."

}
3 changes: 2 additions & 1 deletion lib/l10n/app_sv.arb
Original file line number Diff line number Diff line change
Expand Up @@ -94,5 +94,6 @@
"insightsDexcomTitle":"Dexcom inloggning",
"insightsDexcomContent":"Logga in på Dexcom för att få dina uppskattade glukosvärden",
"insightsOk":"Okej",
"insightsCancel":"Annullera"
"insightsCancel":"Annullera",
"otpLimitExceeded":"Gränsen för OTP-begäran har överskridits. Försök igen efter en stund."
}
1 change: 1 addition & 0 deletions pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ dependencies:
firebase_core: ^2.13.0
firebase_core_platform_interface: 4.8.0
firebase_crashlytics: ^3.3.1
uuid: ^3.0.7

dev_dependencies:
flutter_test:
Expand Down

0 comments on commit defcafd

Please sign in to comment.