Update: 2026-05-06 02:59:42
This commit is contained in:
34
musadaq-app/lib/core/network/dio_client.dart
Normal file
34
musadaq-app/lib/core/network/dio_client.dart
Normal file
@@ -0,0 +1,34 @@
|
||||
import 'package:dio/dio.dart';
|
||||
import 'hmac_interceptor.dart';
|
||||
import '../storage/secure_storage.dart';
|
||||
|
||||
class DioClient {
|
||||
static const String baseUrl = 'https://musadaq.intaleqapp.com/api/v1/'; // Update with actual URL
|
||||
late final Dio dio;
|
||||
|
||||
DioClient() {
|
||||
dio = Dio(
|
||||
BaseOptions(
|
||||
baseUrl: baseUrl,
|
||||
connectTimeout: const Duration(seconds: 15),
|
||||
receiveTimeout: const Duration(seconds: 15),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
// Add Interceptors
|
||||
dio.interceptors.add(HmacInterceptor(SecureStorage()));
|
||||
|
||||
// Logging interceptor for debug
|
||||
dio.interceptors.add(LogInterceptor(
|
||||
requestBody: true,
|
||||
responseBody: true,
|
||||
error: true,
|
||||
));
|
||||
}
|
||||
|
||||
Dio get client => dio;
|
||||
}
|
||||
54
musadaq-app/lib/core/network/hmac_interceptor.dart
Normal file
54
musadaq-app/lib/core/network/hmac_interceptor.dart
Normal file
@@ -0,0 +1,54 @@
|
||||
import 'dart:convert';
|
||||
import 'package:crypto/crypto.dart';
|
||||
import 'package:dio/dio.dart';
|
||||
import '../storage/secure_storage.dart';
|
||||
|
||||
class HmacInterceptor extends Interceptor {
|
||||
final SecureStorage secureStorage;
|
||||
|
||||
HmacInterceptor(this.secureStorage);
|
||||
|
||||
@override
|
||||
void onRequest(RequestOptions options, RequestInterceptorHandler handler) async {
|
||||
final token = await secureStorage.getToken();
|
||||
final deviceSecret = await secureStorage.getDeviceSecret();
|
||||
|
||||
if (token != null) {
|
||||
options.headers['Authorization'] = 'Bearer $token';
|
||||
}
|
||||
|
||||
// Only sign if we have a device secret (after login)
|
||||
if (deviceSecret != null) {
|
||||
final timestamp = DateTime.now().millisecondsSinceEpoch.toString();
|
||||
|
||||
// Create signature payload
|
||||
String payload = '${options.method}:${options.path}:$timestamp';
|
||||
|
||||
// Include body in signature if present
|
||||
if (options.data != null && options.data is Map) {
|
||||
payload += ':${jsonEncode(options.data)}';
|
||||
}
|
||||
|
||||
// Generate HMAC-SHA256
|
||||
final key = utf8.encode(deviceSecret);
|
||||
final bytes = utf8.encode(payload);
|
||||
final hmac = Hmac(sha256, key);
|
||||
final digest = hmac.convert(bytes);
|
||||
|
||||
// Attach headers
|
||||
options.headers['X-Timestamp'] = timestamp;
|
||||
options.headers['X-Signature'] = digest.toString();
|
||||
}
|
||||
|
||||
super.onRequest(options, handler);
|
||||
}
|
||||
|
||||
@override
|
||||
void onError(DioException err, ErrorInterceptorHandler handler) {
|
||||
if (err.response?.statusCode == 401) {
|
||||
// Handle Token Expiry / Unauthorized
|
||||
// TODO: Trigger logout or token refresh
|
||||
}
|
||||
super.onError(err, handler);
|
||||
}
|
||||
}
|
||||
29
musadaq-app/lib/core/storage/secure_storage.dart
Normal file
29
musadaq-app/lib/core/storage/secure_storage.dart
Normal file
@@ -0,0 +1,29 @@
|
||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||
|
||||
class SecureStorage {
|
||||
final FlutterSecureStorage _storage = const FlutterSecureStorage();
|
||||
|
||||
static const String _keyToken = 'jwt_token';
|
||||
static const String _keyDeviceSecret = 'device_secret';
|
||||
static const String _keyUserId = 'user_id';
|
||||
|
||||
Future<void> saveToken(String token) async {
|
||||
await _storage.write(key: _keyToken, value: token);
|
||||
}
|
||||
|
||||
Future<String?> getToken() async {
|
||||
return await _storage.read(key: _keyToken);
|
||||
}
|
||||
|
||||
Future<void> saveDeviceSecret(String secret) async {
|
||||
await _storage.write(key: _keyDeviceSecret, value: secret);
|
||||
}
|
||||
|
||||
Future<String?> getDeviceSecret() async {
|
||||
return await _storage.read(key: _keyDeviceSecret);
|
||||
}
|
||||
|
||||
Future<void> clearAll() async {
|
||||
await _storage.deleteAll();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user