Files
sendico/frontend/pshared/lib/service/authorization/token_mutex.dart
Stephan D c6a56071b5
Some checks failed
ci/woodpecker/push/bff Pipeline was successful
ci/woodpecker/push/db Pipeline was successful
ci/woodpecker/push/billing_fees Pipeline was successful
ci/woodpecker/push/chain_gateway Pipeline was successful
ci/woodpecker/push/fx_ingestor Pipeline was successful
ci/woodpecker/push/frontend Pipeline was successful
ci/woodpecker/push/fx_oracle Pipeline was successful
ci/woodpecker/push/nats Pipeline was successful
ci/woodpecker/push/ledger Pipeline was successful
ci/woodpecker/push/notification Pipeline was successful
ci/woodpecker/push/payments_orchestrator Pipeline was successful
ci/woodpecker/push/bump_version Pipeline failed
+signup +login
2025-11-17 20:16:45 +01:00

103 lines
3.4 KiB
Dart

import 'dart:async';
import 'package:logging/logging.dart';
/// Mutex to prevent concurrent token refresh operations
/// This ensures only one refresh operation happens at a time,
/// preventing race conditions during app startup when multiple
/// providers try to refresh tokens simultaneously.
class TokenRefreshMutex {
static final _instance = TokenRefreshMutex._();
factory TokenRefreshMutex() => _instance;
TokenRefreshMutex._();
static final _logger = Logger('service.authorization.token_mutex');
Completer<String>? _currentRefresh;
Completer<void>? _currentRotation;
/// Execute a token refresh operation with mutex protection
/// If another refresh is in progress, wait for it to complete
Future<String> executeRefresh(Future<String> Function() refreshOperation) async {
if (_currentRefresh != null) {
_logger.fine('Token refresh already in progress, waiting for completion');
return await _currentRefresh!.future;
}
_logger.fine('Starting new token refresh operation');
_currentRefresh = Completer<String>();
try {
final result = await refreshOperation();
if (_currentRefresh != null) {
_currentRefresh!.complete(result);
_logger.fine('Token refresh completed successfully');
}
return result;
} catch (e, st) {
_logger.warning('Token refresh failed', e, st);
if (_currentRefresh != null) {
_currentRefresh!.completeError(e, st);
}
rethrow;
} finally {
_currentRefresh = null;
}
}
/// Execute a token rotation operation with mutex protection
/// If another rotation is in progress, wait for it to complete
Future<void> executeRotation(Future<void> Function() rotationOperation) async {
if (_currentRotation != null) {
_logger.fine('Token rotation already in progress, waiting for completion');
return await _currentRotation!.future;
}
_logger.fine('Starting new token rotation operation');
_currentRotation = Completer<void>();
try {
await rotationOperation();
if (_currentRotation != null) {
_currentRotation!.complete();
_logger.fine('Token rotation completed successfully');
}
} catch (e, st) {
_logger.warning('Token rotation failed', e, st);
if (_currentRotation != null) {
_currentRotation!.completeError(e, st);
}
rethrow;
} finally {
_currentRotation = null;
}
}
/// Check if a refresh operation is currently in progress
bool get isRefreshInProgress => _currentRefresh != null;
/// Check if a rotation operation is currently in progress
bool get isRotationInProgress => _currentRotation != null;
/// Get current status for debugging
Map<String, dynamic> getStatus() {
return {
'refreshInProgress': isRefreshInProgress,
'rotationInProgress': isRotationInProgress,
'timestamp': DateTime.now().toIso8601String(),
};
}
/// Force reset the mutex (for testing or emergency situations)
void forceReset() {
_logger.warning('Force resetting token refresh mutex');
if (_currentRefresh != null && !_currentRefresh!.isCompleted) {
_currentRefresh!.completeError(Exception('Mutex force reset'));
}
if (_currentRotation != null && !_currentRotation!.isCompleted) {
_currentRotation!.completeError(Exception('Mutex force reset'));
}
_currentRefresh = null;
_currentRotation = null;
}
}