Cleanup auth flow
This commit is contained in:
parent
fc1c9568e2
commit
a1f183f216
@ -85,7 +85,7 @@ export default {
|
|||||||
|
|
||||||
storage.set("user", values.user);
|
storage.set("user", values.user);
|
||||||
|
|
||||||
socket.emit("auth", values);
|
socket.emit("auth:perform", values);
|
||||||
},
|
},
|
||||||
getStoredUser() {
|
getStoredUser() {
|
||||||
return storage.get("user");
|
return storage.get("user");
|
||||||
|
@ -53,13 +53,13 @@
|
|||||||
<div id="loading-status-container">
|
<div id="loading-status-container">
|
||||||
<img src="img/logo-vertical-transparent-bg.svg" class="logo" alt="The Lounge" width="256" height="170">
|
<img src="img/logo-vertical-transparent-bg.svg" class="logo" alt="The Lounge" width="256" height="170">
|
||||||
<img src="img/logo-vertical-transparent-bg-inverted.svg" class="logo-inverted" alt="The Lounge" width="256" height="170">
|
<img src="img/logo-vertical-transparent-bg-inverted.svg" class="logo-inverted" alt="The Lounge" width="256" height="170">
|
||||||
<p id="loading-page-message"><a href="https://enable-javascript.com/" target="_blank" rel="noopener">Your JavaScript must be enabled.</a></p>
|
<p id="loading-page-message">The Lounge requires a modern browser with JavaScript enabled.</p>
|
||||||
</div>
|
</div>
|
||||||
<div id="loading-reload-container">
|
<div id="loading-reload-container">
|
||||||
<p id="loading-slow">This is taking longer than it should, there might be connectivity issues.</p>
|
<p id="loading-slow">This is taking longer than it should, there might be connectivity issues.</p>
|
||||||
<button id="loading-reload" class="btn">Reload page</button>
|
<button id="loading-reload" class="btn">Reload page</button>
|
||||||
</div>
|
</div>
|
||||||
<script async src="js/loading-error-handlers.js?v=<%- cacheBust %>"></script>
|
<script src="js/loading-error-handlers.js?v=<%- cacheBust %>"></script>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="viewport"></div>
|
<div id="viewport"></div>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/* eslint strict: 0, no-var: 0 */
|
/* eslint strict: 0 */
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -9,71 +9,66 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
(function() {
|
(function() {
|
||||||
var msg = document.getElementById("loading-page-message");
|
const msg = document.getElementById("loading-page-message");
|
||||||
|
|
||||||
if (msg) {
|
|
||||||
msg.textContent = "Loading the app…";
|
msg.textContent = "Loading the app…";
|
||||||
|
|
||||||
document.getElementById("loading-reload").addEventListener("click", function() {
|
document
|
||||||
location.reload(true);
|
.getElementById("loading-reload")
|
||||||
});
|
.addEventListener("click", () => location.reload(true));
|
||||||
}
|
|
||||||
|
|
||||||
var displayReload = function displayReload() {
|
const displayReload = () => {
|
||||||
var loadingReload = document.getElementById("loading-reload");
|
const loadingReload = document.getElementById("loading-reload");
|
||||||
|
|
||||||
if (loadingReload) {
|
if (loadingReload) {
|
||||||
loadingReload.style.visibility = "visible";
|
loadingReload.style.visibility = "visible";
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var loadingSlowTimeout = setTimeout(function() {
|
const loadingSlowTimeout = setTimeout(() => {
|
||||||
var loadingSlow = document.getElementById("loading-slow");
|
const loadingSlow = document.getElementById("loading-slow");
|
||||||
|
|
||||||
// The parent element, #loading, is being removed when the app is loaded.
|
|
||||||
// Since the timer is not cancelled, `loadingSlow` can be not found after
|
|
||||||
// 5s. Wrap everything in this block to make sure nothing happens if the
|
|
||||||
// element does not exist (i.e. page has loaded).
|
|
||||||
if (loadingSlow) {
|
|
||||||
loadingSlow.style.visibility = "visible";
|
loadingSlow.style.visibility = "visible";
|
||||||
displayReload();
|
displayReload();
|
||||||
}
|
|
||||||
}, 5000);
|
}, 5000);
|
||||||
|
|
||||||
window.g_LoungeErrorHandler = function LoungeErrorHandler(e) {
|
const errorHandler = (e) => {
|
||||||
var message = document.getElementById("loading-page-message");
|
msg.textContent = "An error has occurred that prevented the client from loading correctly.";
|
||||||
message.textContent =
|
|
||||||
"An error has occurred that prevented the client from loading correctly.";
|
|
||||||
|
|
||||||
var summary = document.createElement("summary");
|
const summary = document.createElement("summary");
|
||||||
summary.textContent = "More details";
|
summary.textContent = "More details";
|
||||||
|
|
||||||
var data = document.createElement("pre");
|
const data = document.createElement("pre");
|
||||||
data.textContent = e.message; // e is an ErrorEvent
|
data.textContent = e.message; // e is an ErrorEvent
|
||||||
|
|
||||||
var info = document.createElement("p");
|
const info = document.createElement("p");
|
||||||
info.textContent = "Open the developer tools of your browser for more information.";
|
info.textContent = "Open the developer tools of your browser for more information.";
|
||||||
|
|
||||||
var details = document.createElement("details");
|
const details = document.createElement("details");
|
||||||
details.appendChild(summary);
|
details.appendChild(summary);
|
||||||
details.appendChild(data);
|
details.appendChild(data);
|
||||||
details.appendChild(info);
|
details.appendChild(info);
|
||||||
message.parentNode.insertBefore(details, message.nextSibling);
|
msg.parentNode.insertBefore(details, msg.nextSibling);
|
||||||
|
|
||||||
window.clearTimeout(loadingSlowTimeout);
|
window.clearTimeout(loadingSlowTimeout);
|
||||||
displayReload();
|
displayReload();
|
||||||
};
|
};
|
||||||
|
|
||||||
window.addEventListener("error", window.g_LoungeErrorHandler);
|
window.addEventListener("error", errorHandler);
|
||||||
|
|
||||||
|
window.g_TheLoungeRemoveLoading = () => {
|
||||||
|
delete window.g_TheLoungeRemoveLoading;
|
||||||
|
window.clearTimeout(loadingSlowTimeout);
|
||||||
|
window.removeEventListener("error", errorHandler);
|
||||||
|
document.getElementById("loading").remove();
|
||||||
|
};
|
||||||
|
|
||||||
// Trigger early service worker registration
|
// Trigger early service worker registration
|
||||||
if ("serviceWorker" in navigator) {
|
if ("serviceWorker" in navigator) {
|
||||||
navigator.serviceWorker.register("service-worker.js");
|
navigator.serviceWorker.register("service-worker.js");
|
||||||
|
|
||||||
// Handler for messages coming from the service worker
|
// Handler for messages coming from the service worker
|
||||||
var messageHandler = function ServiceWorkerMessageHandler(event) {
|
const messageHandler = (event) => {
|
||||||
if (event.data.type === "fetch-error") {
|
if (event.data.type === "fetch-error") {
|
||||||
window.g_LoungeErrorHandler({
|
errorHandler({
|
||||||
message: `Service worker failed to fetch an url: ${event.data.message}`,
|
message: `Service worker failed to fetch an url: ${event.data.message}`,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@ const router = new VueRouter({
|
|||||||
});
|
});
|
||||||
|
|
||||||
router.afterEach((to) => {
|
router.afterEach((to) => {
|
||||||
if (router.app.initialized) {
|
if (store.state.appLoaded) {
|
||||||
router.app.closeSidebarIfNeeded();
|
router.app.closeSidebarIfNeeded();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,44 +5,48 @@ const socket = require("../socket");
|
|||||||
const storage = require("../localStorage");
|
const storage = require("../localStorage");
|
||||||
const {getActiveWindowComponent} = require("../vue");
|
const {getActiveWindowComponent} = require("../vue");
|
||||||
const store = require("../store").default;
|
const store = require("../store").default;
|
||||||
let lastServerHash = -1;
|
let lastServerHash = null;
|
||||||
|
|
||||||
socket.on("auth", function(data) {
|
socket.on("auth:success", function() {
|
||||||
// If we reconnected and serverHash differs, that means the server restarted
|
store.commit("currentUserVisibleError", "Loading messages…");
|
||||||
// And we will reload the page to grab the latest version
|
$("#loading-page-message").text(store.state.currentUserVisibleError);
|
||||||
if (lastServerHash > -1 && data.serverHash > -1 && data.serverHash !== lastServerHash) {
|
});
|
||||||
socket.disconnect();
|
|
||||||
store.commit("isConnected", false);
|
|
||||||
store.commit("currentUserVisibleError", "Server restarted, reloading…");
|
|
||||||
location.reload(true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data.serverHash > -1) {
|
|
||||||
lastServerHash = data.serverHash;
|
|
||||||
} else {
|
|
||||||
getActiveWindowComponent().inFlight = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
let token;
|
|
||||||
const user = storage.get("user");
|
|
||||||
|
|
||||||
if (!data.success) {
|
|
||||||
if (store.state.activeWindow !== "SignIn") {
|
|
||||||
socket.disconnect();
|
|
||||||
store.commit("isConnected", false);
|
|
||||||
store.commit("currentUserVisibleError", "Authentication failed, reloading…");
|
|
||||||
location.reload();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
socket.on("auth:failed", function() {
|
||||||
storage.remove("token");
|
storage.remove("token");
|
||||||
|
|
||||||
getActiveWindowComponent().errorShown = true;
|
if (store.state.appLoaded) {
|
||||||
} else if (user) {
|
return reloadPage("Authentication failed, reloading…");
|
||||||
token = storage.get("token");
|
}
|
||||||
|
|
||||||
if (token) {
|
// TODO: This will most likely fail getActiveWindowComponent
|
||||||
|
showSignIn();
|
||||||
|
|
||||||
|
// TODO: getActiveWindowComponent is the SignIn component, find a better way to set this
|
||||||
|
getActiveWindowComponent().errorShown = true;
|
||||||
|
getActiveWindowComponent().inFlight = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on("auth:start", function(serverHash) {
|
||||||
|
// If we reconnected and serverHash differs, that means the server restarted
|
||||||
|
// And we will reload the page to grab the latest version
|
||||||
|
if (lastServerHash && serverHash !== lastServerHash) {
|
||||||
|
return reloadPage("Server restarted, reloading…");
|
||||||
|
}
|
||||||
|
|
||||||
|
lastServerHash = serverHash;
|
||||||
|
|
||||||
|
const user = storage.get("user");
|
||||||
|
const token = storage.get("token");
|
||||||
|
const doFastAuth = user && token;
|
||||||
|
|
||||||
|
// If we reconnect and no longer have a stored token, reload the page
|
||||||
|
if (store.state.appLoaded && !doFastAuth) {
|
||||||
|
return reloadPage("Authentication failed, reloading…");
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we have user and token stored, perform auth without showing sign-in first
|
||||||
|
if (doFastAuth) {
|
||||||
store.commit("currentUserVisibleError", "Authorizing…");
|
store.commit("currentUserVisibleError", "Authorizing…");
|
||||||
$("#loading-page-message").text(store.state.currentUserVisibleError);
|
$("#loading-page-message").text(store.state.currentUserVisibleError);
|
||||||
|
|
||||||
@ -63,18 +67,27 @@ socket.on("auth", function(data) {
|
|||||||
const openChannel =
|
const openChannel =
|
||||||
(store.state.activeChannel && store.state.activeChannel.channel.id) || null;
|
(store.state.activeChannel && store.state.activeChannel.channel.id) || null;
|
||||||
|
|
||||||
socket.emit("auth", {user, token, lastMessage, openChannel});
|
socket.emit("auth:perform", {user, token, lastMessage, openChannel});
|
||||||
|
} else {
|
||||||
|
showSignIn();
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function showSignIn() {
|
||||||
|
// TODO: this flashes grey background because it takes a little time for vue to mount signin
|
||||||
|
if (window.g_TheLoungeRemoveLoading) {
|
||||||
|
window.g_TheLoungeRemoveLoading();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (token) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$("#loading").remove();
|
|
||||||
$("#footer")
|
$("#footer")
|
||||||
.find(".sign-in")
|
.find(".sign-in")
|
||||||
.trigger("click", {
|
.trigger("click", {
|
||||||
pushState: false,
|
pushState: false,
|
||||||
});
|
});
|
||||||
});
|
}
|
||||||
|
|
||||||
|
function reloadPage(message) {
|
||||||
|
socket.disconnect();
|
||||||
|
store.commit("currentUserVisibleError", message);
|
||||||
|
location.reload(true);
|
||||||
|
}
|
||||||
|
@ -10,17 +10,14 @@ const router = require("../router");
|
|||||||
const store = require("../store").default;
|
const store = require("../store").default;
|
||||||
|
|
||||||
socket.on("init", function(data) {
|
socket.on("init", function(data) {
|
||||||
store.commit("currentUserVisibleError", "Rendering…");
|
|
||||||
|
|
||||||
$("#loading-page-message").text(store.state.currentUserVisibleError);
|
|
||||||
|
|
||||||
store.commit("networks", mergeNetworkData(data.networks));
|
store.commit("networks", mergeNetworkData(data.networks));
|
||||||
store.commit("isConnected", true);
|
store.commit("isConnected", true);
|
||||||
store.commit("currentUserVisibleError", null);
|
store.commit("currentUserVisibleError", null);
|
||||||
|
|
||||||
if (!vueApp.initialized) {
|
if (!store.state.appLoaded) {
|
||||||
router.initialize();
|
router.initialize();
|
||||||
vueApp.onSocketInit();
|
|
||||||
|
store.commit("appLoaded");
|
||||||
|
|
||||||
if (data.token) {
|
if (data.token) {
|
||||||
storage.set("token", data.token);
|
storage.set("token", data.token);
|
||||||
@ -43,12 +40,10 @@ socket.on("init", function(data) {
|
|||||||
|
|
||||||
vueApp.setUserlist(isUserlistOpen === "true");
|
vueApp.setUserlist(isUserlistOpen === "true");
|
||||||
|
|
||||||
$(document.body).removeClass("signed-out");
|
document.body.classList.remove("signed-out");
|
||||||
$("#loading").remove();
|
|
||||||
|
|
||||||
if (window.g_LoungeErrorHandler) {
|
if (window.g_TheLoungeRemoveLoading) {
|
||||||
window.removeEventListener("error", window.g_LoungeErrorHandler);
|
window.g_TheLoungeRemoveLoading();
|
||||||
window.g_LoungeErrorHandler = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!vueApp.$route.name || vueApp.$route.name === "SignIn") {
|
if (!vueApp.$route.name || vueApp.$route.name === "SignIn") {
|
||||||
|
@ -38,21 +38,18 @@ socket.on("connect", function() {
|
|||||||
$("#loading-page-message").text(store.state.currentUserVisibleError);
|
$("#loading-page-message").text(store.state.currentUserVisibleError);
|
||||||
});
|
});
|
||||||
|
|
||||||
socket.on("authorized", function() {
|
|
||||||
store.commit("currentUserVisibleError", "Loading messages…");
|
|
||||||
$("#loading-page-message").text(store.state.currentUserVisibleError);
|
|
||||||
});
|
|
||||||
|
|
||||||
function handleDisconnect(data) {
|
function handleDisconnect(data) {
|
||||||
const message = data.message || data;
|
const message = data.message || data;
|
||||||
|
|
||||||
store.commit("isConnected", false);
|
store.commit("isConnected", false);
|
||||||
|
|
||||||
store.commit("currentUserVisibleError", `Waiting to reconnect… (${message})`);
|
store.commit("currentUserVisibleError", `Waiting to reconnect… (${message})`);
|
||||||
$("#loading-page-message").text(store.state.currentUserVisibleError);
|
$("#loading-page-message").text(store.state.currentUserVisibleError);
|
||||||
|
|
||||||
// If the server shuts down, socket.io skips reconnection
|
// If the server shuts down, socket.io skips reconnection
|
||||||
// and we have to manually call connect to start the process
|
// and we have to manually call connect to start the process
|
||||||
if (socket.io.skipReconnect) {
|
// However, do not reconnect if TL client manually closed the connection
|
||||||
|
if (socket.io.skipReconnect && message !== "io client disconnect") {
|
||||||
requestIdleCallback(() => socket.connect(), 2000);
|
requestIdleCallback(() => socket.connect(), 2000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,7 @@ const store = new Vuex.Store({
|
|||||||
settings,
|
settings,
|
||||||
},
|
},
|
||||||
state: {
|
state: {
|
||||||
|
appLoaded: false,
|
||||||
activeChannel: null,
|
activeChannel: null,
|
||||||
currentUserVisibleError: null,
|
currentUserVisibleError: null,
|
||||||
desktopNotificationState: "unsupported",
|
desktopNotificationState: "unsupported",
|
||||||
@ -31,6 +32,9 @@ const store = new Vuex.Store({
|
|||||||
versionDataExpired: false,
|
versionDataExpired: false,
|
||||||
},
|
},
|
||||||
mutations: {
|
mutations: {
|
||||||
|
appLoaded(state) {
|
||||||
|
state.appLoaded = true;
|
||||||
|
},
|
||||||
activeChannel(state, channel) {
|
activeChannel(state, channel) {
|
||||||
state.activeChannel = channel;
|
state.activeChannel = channel;
|
||||||
},
|
},
|
||||||
|
@ -15,9 +15,6 @@ const appName = document.title;
|
|||||||
|
|
||||||
const vueApp = new Vue({
|
const vueApp = new Vue({
|
||||||
el: "#viewport",
|
el: "#viewport",
|
||||||
data: {
|
|
||||||
initialized: false,
|
|
||||||
},
|
|
||||||
router,
|
router,
|
||||||
mounted() {
|
mounted() {
|
||||||
if (navigator.platform.match(/(Mac|iPhone|iPod|iPad)/i)) {
|
if (navigator.platform.match(/(Mac|iPhone|iPod|iPad)/i)) {
|
||||||
@ -43,10 +40,6 @@ const vueApp = new Vue({
|
|||||||
}, 1);
|
}, 1);
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
onSocketInit() {
|
|
||||||
this.initialized = true;
|
|
||||||
this.$store.commit("isConnected", true);
|
|
||||||
},
|
|
||||||
setSidebar(state) {
|
setSidebar(state) {
|
||||||
this.$store.commit("sidebarOpen", state);
|
this.$store.commit("sidebarOpen", state);
|
||||||
|
|
||||||
|
@ -174,11 +174,8 @@ module.exports = function(options = {}) {
|
|||||||
if (Helper.config.public) {
|
if (Helper.config.public) {
|
||||||
performAuthentication.call(socket, {});
|
performAuthentication.call(socket, {});
|
||||||
} else {
|
} else {
|
||||||
socket.emit("auth", {
|
socket.on("auth:perform", performAuthentication);
|
||||||
serverHash: serverHash,
|
socket.emit("auth:start", serverHash);
|
||||||
success: true,
|
|
||||||
});
|
|
||||||
socket.on("auth", performAuthentication);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -337,7 +334,8 @@ function indexRequest(req, res) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function initializeClient(socket, client, token, lastMessage, openChannel) {
|
function initializeClient(socket, client, token, lastMessage, openChannel) {
|
||||||
socket.emit("authorized");
|
socket.off("auth:perform", performAuthentication);
|
||||||
|
socket.emit("auth:success");
|
||||||
|
|
||||||
client.clientAttach(socket.id, token);
|
client.clientAttach(socket.id, token);
|
||||||
|
|
||||||
@ -789,7 +787,7 @@ function performAuthentication(data) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
socket.emit("auth", {success: false});
|
socket.emit("auth:failed");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,7 +72,7 @@ describe("Server", function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should emit authorized message", (done) => {
|
it("should emit authorized message", (done) => {
|
||||||
client.on("authorized", done);
|
client.on("auth:success", done);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should create network", (done) => {
|
it("should create network", (done) => {
|
||||||
|
Loading…
Reference in New Issue
Block a user