Compare commits

...

1 Commits

Author SHA1 Message Date
0e9c2e0055 implementation of security service 2025-11-26 10:10:54 +05:30
3 changed files with 65 additions and 3 deletions

View File

@ -40,6 +40,7 @@ import 'package:on_field_work/model/service_project/service_project_job_detail_m
import 'package:on_field_work/model/service_project/job_attendance_logs_model.dart';
import 'package:on_field_work/model/service_project/job_allocation_model.dart';
import 'package:on_field_work/model/service_project/service_project_branches_model.dart';
import 'package:on_field_work/helpers/services/security_service.dart';
class ApiService {
static const bool enableLogs = true;
@ -165,7 +166,7 @@ class ApiService {
.timeout(extendedTimeout);
logSafe("Response Status: ${response.statusCode}", level: LogLevel.debug);
logSafe("Response Body: ${response.body}", level: LogLevel.debug);
logSafe("Encrypted Response: ${response.body}", level: LogLevel.debug);
if (response.statusCode == 401 && !hasRetried) {
logSafe("Unauthorized (401). Attempting token refresh...",
@ -186,6 +187,14 @@ class ApiService {
await LocalStorage.logout();
}
// *********** DECRYPT HERE *************
final decrypted = await SecurityService.decryptResponse(response.body);
if (decrypted != null) {
return http.Response(jsonEncode(decrypted), response.statusCode);
}
// *************************************
return response;
} catch (e) {
logSafe("HTTP GET Exception: $e", level: LogLevel.error);
@ -219,6 +228,17 @@ class ApiService {
customTimeout: customTimeout, hasRetried: true);
}
}
logSafe("Encrypted Response: ${response.body}", level: LogLevel.debug);
// *********** DECRYPT HERE *************
final decrypted = await SecurityService.decryptResponse(response.body);
if (decrypted != null) {
return http.Response(jsonEncode(decrypted), response.statusCode);
}
// *************************************
return response;
} catch (e) {
logSafe("HTTP POST Exception: $e", level: LogLevel.error);
@ -671,8 +691,7 @@ class ApiService {
'pageNumber': pageNumber.toString(),
'pageSize': pageSize.toString(),
'isActive': isActive.toString(),
if (isArchive)
'isArchive': 'true',
if (isArchive) 'isArchive': 'true',
};
final response = await _getRequest(endpoint, queryParams: queryParams);

View File

@ -0,0 +1,41 @@
import 'dart:convert';
import 'package:cryptography/cryptography.dart';
class SecurityService {
// Same 32-byte key
static const _keyBase64 = "Your32ByteBase64KeyGoesHere/1234567890+XY=";
static Future<dynamic> decryptResponse(String encryptedBase64) async {
final algorithm = AesGcm.with256bits();
// 1. Decode Key and Data
final secretKey = await algorithm.newSecretKeyFromBytes(base64.decode(_keyBase64));
final encryptedBytes = base64.decode(encryptedBase64);
// 2. Extract Parts: [Nonce 12] + [Ciphertext] + [Tag 16]
// The "SecretBox" class helps us organize these parts
final nonce = encryptedBytes.sublist(0, 12);
final ciphertext = encryptedBytes.sublist(12, encryptedBytes.length - 16);
final mac = encryptedBytes.sublist(encryptedBytes.length - 16);
final secretBox = SecretBox(
ciphertext,
nonce: nonce,
mac: Mac(mac),
);
// 3. Decrypt
try {
final decryptedBytes = await algorithm.decrypt(
secretBox,
secretKey: secretKey,
);
final decryptedString = utf8.decode(decryptedBytes);
return json.decode(decryptedString);
} catch (e) {
print("Decryption failed: $e");
return null;
}
}
}

View File

@ -83,6 +83,8 @@ dependencies:
timeago: ^3.7.1
cached_network_image: ^3.4.1
cryptography: ^2.7.0
gallery_saver_plus: ^3.2.9
share_plus: ^12.0.1
timeline_tile: ^2.0.0