From d868b9a9f7d7ded2eba85c2b33316177bed83e72 Mon Sep 17 00:00:00 2001 From: JC Brand Date: Mon, 29 Apr 2019 17:29:07 +0200 Subject: [PATCH] Continuously retry to fetch login credentials --- CHANGES.md | 1 + dist/converse.js | 52 ++++++++++++---------- docs/source/configuration.rst | 5 ++- src/headless/converse-core.js | 60 ++++++++++++++------------ src/headless/dist/converse-headless.js | 52 ++++++++++++---------- 5 files changed, 96 insertions(+), 74 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index fb1b2dd6c..e1ccfa42e 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -9,6 +9,7 @@ - Don't restore a BOSH session without knowing the JID - In the `/help` menu, only show allowed commands - Message deduplication bugfixes and improvements +- Continuously retry (in 2s intervals) to fetch login credentials (via [credentials_url](https://conversejs.org/docs/html/configuration.html#credentials-url)) in case of failure - #1296: `embedded` view mode shows `chatbox-navback` arrow in header - #1532: Converse reloads on enter pressed in the filter box diff --git a/dist/converse.js b/dist/converse.js index 9fa166aa9..92c1eff5a 100644 --- a/dist/converse.js +++ b/dist/converse.js @@ -63914,12 +63914,13 @@ async function finishInitialization() { } function fetchLoginCredentials() { - return new es6_promise_dist_es6_promise_auto__WEBPACK_IMPORTED_MODULE_3___default.a((resolve, reject) => { + let wait = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0; + return new es6_promise_dist_es6_promise_auto__WEBPACK_IMPORTED_MODULE_3___default.a(_lodash_noconflict__WEBPACK_IMPORTED_MODULE_4___default.a.debounce((resolve, reject) => { const xhr = new XMLHttpRequest(); xhr.open('GET', _converse.credentials_url, true); xhr.setRequestHeader('Accept', "application/json, text/javascript"); - xhr.onload = function () { + xhr.onload = () => { if (xhr.status >= 200 && xhr.status < 400) { const data = JSON.parse(xhr.responseText); resolve({ @@ -63927,20 +63928,34 @@ function fetchLoginCredentials() { 'password': data.password }); } else { - xhr.onerror({}); + reject(new Error(`${xhr.status}: ${xhr.responseText}`)); } }; - xhr.onerror = function () { - delete _converse.connection; - - _converse.api.trigger('noResumeableSession', this); - - reject(new Error(xhr.responseText)); - }; - + xhr.onerror = reject; xhr.send(); - }); + }, wait)); +} + +async function getLoginCredentials() { + let credentials; + let wait = 0; + + while (!credentials) { + try { + credentials = await fetchLoginCredentials(wait); // eslint-disable-line no-await-in-loop + } catch (e) { + _converse.log("Could not fetch login credentials", strophe_js__WEBPACK_IMPORTED_MODULE_0__["Strophe"].LogLevel.ERROR); + + _converse.log(e, strophe_js__WEBPACK_IMPORTED_MODULE_0__["Strophe"].LogLevel.ERROR); + } // If unsuccessful, we wait 2 seconds between subsequent attempts to + // fetch the credentials. + + + wait = 2000; + } + + return credentials; } function unregisterGlobalEventHandlers() { @@ -64769,17 +64784,8 @@ _converse.initialize = async function (settings, callback) { this.autoLogin(credentials); } else if (this.auto_login) { if (this.credentials_url) { - let data = {}; - - try { - data = await fetchLoginCredentials(); - } catch (e) { - _converse.log("Could not fetch login credentials", strophe_js__WEBPACK_IMPORTED_MODULE_0__["Strophe"].LogLevel.ERROR); - - _converse.log(e, strophe_js__WEBPACK_IMPORTED_MODULE_0__["Strophe"].LogLevel.ERROR); - } finally { - this.autoLogin(data); - } + const data = await getLoginCredentials(); + this.autoLogin(data); } else if (!this.jid) { throw new Error("attemptNonPreboundSession: If you use auto_login, " + "you also need to give either a jid value (and if " + "applicable a password) or you need to pass in a URL " + "from where the username and password can be fetched " + "(via credentials_url)."); } else { diff --git a/docs/source/configuration.rst b/docs/source/configuration.rst index 85dc45d26..aed1368ba 100644 --- a/docs/source/configuration.rst +++ b/docs/source/configuration.rst @@ -563,10 +563,13 @@ It allows you to specify a URL which Converse will call when it needs to get the username and password (or authentication token) which Converse will use to automatically log the user in. -If ``auto_reconnect`` is also set to true, then Converse will automatically +If ``auto_reconnect`` is also set to ``true``, then Converse will automatically fetch new credentials from the ``credentials_url`` whenever the connection or session drops, and then attempt to reconnect and establish a new session. +If the request to the ``credentials_url`` URL fails for whatever reason, +Converse will continuously retry to fetch the credentials every 2 seconds. + The server behind ``credentials_url`` should return a JSON encoded object:: { diff --git a/src/headless/converse-core.js b/src/headless/converse-core.js index 03a48ede5..c95350908 100644 --- a/src/headless/converse-core.js +++ b/src/headless/converse-core.js @@ -483,29 +483,42 @@ async function finishInitialization () { } } -function fetchLoginCredentials () { - return new Promise((resolve, reject) => { +function fetchLoginCredentials (wait=0) { + return new Promise(_.debounce((resolve, reject) => { const xhr = new XMLHttpRequest(); xhr.open('GET', _converse.credentials_url, true); xhr.setRequestHeader('Accept', "application/json, text/javascript"); - xhr.onload = function() { - if (xhr.status >= 200 && xhr.status < 400) { - const data = JSON.parse(xhr.responseText); - resolve({ - 'jid': data.jid, - 'password': data.password - }); - } else { - xhr.onerror({}); - } - }; - xhr.onerror = function () { - delete _converse.connection; - _converse.api.trigger('noResumeableSession', this); - reject(new Error(xhr.responseText)); + xhr.onload = () => { + if (xhr.status >= 200 && xhr.status < 400) { + const data = JSON.parse(xhr.responseText); + resolve({ + 'jid': data.jid, + 'password': data.password + }); + } else { + reject(new Error(`${xhr.status}: ${xhr.responseText}`)); + } }; + xhr.onerror = reject; xhr.send(); - }); + }, wait)); +} + +async function getLoginCredentials () { + let credentials; + let wait = 0; + while (!credentials) { + try { + credentials = await fetchLoginCredentials(wait); // eslint-disable-line no-await-in-loop + } catch (e) { + _converse.log("Could not fetch login credentials", Strophe.LogLevel.ERROR); + _converse.log(e, Strophe.LogLevel.ERROR); + } + // If unsuccessful, we wait 2 seconds between subsequent attempts to + // fetch the credentials. + wait = 2000; + } + return credentials; } @@ -1244,15 +1257,8 @@ _converse.initialize = async function (settings, callback) { this.autoLogin(credentials); } else if (this.auto_login) { if (this.credentials_url) { - let data = {}; - try { - data = await fetchLoginCredentials(); - } catch (e) { - _converse.log("Could not fetch login credentials", Strophe.LogLevel.ERROR); - _converse.log(e, Strophe.LogLevel.ERROR); - } finally { - this.autoLogin(data); - } + const data = await getLoginCredentials(); + this.autoLogin(data); } else if (!this.jid) { throw new Error( "attemptNonPreboundSession: If you use auto_login, "+ diff --git a/src/headless/dist/converse-headless.js b/src/headless/dist/converse-headless.js index 3a5f7e1e4..eef8a6998 100644 --- a/src/headless/dist/converse-headless.js +++ b/src/headless/dist/converse-headless.js @@ -42160,12 +42160,13 @@ async function finishInitialization() { } function fetchLoginCredentials() { - return new es6_promise_dist_es6_promise_auto__WEBPACK_IMPORTED_MODULE_3___default.a((resolve, reject) => { + let wait = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0; + return new es6_promise_dist_es6_promise_auto__WEBPACK_IMPORTED_MODULE_3___default.a(_lodash_noconflict__WEBPACK_IMPORTED_MODULE_4___default.a.debounce((resolve, reject) => { const xhr = new XMLHttpRequest(); xhr.open('GET', _converse.credentials_url, true); xhr.setRequestHeader('Accept', "application/json, text/javascript"); - xhr.onload = function () { + xhr.onload = () => { if (xhr.status >= 200 && xhr.status < 400) { const data = JSON.parse(xhr.responseText); resolve({ @@ -42173,20 +42174,34 @@ function fetchLoginCredentials() { 'password': data.password }); } else { - xhr.onerror({}); + reject(new Error(`${xhr.status}: ${xhr.responseText}`)); } }; - xhr.onerror = function () { - delete _converse.connection; - - _converse.api.trigger('noResumeableSession', this); - - reject(new Error(xhr.responseText)); - }; - + xhr.onerror = reject; xhr.send(); - }); + }, wait)); +} + +async function getLoginCredentials() { + let credentials; + let wait = 0; + + while (!credentials) { + try { + credentials = await fetchLoginCredentials(wait); // eslint-disable-line no-await-in-loop + } catch (e) { + _converse.log("Could not fetch login credentials", strophe_js__WEBPACK_IMPORTED_MODULE_0__["Strophe"].LogLevel.ERROR); + + _converse.log(e, strophe_js__WEBPACK_IMPORTED_MODULE_0__["Strophe"].LogLevel.ERROR); + } // If unsuccessful, we wait 2 seconds between subsequent attempts to + // fetch the credentials. + + + wait = 2000; + } + + return credentials; } function unregisterGlobalEventHandlers() { @@ -43015,17 +43030,8 @@ _converse.initialize = async function (settings, callback) { this.autoLogin(credentials); } else if (this.auto_login) { if (this.credentials_url) { - let data = {}; - - try { - data = await fetchLoginCredentials(); - } catch (e) { - _converse.log("Could not fetch login credentials", strophe_js__WEBPACK_IMPORTED_MODULE_0__["Strophe"].LogLevel.ERROR); - - _converse.log(e, strophe_js__WEBPACK_IMPORTED_MODULE_0__["Strophe"].LogLevel.ERROR); - } finally { - this.autoLogin(data); - } + const data = await getLoginCredentials(); + this.autoLogin(data); } else if (!this.jid) { throw new Error("attemptNonPreboundSession: If you use auto_login, " + "you also need to give either a jid value (and if " + "applicable a password) or you need to pass in a URL " + "from where the username and password can be fetched " + "(via credentials_url)."); } else {