Don't schedule multiple token refreshes for the same user/realm (#2071)

* Don't schedule multiple token refreshes for the same user/realm

* Avoid setting a const property
This commit is contained in:
Nikola Irinchev 2018-10-15 14:20:19 +02:00 committed by GitHub
parent cf1632eb0f
commit 10f72c5444
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 37 additions and 12 deletions

View File

@ -1,11 +1,11 @@
x.x.x Release notes (yyyy-MM-dd)
=============================================================
### Enhancements
* None.
* Improved the proactive token refresh mechanism to make several attempts to refresh the token before it expires and to also ensure that there is only one ongoing refresh timer for a combination of user and realm path. Previously it was possible to end up in a situation where many redundant refreshes were scheduled for the same Realm. ([#2071](https://github.com/realm/realm-js/pull/2071), since v1.0.2)
### Fixes
* <How to hit and notice issue? what was the impact?> ([#????](https://github.com/realm/realm-js/issues/????), since v?.?.?)
* None.
* Fixed the signature of `user.logout` to return a `Promise<void>` rather than `void`. It has always done asynchronous work, but previously, it was impossible to be notified that the call has completed. Since that is now possible, the superfluous "User is logged out" message printed in the console upon logout has been removed. ([#2071](https://github.com/realm/realm-js/pull/2071), since v2.3.0)
### Compatibility
* Realm Object Server: 3.11.0 or later.

View File

@ -524,7 +524,9 @@ class User {
serialize() {}
/**
* Logs out the user from the Realm Object Server.
* Logs out the user from the Realm Object Server. Once the Object Server has confirmed the logout the user
* credentials will be deleted from this device.
* @return {Promise<void>} A promise which is resolved when the user has logged out both locally and on the server.
*/
logout() {}

2
lib/index.d.ts vendored
View File

@ -337,7 +337,7 @@ declare namespace Realm.Sync {
createConfiguration(config?: Realm.PartialConfiguration): Realm.Configuration
serialize(): SerializedUser;
logout(): void;
logout(): Promise<void>;
openManagementRealm(): Realm;
retrieveAccount(provider: string, username: string): Promise<Account>;

View File

@ -25,6 +25,10 @@ const merge = require('deepmerge');
const require_method = require;
const URL = require('url-parse');
const refreshTimers = {};
const retryInterval = 5 * 1000;
const refreshBuffer = 20 * 1000;
function node_require(module) {
return require_method(module);
}
@ -107,9 +111,20 @@ function append_url(server, path) {
}
function scheduleAccessTokenRefresh(user, localRealmPath, realmUrl, expirationDate) {
const refreshBuffer = 10 * 1000;
const timeout = expirationDate - Date.now() - refreshBuffer;
setTimeout(() => refreshAccessToken(user, localRealmPath, realmUrl), timeout);
let userTimers = refreshTimers[user.identity];
if (!userTimers) {
refreshTimers[user.identity] = userTimers = {};
}
// We assume that access tokens have ~ the same expiration time, so if someone already
// scheduled a refresh, it's likely to complete before the one we would have scheduled
if (!userTimers[localRealmPath]) {
const timeout = expirationDate - Date.now() - refreshBuffer;
userTimers[localRealmPath] = setTimeout(() => {
delete userTimers[localRealmPath];
refreshAccessToken(user, localRealmPath, realmUrl);
}, timeout);
}
}
function print_error() {
@ -177,7 +192,7 @@ function refreshAdminToken(user, localRealmPath, realmUrl) {
})
.catch((e) => {
print_error(e);
setTimeout(() => refreshAccessToken(user, localRealmPath, realmUrl), 10 * 1000);
setTimeout(() => refreshAccessToken(user, localRealmPath, realmUrl), retryInterval);
});
}
@ -239,7 +254,7 @@ function refreshAccessToken(user, localRealmPath, realmUrl) {
.catch((e) => {
print_error(e);
// in case something lower in the HTTP stack breaks, try again in 10 seconds
setTimeout(() => refreshAccessToken(user, localRealmPath, realmUrl), 10 * 1000);
setTimeout(() => refreshAccessToken(user, localRealmPath, realmUrl), retryInterval);
})
}
@ -498,6 +513,15 @@ const staticMethods = {
const instanceMethods = {
logout() {
this._logout();
const userTimers = refreshTimers[this.identity];
if (userTimers) {
Object.keys(userTimers).forEach((key) => {
clearTimeout(userTimers[key]);
});
delete refreshTimers[this.identity];
}
const url = url_parse(this.server);
url.set('pathname', '/auth/revoke');
const headers = {
@ -513,9 +537,8 @@ const instanceMethods = {
open_timeout: 5000
};
performFetch(url.href, options)
.then(() => console.log('User is logged out'))
.catch((e) => print_error(e));
return performFetch(url.href, options)
.catch((e) => print_error('An error occurred while logging out a user', e));
},
serialize() {
return {