fix: harden manual cloud upload authentication

This commit is contained in:
bponders12-cell 2026-09-01 22:31:36 -04:00
parent 825f327549
commit d8abc78a40
1 changed files with 31 additions and 3 deletions

View File

@ -37,14 +37,24 @@ class FirebaseSyncProvider implements SyncProvider {
}
@override
Future<bool> requiresAuthentication() async => _authService.currentUser == null;
Future<bool> requiresAuthentication() async {
final user = _authService.currentUser;
return user == null || user.isAnonymous;
}
@override
Future<SyncResult> uploadRide(SyncItem item) async {
if (!await isAvailable()) return SyncResult.temporaryFailure('Firebase services are unavailable or unconfigured. Ride remains safely stored locally.');
final user = _authService.currentUser;
if (user == null || user.isAnonymous) return SyncResult.authRequired(message: 'Sign in to MotorCloud is required for cloud backup.');
final idToken = await _authService.getIdToken();
// Refresh the Firebase ID token before a manual upload. This prevents a
// recently completed sign-in from being rejected because the cached token
// has not yet propagated through the app's auth abstraction.
var idToken = await _authService.getIdToken();
if (idToken == null || idToken.isEmpty) {
idToken = await _authService.getIdToken(true);
}
if (idToken == null || idToken.isEmpty) return SyncResult.authRequired(message: 'Valid Firebase authentication token required for cloud sync.');
try {
@ -59,13 +69,31 @@ class FirebaseSyncProvider implements SyncProvider {
if (zipBytes == null || zipBytes.isEmpty) return SyncResult.permanentFailure('Failed generating compressed telemetry bundle for ride #${item.localRideId}.');
final sha256Hash = sha256.convert(zipBytes).toString();
final r2Result = await _r2UploadService.uploadTelemetryBundle(
var r2Result = await _r2UploadService.uploadTelemetryBundle(
idToken: idToken,
remoteRideId: remoteRideId,
zipBytes: zipBytes,
sha256Hash: sha256Hash,
appVersion: appIdentity.semanticVersion,
);
// If the gateway rejects the first token as unauthorized, refresh once
// and retry. This specifically handles token rotation/expiry without
// turning a valid signed-in session into a false authentication error.
if (!r2Result.success && r2Result.requiresAuth) {
final refreshedToken = await _authService.getIdToken(true);
if (refreshedToken != null && refreshedToken.isNotEmpty && refreshedToken != idToken) {
idToken = refreshedToken;
r2Result = await _r2UploadService.uploadTelemetryBundle(
idToken: idToken,
remoteRideId: remoteRideId,
zipBytes: zipBytes,
sha256Hash: sha256Hash,
appVersion: appIdentity.semanticVersion,
);
}
}
if (!r2Result.success) {
if (r2Result.requiresAuth) return SyncResult.authRequired(message: r2Result.errorMessage ?? 'R2 Gateway authentication required');
if (r2Result.isRetryable) return SyncResult.temporaryFailure(r2Result.errorMessage ?? 'Temporary R2 upload error');