Cleanup auth flow
This commit is contained in:
parent
fc1c9568e2
commit
a1f183f216
@ -85,7 +85,7 @@ export default {
|
||||
|
||||
storage.set("user", values.user);
|
||||
|
||||
socket.emit("auth", values);
|
||||
socket.emit("auth:perform", values);
|
||||
},
|
||||
getStoredUser() {
|
||||
return storage.get("user");
|
||||
|
@ -53,13 +53,13 @@
|
||||
<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-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 id="loading-reload-container">
|
||||
<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>
|
||||
</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 id="viewport"></div>
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* eslint strict: 0, no-var: 0 */
|
||||
/* eslint strict: 0 */
|
||||
"use strict";
|
||||
|
||||
/*
|
||||
@ -9,71 +9,66 @@
|
||||
*/
|
||||
|
||||
(function() {
|
||||
var msg = document.getElementById("loading-page-message");
|
||||
|
||||
if (msg) {
|
||||
const msg = document.getElementById("loading-page-message");
|
||||
msg.textContent = "Loading the app…";
|
||||
|
||||
document.getElementById("loading-reload").addEventListener("click", function() {
|
||||
location.reload(true);
|
||||
});
|
||||
}
|
||||
document
|
||||
.getElementById("loading-reload")
|
||||
.addEventListener("click", () => location.reload(true));
|
||||
|
||||
var displayReload = function displayReload() {
|
||||
var loadingReload = document.getElementById("loading-reload");
|
||||
const displayReload = () => {
|
||||
const loadingReload = document.getElementById("loading-reload");
|
||||
|
||||
if (loadingReload) {
|
||||
loadingReload.style.visibility = "visible";
|
||||
}
|
||||
};
|
||||
|
||||
var loadingSlowTimeout = setTimeout(function() {
|
||||
var 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) {
|
||||
const loadingSlowTimeout = setTimeout(() => {
|
||||
const loadingSlow = document.getElementById("loading-slow");
|
||||
loadingSlow.style.visibility = "visible";
|
||||
displayReload();
|
||||
}
|
||||
}, 5000);
|
||||
|
||||
window.g_LoungeErrorHandler = function LoungeErrorHandler(e) {
|
||||
var message = document.getElementById("loading-page-message");
|
||||
message.textContent =
|
||||
"An error has occurred that prevented the client from loading correctly.";
|
||||
const errorHandler = (e) => {
|
||||
msg.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";
|
||||
|
||||
var data = document.createElement("pre");
|
||||
const data = document.createElement("pre");
|
||||
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.";
|
||||
|
||||
var details = document.createElement("details");
|
||||
const details = document.createElement("details");
|
||||
details.appendChild(summary);
|
||||
details.appendChild(data);
|
||||
details.appendChild(info);
|
||||
message.parentNode.insertBefore(details, message.nextSibling);
|
||||
msg.parentNode.insertBefore(details, msg.nextSibling);
|
||||
|
||||
window.clearTimeout(loadingSlowTimeout);
|
||||
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
|
||||
if ("serviceWorker" in navigator) {
|
||||
navigator.serviceWorker.register("service-worker.js");
|
||||
|
||||
// Handler for messages coming from the service worker
|
||||
var messageHandler = function ServiceWorkerMessageHandler(event) {
|
||||
const messageHandler = (event) => {
|
||||
if (event.data.type === "fetch-error") {
|
||||
window.g_LoungeErrorHandler({
|
||||
errorHandler({
|
||||
message: `Service worker failed to fetch an url: ${event.data.message}`,
|
||||
});
|
||||
|
||||
|
@ -25,7 +25,7 @@ const router = new VueRouter({
|
||||
});
|
||||
|
||||
router.afterEach((to) => {
|
||||
if (router.app.initialized) {
|
||||
if (store.state.appLoaded) {
|
||||
router.app.closeSidebarIfNeeded();
|
||||
}
|
||||
|
||||
|
@ -5,44 +5,48 @@ const socket = require("../socket");
|
||||
const storage = require("../localStorage");
|
||||
const {getActiveWindowComponent} = require("../vue");
|
||||
const store = require("../store").default;
|
||||
let lastServerHash = -1;
|
||||
let lastServerHash = null;
|
||||
|
||||
socket.on("auth", function(data) {
|
||||
// If we reconnected and serverHash differs, that means the server restarted
|
||||
// And we will reload the page to grab the latest version
|
||||
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:success", function() {
|
||||
store.commit("currentUserVisibleError", "Loading messages…");
|
||||
$("#loading-page-message").text(store.state.currentUserVisibleError);
|
||||
});
|
||||
|
||||
socket.on("auth:failed", function() {
|
||||
storage.remove("token");
|
||||
|
||||
getActiveWindowComponent().errorShown = true;
|
||||
} else if (user) {
|
||||
token = storage.get("token");
|
||||
if (store.state.appLoaded) {
|
||||
return reloadPage("Authentication failed, reloading…");
|
||||
}
|
||||
|
||||
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…");
|
||||
$("#loading-page-message").text(store.state.currentUserVisibleError);
|
||||
|
||||
@ -63,18 +67,27 @@ socket.on("auth", function(data) {
|
||||
const openChannel =
|
||||
(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")
|
||||
.find(".sign-in")
|
||||
.trigger("click", {
|
||||
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;
|
||||
|
||||
socket.on("init", function(data) {
|
||||
store.commit("currentUserVisibleError", "Rendering…");
|
||||
|
||||
$("#loading-page-message").text(store.state.currentUserVisibleError);
|
||||
|
||||
store.commit("networks", mergeNetworkData(data.networks));
|
||||
store.commit("isConnected", true);
|
||||
store.commit("currentUserVisibleError", null);
|
||||
|
||||
if (!vueApp.initialized) {
|
||||
if (!store.state.appLoaded) {
|
||||
router.initialize();
|
||||
vueApp.onSocketInit();
|
||||
|
||||
store.commit("appLoaded");
|
||||
|
||||
if (data.token) {
|
||||
storage.set("token", data.token);
|
||||
@ -43,12 +40,10 @@ socket.on("init", function(data) {
|
||||
|
||||
vueApp.setUserlist(isUserlistOpen === "true");
|
||||
|
||||
$(document.body).removeClass("signed-out");
|
||||
$("#loading").remove();
|
||||
document.body.classList.remove("signed-out");
|
||||
|
||||
if (window.g_LoungeErrorHandler) {
|
||||
window.removeEventListener("error", window.g_LoungeErrorHandler);
|
||||
window.g_LoungeErrorHandler = null;
|
||||
if (window.g_TheLoungeRemoveLoading) {
|
||||
window.g_TheLoungeRemoveLoading();
|
||||
}
|
||||
|
||||
if (!vueApp.$route.name || vueApp.$route.name === "SignIn") {
|
||||
|
@ -38,21 +38,18 @@ socket.on("connect", function() {
|
||||
$("#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) {
|
||||
const message = data.message || data;
|
||||
|
||||
store.commit("isConnected", false);
|
||||
|
||||
store.commit("currentUserVisibleError", `Waiting to reconnect… (${message})`);
|
||||
$("#loading-page-message").text(store.state.currentUserVisibleError);
|
||||
|
||||
// If the server shuts down, socket.io skips reconnection
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ const store = new Vuex.Store({
|
||||
settings,
|
||||
},
|
||||
state: {
|
||||
appLoaded: false,
|
||||
activeChannel: null,
|
||||
currentUserVisibleError: null,
|
||||
desktopNotificationState: "unsupported",
|
||||
@ -31,6 +32,9 @@ const store = new Vuex.Store({
|
||||
versionDataExpired: false,
|
||||
},
|
||||
mutations: {
|
||||
appLoaded(state) {
|
||||
state.appLoaded = true;
|
||||
},
|
||||
activeChannel(state, channel) {
|
||||
state.activeChannel = channel;
|
||||
},
|
||||
|
@ -15,9 +15,6 @@ const appName = document.title;
|
||||
|
||||
const vueApp = new Vue({
|
||||
el: "#viewport",
|
||||
data: {
|
||||
initialized: false,
|
||||
},
|
||||
router,
|
||||
mounted() {
|
||||
if (navigator.platform.match(/(Mac|iPhone|iPod|iPad)/i)) {
|
||||
@ -43,10 +40,6 @@ const vueApp = new Vue({
|
||||
}, 1);
|
||||
},
|
||||
methods: {
|
||||
onSocketInit() {
|
||||
this.initialized = true;
|
||||
this.$store.commit("isConnected", true);
|
||||
},
|
||||
setSidebar(state) {
|
||||
this.$store.commit("sidebarOpen", state);
|
||||
|
||||
|
@ -174,11 +174,8 @@ module.exports = function(options = {}) {
|
||||
if (Helper.config.public) {
|
||||
performAuthentication.call(socket, {});
|
||||
} else {
|
||||
socket.emit("auth", {
|
||||
serverHash: serverHash,
|
||||
success: true,
|
||||
});
|
||||
socket.on("auth", performAuthentication);
|
||||
socket.on("auth:perform", performAuthentication);
|
||||
socket.emit("auth:start", serverHash);
|
||||
}
|
||||
});
|
||||
|
||||
@ -337,7 +334,8 @@ function indexRequest(req, res) {
|
||||
}
|
||||
|
||||
function initializeClient(socket, client, token, lastMessage, openChannel) {
|
||||
socket.emit("authorized");
|
||||
socket.off("auth:perform", performAuthentication);
|
||||
socket.emit("auth:success");
|
||||
|
||||
client.clientAttach(socket.id, token);
|
||||
|
||||
@ -789,7 +787,7 @@ function performAuthentication(data) {
|
||||
);
|
||||
}
|
||||
|
||||
socket.emit("auth", {success: false});
|
||||
socket.emit("auth:failed");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -72,7 +72,7 @@ describe("Server", function() {
|
||||
});
|
||||
|
||||
it("should emit authorized message", (done) => {
|
||||
client.on("authorized", done);
|
||||
client.on("auth:success", done);
|
||||
});
|
||||
|
||||
it("should create network", (done) => {
|
||||
|
Loading…
Reference in New Issue
Block a user