From 65c56f3630da66322aef3b197838b9de4b959f44 Mon Sep 17 00:00:00 2001 From: Krille Date: Fri, 23 Feb 2024 12:50:55 +0100 Subject: [PATCH] feat: Store accesstokenExpiresIn and call softlogout 5 minutes before --- lib/src/client.dart | 40 +++++++++++++++++++ lib/src/database/database_api.dart | 2 + .../database/hive_collections_database.dart | 16 ++++++++ lib/src/database/hive_database.dart | 6 +++ lib/src/database/matrix_sdk_database.dart | 14 +++++++ test/database_api_test.dart | 7 ++++ test/matrix_database_test.dart | 1 + 7 files changed, 86 insertions(+) diff --git a/lib/src/client.dart b/lib/src/client.dart index a8347ddc..4ba8ce9b 100644 --- a/lib/src/client.dart +++ b/lib/src/client.dart @@ -91,6 +91,8 @@ class Client extends MatrixApi { Future Function(Client client)? onSoftLogout; + DateTime? accessTokenExpiresAt; + // For CommandsClientExtension final Map Function(CommandArgs)> commands = {}; final Filter syncFilter; @@ -268,6 +270,7 @@ class Client extends MatrixApi { await database?.updateClient( homeserverUrl, tokenResponse.accessToken, + accessTokenExpiresAt, tokenResponse.refreshToken, userId, deviceId, @@ -540,8 +543,14 @@ class Client extends MatrixApi { throw Exception( 'Registered but token, device ID, user ID or homeserver is null.'); } + final expiresInMs = response.expiresInMs; + final tokenExpiresAt = expiresInMs == null + ? null + : DateTime.now().add(Duration(milliseconds: expiresInMs)); + await init( newToken: accessToken, + newTokenExpiresAt: tokenExpiresAt, newRefreshToken: response.refreshToken, newUserID: userId, newHomeserver: homeserver, @@ -604,8 +613,15 @@ class Client extends MatrixApi { if (homeserver_ == null) { throw Exception('Registered but homerserver is null.'); } + + final expiresInMs = response.expiresInMs; + final tokenExpiresAt = expiresInMs == null + ? null + : DateTime.now().add(Duration(milliseconds: expiresInMs)); + await init( newToken: accessToken, + newTokenExpiresAt: tokenExpiresAt, newRefreshToken: response.refreshToken, newUserID: userId, newHomeserver: homeserver_, @@ -1521,6 +1537,7 @@ class Client extends MatrixApi { /// `userDeviceKeysLoading` where it is necessary. Future init({ String? newToken, + DateTime? newTokenExpiresAt, String? newRefreshToken, Uri? newHomeserver, String? newUserID, @@ -1579,6 +1596,11 @@ class Client extends MatrixApi { _id = account['client_id']; homeserver = Uri.parse(account['homeserver_url']); accessToken = this.accessToken = account['token']; + final tokenExpiresAtMs = + int.tryParse(account.tryGet('token_expires_at') ?? ''); + accessTokenExpiresAt = tokenExpiresAtMs == null + ? null + : DateTime.fromMillisecondsSinceEpoch(tokenExpiresAtMs); userID = _userID = account['user_id']; _deviceID = account['device_id']; _deviceName = account['device_name']; @@ -1588,6 +1610,7 @@ class Client extends MatrixApi { } if (newToken != null) { accessToken = this.accessToken = newToken; + accessTokenExpiresAt = newTokenExpiresAt; homeserver = newHomeserver; userID = _userID = newUserID; _deviceID = newDeviceID; @@ -1595,6 +1618,7 @@ class Client extends MatrixApi { olmAccount = newOlmAccount; } else { accessToken = this.accessToken = newToken ?? accessToken; + accessTokenExpiresAt = newTokenExpiresAt ?? accessTokenExpiresAt; homeserver = newHomeserver ?? homeserver; userID = _userID = newUserID ?? userID; _deviceID = newDeviceID ?? _deviceID; @@ -1635,6 +1659,7 @@ class Client extends MatrixApi { await database.updateClient( homeserver.toString(), accessToken, + accessTokenExpiresAt, newRefreshToken, userID, _deviceID, @@ -1647,6 +1672,7 @@ class Client extends MatrixApi { clientName, homeserver.toString(), accessToken, + accessTokenExpiresAt, newRefreshToken, userID, _deviceID, @@ -1794,6 +1820,15 @@ class Client extends MatrixApi { Object? syncError; await _checkSyncFilter(); + // Call onSoftLogout 5 minutes before access token expires to prevent + // failing network requests. + final tokenExpiresAt = accessTokenExpiresAt; + if (onSoftLogout != null && + tokenExpiresAt != null && + tokenExpiresAt.difference(DateTime.now()) <= Duration(minutes: 5)) { + await onSoftLogout?.call(this); + } + // The timeout we send to the server for the sync loop. It says to the // server that we want to receive an empty sync response after this // amount of time if nothing happens. @@ -3169,10 +3204,15 @@ class Client extends MatrixApi { Logs().i('Found data in the legacy database!'); onMigration?.call(); _id = migrateClient['client_id']; + final tokenExpiresAtMs = + int.tryParse(migrateClient.tryGet('token_expires_at') ?? ''); await database.insertClient( clientName, migrateClient['homeserver_url'], migrateClient['token'], + tokenExpiresAtMs == null + ? null + : DateTime.fromMillisecondsSinceEpoch(tokenExpiresAtMs), migrateClient['refresh_token'], migrateClient['user_id'], migrateClient['device_id'], diff --git a/lib/src/database/database_api.dart b/lib/src/database/database_api.dart index f6d15054..b7e79c16 100644 --- a/lib/src/database/database_api.dart +++ b/lib/src/database/database_api.dart @@ -33,6 +33,7 @@ abstract class DatabaseApi { Future updateClient( String homeserverUrl, String token, + DateTime? tokenExpiresAt, String? refreshToken, String userId, String? deviceId, @@ -45,6 +46,7 @@ abstract class DatabaseApi { String name, String homeserverUrl, String token, + DateTime? tokenExpiresAt, String? refreshToken, String userId, String? deviceId, diff --git a/lib/src/database/hive_collections_database.dart b/lib/src/database/hive_collections_database.dart index 9582f40a..1577ac38 100644 --- a/lib/src/database/hive_collections_database.dart +++ b/lib/src/database/hive_collections_database.dart @@ -785,6 +785,7 @@ class HiveCollectionsDatabase extends DatabaseApi { String name, String homeserverUrl, String token, + DateTime? tokenExpiresAt, String? refreshToken, String userId, String? deviceId, @@ -800,6 +801,14 @@ class HiveCollectionsDatabase extends DatabaseApi { } else { await _clientBox.put('refresh_token', refreshToken); } + if (tokenExpiresAt == null) { + await _clientBox.delete('token_expires_at'); + } else { + await _clientBox.put( + 'token_expires_at', + tokenExpiresAt.millisecondsSinceEpoch.toString(), + ); + } if (deviceId == null) { await _clientBox.delete('device_id'); } else { @@ -1377,6 +1386,7 @@ class HiveCollectionsDatabase extends DatabaseApi { Future updateClient( String homeserverUrl, String token, + DateTime? tokenExpiresAt, String? refreshToken, String userId, String? deviceId, @@ -1387,6 +1397,12 @@ class HiveCollectionsDatabase extends DatabaseApi { await transaction(() async { await _clientBox.put('homeserver_url', homeserverUrl); await _clientBox.put('token', token); + if (tokenExpiresAt == null) { + await _clientBox.delete('token_expires_at'); + } else { + await _clientBox.put('token_expires_at', + tokenExpiresAt.millisecondsSinceEpoch.toString()); + } if (refreshToken == null) { await _clientBox.delete('refresh_token'); } else { diff --git a/lib/src/database/hive_database.dart b/lib/src/database/hive_database.dart index 89576c7e..8370de50 100644 --- a/lib/src/database/hive_database.dart +++ b/lib/src/database/hive_database.dart @@ -750,6 +750,7 @@ class FamedlySdkHiveDatabase extends DatabaseApi with ZoneTransactionMixin { String name, String homeserverUrl, String token, + DateTime? tokenExpiresAt, String? refreshToken, String userId, String? deviceId, @@ -758,6 +759,8 @@ class FamedlySdkHiveDatabase extends DatabaseApi with ZoneTransactionMixin { String? olmAccount) async { await _clientBox.put('homeserver_url', homeserverUrl); await _clientBox.put('token', token); + await _clientBox.put( + 'token_expires_at', tokenExpiresAt?.millisecondsSinceEpoch.toString()); await _clientBox.put('refresh_token', refreshToken); await _clientBox.put('user_id', userId); await _clientBox.put('device_id', deviceId); @@ -1316,6 +1319,7 @@ class FamedlySdkHiveDatabase extends DatabaseApi with ZoneTransactionMixin { Future updateClient( String homeserverUrl, String token, + DateTime? tokenExpiresAt, String? refreshToken, String userId, String? deviceId, @@ -1325,6 +1329,8 @@ class FamedlySdkHiveDatabase extends DatabaseApi with ZoneTransactionMixin { ) async { await _clientBox.put('homeserver_url', homeserverUrl); await _clientBox.put('token', token); + await _clientBox.put( + 'token_expires_at', tokenExpiresAt?.millisecondsSinceEpoch.toString()); await _clientBox.put('refresh_token', refreshToken); await _clientBox.put('user_id', userId); await _clientBox.put('device_id', deviceId); diff --git a/lib/src/database/matrix_sdk_database.dart b/lib/src/database/matrix_sdk_database.dart index 82a7dca3..ee0bc961 100644 --- a/lib/src/database/matrix_sdk_database.dart +++ b/lib/src/database/matrix_sdk_database.dart @@ -727,6 +727,7 @@ class MatrixSdkDatabase extends DatabaseApi { String name, String homeserverUrl, String token, + DateTime? tokenExpiresAt, String? refreshToken, String userId, String? deviceId, @@ -736,6 +737,12 @@ class MatrixSdkDatabase extends DatabaseApi { await transaction(() async { await _clientBox.put('homeserver_url', homeserverUrl); await _clientBox.put('token', token); + if (tokenExpiresAt == null) { + await _clientBox.delete('token_expires_at'); + } else { + await _clientBox.put('token_expires_at', + tokenExpiresAt.millisecondsSinceEpoch.toString()); + } if (refreshToken == null) { await _clientBox.delete('refresh_token'); } else { @@ -1349,6 +1356,7 @@ class MatrixSdkDatabase extends DatabaseApi { Future updateClient( String homeserverUrl, String token, + DateTime? tokenExpiresAt, String? refreshToken, String userId, String? deviceId, @@ -1359,6 +1367,12 @@ class MatrixSdkDatabase extends DatabaseApi { await transaction(() async { await _clientBox.put('homeserver_url', homeserverUrl); await _clientBox.put('token', token); + if (tokenExpiresAt == null) { + await _clientBox.delete('token_expires_at'); + } else { + await _clientBox.put('token_expires_at', + tokenExpiresAt.millisecondsSinceEpoch.toString()); + } if (refreshToken == null) { await _clientBox.delete('refresh_token'); } else { diff --git a/test/database_api_test.dart b/test/database_api_test.dart index d043912f..c2a48566 100644 --- a/test/database_api_test.dart +++ b/test/database_api_test.dart @@ -129,10 +129,12 @@ void main() { await database.getClient('name'); }); test('insertClient', () async { + final now = DateTime.now(); await database.insertClient( 'name', 'homeserverUrl', 'token', + now, 'refresh_token', 'userId', 'deviceId', @@ -143,11 +145,16 @@ void main() { final client = await database.getClient('name'); expect(client?['token'], 'token'); + expect( + client?['token_expires_at'], + now.millisecondsSinceEpoch.toString(), + ); }); test('updateClient', () async { await database.updateClient( 'homeserverUrl', 'token_different', + DateTime.now(), 'refresh_token', 'userId', 'deviceId', diff --git a/test/matrix_database_test.dart b/test/matrix_database_test.dart index cc8cb833..95a58d8b 100644 --- a/test/matrix_database_test.dart +++ b/test/matrix_database_test.dart @@ -33,6 +33,7 @@ void main() { 'https://example.org', 'blubb', null, + null, '@test:example.org', null, null,