Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions packages/komodo_defi_framework/app_build/build_config.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,10 @@
"coins": {
"fetch_at_build_enabled": true,
"update_commit_on_build": true,
"bundled_coins_repo_commit": "8fab732731df59a2c1ccc694d565e8535cd51c93",
"bundled_coins_repo_commit": "aceacfb4996a9bda784b0dfda588e24bba3fa9f8",
"coins_repo_api_url": "https://api.github.com/repos/GLEECBTC/coins",
"coins_repo_content_url": "https://raw.githubusercontent.com/GLEECBTC/coins",
"coins_repo_branch": "feat/add-tron-coins",
"coins_repo_branch": "master",
"runtime_updates_enabled": true,
"mapped_files": {
"assets/config/coins_config.json": "utils/coins_config_unfiltered.json",
Expand Down
61 changes: 48 additions & 13 deletions packages/komodo_defi_local_auth/lib/src/auth/auth_service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,16 @@ abstract interface class IAuthService {
/// is fully integrated with KW. This may be deprecated in the future.
Future<void> setActiveUserMetadata(JsonMap metadata);

/// Atomically reads the current value of [key] from the active user's
/// metadata, applies [transform] to it, and writes the result back.
///
/// This is safe to call concurrently — a dedicated metadata mutex
/// serialises all read-modify-write cycles.
Future<void> updateActiveUserMetadataKey(
String key,
dynamic Function(dynamic currentValue) transform,
);

/// Attempts to restore a user session without requiring password authentication
/// Only works if the KDF API is running and the wallet exists
Future<void> restoreSession(KdfUser user);
Expand Down Expand Up @@ -123,6 +133,7 @@ class KdfAuthService implements IAuthService {
StreamController.broadcast();
final SecureLocalStorage _secureStorage = SecureLocalStorage();
final ReadWriteMutex _authMutex = ReadWriteMutex();
final Mutex _metadataMutex = Mutex();
final Logger _logger = Logger('KdfAuthService');
final String _sessionId;

Expand Down Expand Up @@ -697,21 +708,45 @@ class KdfAuthService implements IAuthService {

@override
Future<void> setActiveUserMetadata(Map<String, dynamic> metadata) async {
final activeUser = await _activeUserOrThrow();
// TODO: Implement locks for this to avoid this method interfering with
// more sensitive operations.
final user = await _secureStorage.getUser(activeUser.walletId.name);
if (user == null) throw AuthException.notFound();
await _metadataMutex.protect(() async {
final activeUser = await _activeUserOrThrow();
final user = await _secureStorage.getUser(activeUser.walletId.name);
if (user == null) throw AuthException.notFound();

final updatedUser = user.copyWith(metadata: metadata);
await _secureStorage.saveUser(updatedUser);

// Update cache silently without triggering auth state change. Updating
// the storage and cache at the same time emulates the same behaviour as
// before. Update user metadata for any subsequent access without emitting
// auth state changes, as the metadata field is currently used for events
// like coin activation, wallet type (derivation), and seed backup status
_lastEmittedUser = updatedUser;
});
}

final updatedUser = user.copyWith(metadata: metadata);
await _secureStorage.saveUser(updatedUser);
@override
Future<void> updateActiveUserMetadataKey(
String key,
dynamic Function(dynamic currentValue) transform,
) async {
await _metadataMutex.protect(() async {
final activeUser = await _activeUserOrThrow();
final user = await _secureStorage.getUser(activeUser.walletId.name);
if (user == null) throw AuthException.notFound();

final metadata = JsonMap.from(user.metadata);
final transformed = transform(metadata[key]);
if (transformed == null) {
metadata.remove(key);
} else {
metadata[key] = transformed;
}

// Update cache silently without triggering auth state change. Updating the
// storage and cache at the same time emulates the same behaviour as before.
// Update user metadata for any subsequent access without emitting auth
// state changes, as the metadata field is currently used for events like
// coin activation, wallet type (derivation), and seed backup status
_lastEmittedUser = updatedUser;
final updatedUser = user.copyWith(metadata: metadata);
await _secureStorage.saveUser(updatedUser);
_lastEmittedUser = updatedUser;
});
}

@override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import 'package:komodo_defi_local_auth/src/auth/auth_state.dart';
import 'package:komodo_defi_local_auth/src/auth/storage/secure_storage.dart';
import 'package:komodo_defi_local_auth/src/trezor/_trezor_index.dart';
import 'package:komodo_defi_rpc_methods/komodo_defi_rpc_methods.dart';
import 'package:komodo_defi_types/komodo_defi_type_utils.dart';
import 'package:komodo_defi_types/komodo_defi_types.dart';

/// The [KomodoDefiAuth] class provides a simplified local authentication
Expand Down Expand Up @@ -193,6 +192,15 @@ abstract interface class KomodoDefiAuth {

Future<void> setOrRemoveActiveUserKeyValue(String key, dynamic value);

/// Atomically reads the current value of [key] from the active user's
/// metadata, applies [transform], and writes the result back.
///
/// Safe to call concurrently — uses a dedicated metadata mutex internally.
Future<void> updateActiveUserKeyValue(
String key,
dynamic Function(dynamic currentValue) transform,
);

/// Provides PIN to a Trezor hardware device during authentication.
///
/// The [taskId] should be obtained from the authentication state when the
Expand Down Expand Up @@ -611,15 +619,15 @@ class KomodoDefiLocalAuth implements KomodoDefiAuth {

@override
Future<void> setOrRemoveActiveUserKeyValue(String key, dynamic value) async {
final activeUser = await _authService.getActiveUser();

if (activeUser == null) throw AuthException.notFound();

final updatedMetadata = JsonMap.from(activeUser.metadata)..[key] = value;

if (value == null) updatedMetadata.remove(key);
await _authService.updateActiveUserMetadataKey(key, (_) => value);
}

await _authService.setActiveUserMetadata(updatedMetadata);
@override
Future<void> updateActiveUserKeyValue(
String key,
dynamic Function(dynamic currentValue) transform,
) async {
await _authService.updateActiveUserMetadataKey(key, transform);
}

@override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,13 @@ class TrezorAuthService implements IAuthService {
Future<void> setActiveUserMetadata(JsonMap metadata) =>
_authService.setActiveUserMetadata(metadata);

@override
Future<void> updateActiveUserMetadataKey(
String key,
dynamic Function(dynamic currentValue) transform,
) =>
_authService.updateActiveUserMetadataKey(key, transform);

@override
Future<void> restoreSession(KdfUser user) =>
_authService.restoreSession(user);
Expand Down
Loading