Fix: handle incorrect device time during code generation
This commit is contained in:
@@ -73,7 +73,10 @@ class AuthenticatorGateway {
|
||||
);
|
||||
}
|
||||
|
||||
Future<List<AuthEntity>> getDiff(int sinceTime, {int limit = 500}) async {
|
||||
Future<(List<AuthEntity>, int?)> getDiff(
|
||||
int sinceTime, {
|
||||
int limit = 500,
|
||||
}) async {
|
||||
try {
|
||||
final response = await _enteDio.get(
|
||||
"/authenticator/entity/diff",
|
||||
@@ -84,11 +87,12 @@ class AuthenticatorGateway {
|
||||
);
|
||||
final List<AuthEntity> authEntities = <AuthEntity>[];
|
||||
final diff = response.data["diff"] as List;
|
||||
final int? unixTimeInMicroSeconds = response.data["timestamp"] as int?;
|
||||
for (var entry in diff) {
|
||||
final AuthEntity entity = AuthEntity.fromMap(entry);
|
||||
authEntities.add(entity);
|
||||
}
|
||||
return authEntities;
|
||||
return (authEntities, unixTimeInMicroSeconds);
|
||||
} catch (e) {
|
||||
if (e is DioException && e.response?.statusCode == 401) {
|
||||
throw UnauthorizedError();
|
||||
|
||||
@@ -13,6 +13,7 @@ import 'package:ente_auth/models/authenticator/auth_entity.dart';
|
||||
import 'package:ente_auth/models/authenticator/auth_key.dart';
|
||||
import 'package:ente_auth/models/authenticator/entity_result.dart';
|
||||
import 'package:ente_auth/models/authenticator/local_auth_entity.dart';
|
||||
import 'package:ente_auth/services/preference_service.dart';
|
||||
import 'package:ente_auth/store/authenticator_db.dart';
|
||||
import 'package:ente_auth/store/offline_authenticator_db.dart';
|
||||
import 'package:ente_crypto_dart/ente_crypto_dart.dart';
|
||||
@@ -194,8 +195,13 @@ class AuthenticatorService {
|
||||
final int lastSyncTime = _prefs.getInt(_lastEntitySyncTime) ?? 0;
|
||||
_logger.info("Current sync is $lastSyncTime");
|
||||
const int fetchLimit = 500;
|
||||
final List<AuthEntity> result =
|
||||
late final List<AuthEntity> result;
|
||||
late final int? epochTimeInMicroseconds;
|
||||
(result, epochTimeInMicroseconds) =
|
||||
await _gateway.getDiff(lastSyncTime, limit: fetchLimit);
|
||||
PreferenceService.instance
|
||||
.computeAndStoreTimeOffset(epochTimeInMicroseconds);
|
||||
|
||||
_logger.info("${result.length} entries fetched from remote");
|
||||
if (result.isEmpty) {
|
||||
return;
|
||||
|
||||
@@ -18,6 +18,7 @@ class PreferenceService {
|
||||
late final SharedPreferences _prefs;
|
||||
|
||||
static const kHasShownCoachMarkKey = "has_shown_coach_mark_v2";
|
||||
static const kLocalTimeOffsetKey = "local_time_offset";
|
||||
static const kShouldShowLargeIconsKey = "should_show_large_icons";
|
||||
static const kShouldHideCodesKey = "should_hide_codes";
|
||||
static const kShouldAutoFocusOnSearchBar = "should_auto_focus_on_search_bar";
|
||||
@@ -114,4 +115,24 @@ class PreferenceService {
|
||||
return installedTimeinMillis;
|
||||
}
|
||||
}
|
||||
|
||||
// localEpochOffsetInMilliSecond returns the local epoch offset in milliseconds.
|
||||
// This is used to adjust the time for TOTP calculations when device local time is not in sync with actual time.
|
||||
int timeOffsetInMilliSeconds() {
|
||||
return _prefs.getInt(kLocalTimeOffsetKey) ?? 0;
|
||||
}
|
||||
|
||||
void computeAndStoreTimeOffset(
|
||||
int? epochTimeInMicroseconds,
|
||||
) {
|
||||
if (epochTimeInMicroseconds == null) {
|
||||
_prefs.remove(kLocalTimeOffsetKey);
|
||||
return;
|
||||
}
|
||||
int serverEpochTimeInMilliSecond = epochTimeInMicroseconds ~/ 1000;
|
||||
int localEpochTimeInMilliSecond = DateTime.now().millisecondsSinceEpoch;
|
||||
int localEpochOffset =
|
||||
serverEpochTimeInMilliSecond - localEpochTimeInMilliSecond;
|
||||
_prefs.setInt(kLocalTimeOffsetKey, localEpochOffset);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,14 @@
|
||||
import 'package:ente_auth/models/code.dart';
|
||||
import 'package:ente_auth/services/preference_service.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:otp/otp.dart' as otp;
|
||||
import 'package:steam_totp/steam_totp.dart';
|
||||
|
||||
int millisecondsSinceEpoch() {
|
||||
return DateTime.now().millisecondsSinceEpoch +
|
||||
PreferenceService.instance.timeOffsetInMilliSeconds();
|
||||
}
|
||||
|
||||
String getOTP(Code code) {
|
||||
if (code.type == Type.steam || code.issuer.toLowerCase() == 'steam') {
|
||||
return _getSteamCode(code);
|
||||
@@ -12,7 +18,7 @@ String getOTP(Code code) {
|
||||
}
|
||||
return otp.OTP.generateTOTPCodeString(
|
||||
getSanitizedSecret(code.secret),
|
||||
DateTime.now().millisecondsSinceEpoch,
|
||||
millisecondsSinceEpoch(),
|
||||
length: code.digits,
|
||||
interval: code.period,
|
||||
algorithm: _getAlgorithm(code),
|
||||
@@ -34,7 +40,7 @@ String _getSteamCode(Code code, [bool isNext = false]) {
|
||||
final SteamTOTP steamtotp = SteamTOTP(secret: code.secret);
|
||||
|
||||
return steamtotp.generate(
|
||||
DateTime.now().millisecondsSinceEpoch ~/ 1000 + (isNext ? code.period : 0),
|
||||
millisecondsSinceEpoch() ~/ 1000 + (isNext ? code.period : 0),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -44,7 +50,7 @@ String getNextTotp(Code code) {
|
||||
}
|
||||
return otp.OTP.generateTOTPCodeString(
|
||||
getSanitizedSecret(code.secret),
|
||||
DateTime.now().millisecondsSinceEpoch + code.period * 1000,
|
||||
millisecondsSinceEpoch() + code.period * 1000,
|
||||
length: code.digits,
|
||||
interval: code.period,
|
||||
algorithm: _getAlgorithm(code),
|
||||
@@ -56,9 +62,7 @@ String getNextTotp(Code code) {
|
||||
// It returns the start time and a list of future codes.
|
||||
(int, List<String>) generateFutureTotpCodes(Code code, int count) {
|
||||
final int startTime =
|
||||
((DateTime.now().millisecondsSinceEpoch ~/ 1000) ~/ code.period) *
|
||||
code.period *
|
||||
1000;
|
||||
((millisecondsSinceEpoch() ~/ 1000) ~/ code.period) * code.period * 1000;
|
||||
final String secret = getSanitizedSecret(code.secret);
|
||||
final List<String> codes = [];
|
||||
if (code.type == Type.steam || code.issuer.toLowerCase() == 'steam') {
|
||||
|
||||
Reference in New Issue
Block a user