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
135 lines
5.3 KiB
Dart
135 lines
5.3 KiB
Dart
import 'package:logging/logging.dart';
|
|
|
|
import 'package:pshared/api/errors/authorization_failed.dart';
|
|
import 'package:pshared/api/requests/login.dart';
|
|
import 'package:pshared/api/requests/login_data.dart';
|
|
import 'package:pshared/api/responses/account.dart';
|
|
import 'package:pshared/api/responses/login.dart';
|
|
import 'package:pshared/config/web.dart';
|
|
import 'package:pshared/data/mapper/account/account.dart';
|
|
import 'package:pshared/models/account/account.dart';
|
|
import 'package:pshared/service/authorization/circuit_breaker.dart';
|
|
import 'package:pshared/service/authorization/retry_helper.dart';
|
|
import 'package:pshared/service/authorization/storage.dart';
|
|
import 'package:pshared/service/authorization/token.dart';
|
|
import 'package:pshared/service/device_id.dart';
|
|
import 'package:pshared/utils/exception.dart';
|
|
import 'package:pshared/utils/http/requests.dart' as httpr;
|
|
|
|
|
|
/// AuthorizationService provides centralized authorization management
|
|
/// with token refresh, retry logic, and circuit breaker patterns
|
|
class AuthorizationService {
|
|
static final _logger = Logger('service.authorization.auth_service');
|
|
|
|
static Future<Account> login(String service, LoginData login) async {
|
|
_logger.fine('Logging in ${login.login} with ${login.locale} locale');
|
|
final deviceId = await DeviceIdManager.getDeviceId();
|
|
final response = await httpr.getPOSTResponse(
|
|
service,
|
|
'/login',
|
|
LoginRequest(login: login, deviceId: deviceId, clientId: Constants.clientId).toJson(),
|
|
);
|
|
|
|
return (await _completeLogin(response)).account.toDomain();
|
|
}
|
|
|
|
static Future<void> _updateAccessToken(AccountResponse response) async {
|
|
await AuthorizationStorage.updateToken(response.accessToken);
|
|
}
|
|
|
|
static Future<void> _updateTokens(LoginResponse response) async {
|
|
await _updateAccessToken(response);
|
|
return AuthorizationStorage.updateRefreshToken(response.refreshToken);
|
|
}
|
|
|
|
static Future<LoginResponse> _completeLogin(Map<String, dynamic> response) async {
|
|
final LoginResponse lr = LoginResponse.fromJson(response);
|
|
await _updateTokens(lr);
|
|
return lr;
|
|
}
|
|
|
|
static Future<Account> restore() async {
|
|
return (await TokenService.refreshAccessToken()).account.toDomain();
|
|
}
|
|
|
|
static Future<void> logout() async {
|
|
return AuthorizationStorage.removeTokens();
|
|
}
|
|
|
|
// Original AuthorizationService methods - keeping the interface unchanged
|
|
static Future<Map<String, dynamic>> getGETResponse(String service, String url) async {
|
|
final token = await TokenService.getAccessTokenSafe();
|
|
return httpr.getGETResponse(service, url, authToken: token);
|
|
}
|
|
|
|
static Future<Map<String, dynamic>> getPOSTResponse(String service, String url, Map<String, dynamic> body) async {
|
|
final token = await TokenService.getAccessTokenSafe();
|
|
return httpr.getPOSTResponse(service, url, body, authToken: token);
|
|
}
|
|
|
|
static Future<Map<String, dynamic>> getPUTResponse(String service, String url, Map<String, dynamic> body) async {
|
|
final token = await TokenService.getAccessTokenSafe();
|
|
return httpr.getPUTResponse(service, url, body, authToken: token);
|
|
}
|
|
|
|
static Future<Map<String, dynamic>> getPATCHResponse(String service, String url, Map<String, dynamic> body) async {
|
|
final token = await TokenService.getAccessTokenSafe();
|
|
return httpr.getPATCHResponse(service, url, body, authToken: token);
|
|
}
|
|
|
|
static Future<Map<String, dynamic>> getDELETEResponse(String service, String url, Map<String, dynamic> body) async {
|
|
final token = await TokenService.getAccessTokenSafe();
|
|
return httpr.getDELETEResponse(service, url, body, authToken: token);
|
|
}
|
|
|
|
static Future<String> getFileUploadResponseAuth(String service, String url, String fileName, String fileType, String mediaType, List<int> bytes) async {
|
|
final token = await TokenService.getAccessTokenSafe();
|
|
final res = await httpr.getFileUploadResponse(service, url, fileName, fileType, mediaType, bytes, authToken: token);
|
|
if (res == null) {
|
|
throw Exception('Upload failed');
|
|
}
|
|
return res.url;
|
|
}
|
|
|
|
static Future<bool> isAuthorizationStored() async => AuthorizationStorage.isAuthorizationStored();
|
|
|
|
/// Execute an operation with automatic token management and retry logic
|
|
static Future<T> executeWithAuth<T>(
|
|
Future<T> Function() operation,
|
|
String description, {
|
|
int? maxRetries,
|
|
}) async => AuthCircuitBreaker.execute(() async => RetryHelper.withExponentialBackoff(
|
|
operation,
|
|
maxRetries: maxRetries ?? 3,
|
|
initialDelay: Duration(milliseconds: 100),
|
|
maxDelay: Duration(seconds: 5),
|
|
shouldRetry: (error) => RetryHelper.isRetryableError(error),
|
|
));
|
|
|
|
|
|
/// Handle 401 unauthorized errors with automatic token recovery
|
|
static Future<T> handleUnauthorized<T>(
|
|
Future<T> Function() operation,
|
|
String description,
|
|
) async {
|
|
_logger.warning('Handling unauthorized error with token recovery: $description');
|
|
|
|
return executeWithAuth(
|
|
() async {
|
|
try {
|
|
// Attempt token recovery first
|
|
await TokenService.handleUnauthorized();
|
|
|
|
// Retry the original operation
|
|
return await operation();
|
|
} catch (e) {
|
|
_logger.severe('Token recovery failed', e);
|
|
throw AuthenticationFailedException('Token recovery failed', toException(e));
|
|
}
|
|
},
|
|
'unauthorized recovery: $description',
|
|
);
|
|
}
|
|
}
|