feat(webui): handle auth via websocket (#2226)
Now when logging in and out of the game, the webui is notified so it can refresh the nonce, removing the need for constant login requests to revalidate it. Closes #2223 Reviewed-on: #2226 Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com> Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
This commit is contained in:
parent
93ef9a5348
commit
2fa6dcc7ed
@ -4,16 +4,16 @@ import { config } from "@/src/services/configService";
|
|||||||
import { buildConfig } from "@/src/services/buildConfigService";
|
import { buildConfig } from "@/src/services/buildConfigService";
|
||||||
|
|
||||||
import { Account } from "@/src/models/loginModel";
|
import { Account } from "@/src/models/loginModel";
|
||||||
import { createAccount, isCorrectPassword, isNameTaken } from "@/src/services/loginService";
|
import { createAccount, createNonce, getUsernameFromEmail, isCorrectPassword } from "@/src/services/loginService";
|
||||||
import { IDatabaseAccountJson, ILoginRequest, ILoginResponse } from "@/src/types/loginTypes";
|
import { IDatabaseAccountJson, ILoginRequest, ILoginResponse } from "@/src/types/loginTypes";
|
||||||
import { logger } from "@/src/utils/logger";
|
import { logger } from "@/src/utils/logger";
|
||||||
import { version_compare } from "@/src/helpers/inventoryHelpers";
|
import { version_compare } from "@/src/helpers/inventoryHelpers";
|
||||||
|
import { sendWsBroadcastTo } from "@/src/services/webService";
|
||||||
|
|
||||||
export const loginController: RequestHandler = async (request, response) => {
|
export const loginController: RequestHandler = async (request, response) => {
|
||||||
const loginRequest = JSON.parse(String(request.body)) as ILoginRequest; // parse octet stream of json data to json object
|
const loginRequest = JSON.parse(String(request.body)) as ILoginRequest; // parse octet stream of json data to json object
|
||||||
|
|
||||||
const account = await Account.findOne({ email: loginRequest.email });
|
const account = await Account.findOne({ email: loginRequest.email });
|
||||||
const nonce = Math.round(Math.random() * Number.MAX_SAFE_INTEGER);
|
|
||||||
|
|
||||||
const buildLabel: string =
|
const buildLabel: string =
|
||||||
typeof request.query.buildLabel == "string"
|
typeof request.query.buildLabel == "string"
|
||||||
@ -42,26 +42,14 @@ export const loginController: RequestHandler = async (request, response) => {
|
|||||||
loginRequest.ClientType == "webui-register")
|
loginRequest.ClientType == "webui-register")
|
||||||
) {
|
) {
|
||||||
try {
|
try {
|
||||||
const nameFromEmail = loginRequest.email.substring(0, loginRequest.email.indexOf("@"));
|
const name = await getUsernameFromEmail(loginRequest.email);
|
||||||
let name = nameFromEmail || loginRequest.email.substring(1) || "SpaceNinja";
|
|
||||||
if (await isNameTaken(name)) {
|
|
||||||
let suffix = 0;
|
|
||||||
do {
|
|
||||||
++suffix;
|
|
||||||
name = nameFromEmail + suffix;
|
|
||||||
} while (await isNameTaken(name));
|
|
||||||
}
|
|
||||||
const newAccount = await createAccount({
|
const newAccount = await createAccount({
|
||||||
email: loginRequest.email,
|
email: loginRequest.email,
|
||||||
password: loginRequest.password,
|
password: loginRequest.password,
|
||||||
DisplayName: name,
|
DisplayName: name,
|
||||||
CountryCode: loginRequest.lang?.toUpperCase() ?? "EN",
|
CountryCode: loginRequest.lang?.toUpperCase() ?? "EN",
|
||||||
ClientType: loginRequest.ClientType == "webui-register" ? "webui" : loginRequest.ClientType,
|
ClientType: loginRequest.ClientType,
|
||||||
CrossPlatformAllowed: true,
|
Nonce: createNonce(),
|
||||||
ForceLogoutVersion: 0,
|
|
||||||
ConsentNeeded: false,
|
|
||||||
TrackedSettings: [],
|
|
||||||
Nonce: nonce,
|
|
||||||
BuildLabel: buildLabel,
|
BuildLabel: buildLabel,
|
||||||
LastLogin: new Date()
|
LastLogin: new Date()
|
||||||
});
|
});
|
||||||
@ -80,38 +68,29 @@ export const loginController: RequestHandler = async (request, response) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (loginRequest.ClientType == "webui-register") {
|
|
||||||
response.status(400).json({ error: "account already exists" });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isCorrectPassword(loginRequest.password, account.password)) {
|
if (!isCorrectPassword(loginRequest.password, account.password)) {
|
||||||
response.status(400).json({ error: "incorrect login data" });
|
response.status(400).json({ error: "incorrect login data" });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (loginRequest.ClientType == "webui") {
|
if (account.Nonce && account.ClientType != "webui" && !account.Dropped && !loginRequest.kick) {
|
||||||
if (!account.Nonce) {
|
// U17 seems to handle "nonce still set" like a login failure.
|
||||||
account.ClientType = "webui";
|
if (version_compare(buildLabel, "2015.12.05.18.07") >= 0) {
|
||||||
account.Nonce = nonce;
|
response.status(400).send({ error: "nonce still set" });
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
if (account.Nonce && account.ClientType != "webui" && !account.Dropped && !loginRequest.kick) {
|
|
||||||
// U17 seems to handle "nonce still set" like a login failure.
|
|
||||||
if (version_compare(buildLabel, "2015.12.05.18.07") >= 0) {
|
|
||||||
response.status(400).send({ error: "nonce still set" });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
account.ClientType = loginRequest.ClientType;
|
|
||||||
account.Nonce = nonce;
|
|
||||||
account.CountryCode = loginRequest.lang?.toUpperCase() ?? "EN";
|
|
||||||
account.BuildLabel = buildLabel;
|
|
||||||
account.LastLogin = new Date();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
account.ClientType = loginRequest.ClientType;
|
||||||
|
account.Nonce = createNonce();
|
||||||
|
account.CountryCode = loginRequest.lang?.toUpperCase() ?? "EN";
|
||||||
|
account.BuildLabel = buildLabel;
|
||||||
|
account.LastLogin = new Date();
|
||||||
await account.save();
|
await account.save();
|
||||||
|
|
||||||
|
// Tell WebUI its nonce has been invalidated
|
||||||
|
sendWsBroadcastTo(account._id.toString(), { logged_out: true });
|
||||||
|
|
||||||
response.json(createLoginResponse(myAddress, myUrlBase, account.toJSON(), buildLabel));
|
response.json(createLoginResponse(myAddress, myUrlBase, account.toJSON(), buildLabel));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { RequestHandler } from "express";
|
import { RequestHandler } from "express";
|
||||||
import { Account } from "@/src/models/loginModel";
|
import { Account } from "@/src/models/loginModel";
|
||||||
|
import { sendWsBroadcastTo } from "@/src/services/webService";
|
||||||
|
|
||||||
export const logoutController: RequestHandler = async (req, res) => {
|
export const logoutController: RequestHandler = async (req, res) => {
|
||||||
if (!req.query.accountId) {
|
if (!req.query.accountId) {
|
||||||
@ -10,7 +11,7 @@ export const logoutController: RequestHandler = async (req, res) => {
|
|||||||
throw new Error("Request is missing nonce parameter");
|
throw new Error("Request is missing nonce parameter");
|
||||||
}
|
}
|
||||||
|
|
||||||
await Account.updateOne(
|
const stat = await Account.updateOne(
|
||||||
{
|
{
|
||||||
_id: req.query.accountId,
|
_id: req.query.accountId,
|
||||||
Nonce: nonce
|
Nonce: nonce
|
||||||
@ -19,6 +20,10 @@ export const logoutController: RequestHandler = async (req, res) => {
|
|||||||
Nonce: 0
|
Nonce: 0
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
if (stat.modifiedCount) {
|
||||||
|
// Tell WebUI its nonce has been invalidated
|
||||||
|
sendWsBroadcastTo(req.query.accountId as string, { logged_out: true });
|
||||||
|
}
|
||||||
|
|
||||||
res.writeHead(200, {
|
res.writeHead(200, {
|
||||||
"Content-Type": "text/html",
|
"Content-Type": "text/html",
|
||||||
|
@ -11,13 +11,13 @@ const databaseAccountSchema = new Schema<IDatabaseAccountJson>(
|
|||||||
email: { type: String, required: true, unique: true },
|
email: { type: String, required: true, unique: true },
|
||||||
password: { type: String, required: true },
|
password: { type: String, required: true },
|
||||||
DisplayName: { type: String, required: true, unique: true },
|
DisplayName: { type: String, required: true, unique: true },
|
||||||
CountryCode: { type: String, required: true },
|
CountryCode: { type: String, default: "" },
|
||||||
ClientType: { type: String },
|
ClientType: { type: String },
|
||||||
CrossPlatformAllowed: { type: Boolean, required: true },
|
CrossPlatformAllowed: { type: Boolean, default: true },
|
||||||
ForceLogoutVersion: { type: Number, required: true },
|
ForceLogoutVersion: { type: Number, default: 0 },
|
||||||
AmazonAuthToken: { type: String },
|
AmazonAuthToken: { type: String },
|
||||||
AmazonRefreshToken: { type: String },
|
AmazonRefreshToken: { type: String },
|
||||||
ConsentNeeded: { type: Boolean, required: true },
|
ConsentNeeded: { type: Boolean, default: false },
|
||||||
TrackedSettings: { type: [String], default: [] },
|
TrackedSettings: { type: [String], default: [] },
|
||||||
Nonce: { type: Number, default: 0 },
|
Nonce: { type: Number, default: 0 },
|
||||||
BuildLabel: String,
|
BuildLabel: String,
|
||||||
|
@ -18,6 +18,23 @@ export const isNameTaken = async (name: string): Promise<boolean> => {
|
|||||||
return !!(await Account.findOne({ DisplayName: name }));
|
return !!(await Account.findOne({ DisplayName: name }));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const createNonce = (): number => {
|
||||||
|
return Math.round(Math.random() * Number.MAX_SAFE_INTEGER);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getUsernameFromEmail = async (email: string): Promise<string> => {
|
||||||
|
const nameFromEmail = email.substring(0, email.indexOf("@"));
|
||||||
|
let name = nameFromEmail || email.substring(1) || "SpaceNinja";
|
||||||
|
if (await isNameTaken(name)) {
|
||||||
|
let suffix = 0;
|
||||||
|
do {
|
||||||
|
++suffix;
|
||||||
|
name = nameFromEmail + suffix;
|
||||||
|
} while (await isNameTaken(name));
|
||||||
|
}
|
||||||
|
return nameFromEmail;
|
||||||
|
};
|
||||||
|
|
||||||
export const createAccount = async (accountData: IDatabaseAccountRequiredFields): Promise<IDatabaseAccountJson> => {
|
export const createAccount = async (accountData: IDatabaseAccountRequiredFields): Promise<IDatabaseAccountJson> => {
|
||||||
const account = new Account(accountData);
|
const account = new Account(accountData);
|
||||||
try {
|
try {
|
||||||
|
@ -6,6 +6,10 @@ import { logger } from "../utils/logger";
|
|||||||
import { app } from "../app";
|
import { app } from "../app";
|
||||||
import { AddressInfo } from "node:net";
|
import { AddressInfo } from "node:net";
|
||||||
import ws from "ws";
|
import ws from "ws";
|
||||||
|
import { Account } from "../models/loginModel";
|
||||||
|
import { createAccount, createNonce, getUsernameFromEmail, isCorrectPassword } from "./loginService";
|
||||||
|
import { IDatabaseAccountJson } from "../types/loginTypes";
|
||||||
|
import { HydratedDocument } from "mongoose";
|
||||||
|
|
||||||
let httpServer: http.Server | undefined;
|
let httpServer: http.Server | undefined;
|
||||||
let httpsServer: https.Server | undefined;
|
let httpsServer: https.Server | undefined;
|
||||||
@ -25,7 +29,7 @@ export const startWebServer = (): void => {
|
|||||||
httpServer = http.createServer(app);
|
httpServer = http.createServer(app);
|
||||||
httpServer.listen(httpPort, () => {
|
httpServer.listen(httpPort, () => {
|
||||||
wsServer = new ws.Server({ server: httpServer });
|
wsServer = new ws.Server({ server: httpServer });
|
||||||
//wsServer.on("connection", wsOnConnect);
|
wsServer.on("connection", wsOnConnect);
|
||||||
|
|
||||||
logger.info("HTTP server started on port " + httpPort);
|
logger.info("HTTP server started on port " + httpPort);
|
||||||
|
|
||||||
@ -33,7 +37,7 @@ export const startWebServer = (): void => {
|
|||||||
httpsServer = https.createServer(tlsOptions, app);
|
httpsServer = https.createServer(tlsOptions, app);
|
||||||
httpsServer.listen(httpsPort, () => {
|
httpsServer.listen(httpsPort, () => {
|
||||||
wssServer = new ws.Server({ server: httpsServer });
|
wssServer = new ws.Server({ server: httpsServer });
|
||||||
//wssServer.on("connection", wsOnConnect);
|
wssServer.on("connection", wsOnConnect);
|
||||||
|
|
||||||
logger.info("HTTPS server started on port " + httpsPort);
|
logger.info("HTTPS server started on port " + httpsPort);
|
||||||
|
|
||||||
@ -92,11 +96,91 @@ export const stopWebServer = async (): Promise<void> => {
|
|||||||
await Promise.all(promises);
|
await Promise.all(promises);
|
||||||
};
|
};
|
||||||
|
|
||||||
/*const wsOnConnect = (ws: ws, _req: http.IncomingMessage): void => {
|
interface IWsCustomData extends ws {
|
||||||
ws.on("message", console.log);
|
accountId?: string;
|
||||||
};*/
|
}
|
||||||
|
|
||||||
export const sendWsBroadcast = <T>(data: T): void => {
|
interface IWsMsgFromClient {
|
||||||
|
auth?: {
|
||||||
|
email: string;
|
||||||
|
password: string;
|
||||||
|
isRegister: boolean;
|
||||||
|
};
|
||||||
|
logout?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IWsMsgToClient {
|
||||||
|
ports?: {
|
||||||
|
http: number | undefined;
|
||||||
|
https: number | undefined;
|
||||||
|
};
|
||||||
|
config_reloaded?: boolean;
|
||||||
|
auth_succ?: {
|
||||||
|
id: string;
|
||||||
|
DisplayName: string;
|
||||||
|
Nonce: number;
|
||||||
|
};
|
||||||
|
auth_fail?: {
|
||||||
|
isRegister: boolean;
|
||||||
|
};
|
||||||
|
logged_out?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const wsOnConnect = (ws: ws, _req: http.IncomingMessage): void => {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
||||||
|
ws.on("message", async msg => {
|
||||||
|
const data = JSON.parse(String(msg)) as IWsMsgFromClient;
|
||||||
|
if (data.auth) {
|
||||||
|
let account: IDatabaseAccountJson | null = await Account.findOne({ email: data.auth.email });
|
||||||
|
if (account) {
|
||||||
|
if (isCorrectPassword(data.auth.password, account.password)) {
|
||||||
|
if (!account.Nonce) {
|
||||||
|
account.ClientType = "webui";
|
||||||
|
account.Nonce = createNonce();
|
||||||
|
await (account as HydratedDocument<IDatabaseAccountJson>).save();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
account = null;
|
||||||
|
}
|
||||||
|
} else if (data.auth.isRegister) {
|
||||||
|
const name = await getUsernameFromEmail(data.auth.email);
|
||||||
|
account = await createAccount({
|
||||||
|
email: data.auth.email,
|
||||||
|
password: data.auth.password,
|
||||||
|
ClientType: "webui",
|
||||||
|
LastLogin: new Date(),
|
||||||
|
DisplayName: name,
|
||||||
|
Nonce: createNonce()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (account) {
|
||||||
|
(ws as IWsCustomData).accountId = account.id;
|
||||||
|
ws.send(
|
||||||
|
JSON.stringify({
|
||||||
|
auth_succ: {
|
||||||
|
id: account.id,
|
||||||
|
DisplayName: account.DisplayName,
|
||||||
|
Nonce: account.Nonce
|
||||||
|
}
|
||||||
|
} satisfies IWsMsgToClient)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
ws.send(
|
||||||
|
JSON.stringify({
|
||||||
|
auth_fail: {
|
||||||
|
isRegister: data.auth.isRegister
|
||||||
|
}
|
||||||
|
} satisfies IWsMsgToClient)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (data.logout) {
|
||||||
|
(ws as IWsCustomData).accountId = undefined;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const sendWsBroadcast = (data: IWsMsgToClient): void => {
|
||||||
const msg = JSON.stringify(data);
|
const msg = JSON.stringify(data);
|
||||||
if (wsServer) {
|
if (wsServer) {
|
||||||
for (const client of wsServer.clients) {
|
for (const client of wsServer.clients) {
|
||||||
@ -109,3 +193,21 @@ export const sendWsBroadcast = <T>(data: T): void => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const sendWsBroadcastTo = (accountId: string, data: IWsMsgToClient): void => {
|
||||||
|
const msg = JSON.stringify(data);
|
||||||
|
if (wsServer) {
|
||||||
|
for (const client of wsServer.clients) {
|
||||||
|
if ((client as IWsCustomData).accountId == accountId) {
|
||||||
|
client.send(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (wssServer) {
|
||||||
|
for (const client of wssServer.clients) {
|
||||||
|
if ((client as IWsCustomData).accountId == accountId) {
|
||||||
|
client.send(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
@ -2,7 +2,7 @@ import { Types } from "mongoose";
|
|||||||
|
|
||||||
export interface IAccountAndLoginResponseCommons {
|
export interface IAccountAndLoginResponseCommons {
|
||||||
DisplayName: string;
|
DisplayName: string;
|
||||||
CountryCode: string;
|
CountryCode?: string;
|
||||||
ClientType?: string;
|
ClientType?: string;
|
||||||
CrossPlatformAllowed?: boolean;
|
CrossPlatformAllowed?: boolean;
|
||||||
ForceLogoutVersion?: number;
|
ForceLogoutVersion?: number;
|
||||||
|
@ -37,7 +37,7 @@
|
|||||||
<li class="nav-item dropdown user-dropdown">
|
<li class="nav-item dropdown user-dropdown">
|
||||||
<button class="nav-link dropdown-toggle displayname" data-bs-toggle="dropdown" aria-expanded="false"></button>
|
<button class="nav-link dropdown-toggle displayname" data-bs-toggle="dropdown" aria-expanded="false"></button>
|
||||||
<ul class="dropdown-menu dropdown-menu-end">
|
<ul class="dropdown-menu dropdown-menu-end">
|
||||||
<li><a class="dropdown-item" href="/webui/" onclick="logout();" data-loc="navbar_logout"></a></li>
|
<li><a class="dropdown-item" href="/webui/" onclick="doLogout();" data-loc="navbar_logout"></a></li>
|
||||||
<li><hr class="dropdown-divider"></li>
|
<li><hr class="dropdown-divider"></li>
|
||||||
<li><a class="dropdown-item" href="#" onclick="event.preventDefault();renameAccount();" data-loc="navbar_renameAccount"></a></li>
|
<li><a class="dropdown-item" href="#" onclick="event.preventDefault();renameAccount();" data-loc="navbar_renameAccount"></a></li>
|
||||||
<li><a class="dropdown-item" href="#" onclick="event.preventDefault();deleteAccount();" data-loc="navbar_deleteAccount"></a></li>
|
<li><a class="dropdown-item" href="#" onclick="event.preventDefault();deleteAccount();" data-loc="navbar_deleteAccount"></a></li>
|
||||||
|
@ -8,8 +8,28 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
||||||
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
||||||
|
|
||||||
|
let auth_pending = false,
|
||||||
|
did_initial_auth = false;
|
||||||
|
const sendAuth = isRegister => {
|
||||||
|
if (localStorage.getItem("email") && localStorage.getItem("password")) {
|
||||||
|
auth_pending = true;
|
||||||
|
window.ws.send(
|
||||||
|
JSON.stringify({
|
||||||
|
auth: {
|
||||||
|
email: localStorage.getItem("email"),
|
||||||
|
password: wp.encSync(localStorage.getItem("password")),
|
||||||
|
isRegister
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
function openWebSocket() {
|
function openWebSocket() {
|
||||||
window.ws = new WebSocket("/custom/ws");
|
window.ws = new WebSocket("/custom/ws");
|
||||||
|
window.ws.onopen = () => {
|
||||||
|
sendAuth(false);
|
||||||
|
};
|
||||||
window.ws.onmessage = e => {
|
window.ws.onmessage = e => {
|
||||||
const msg = JSON.parse(e.data);
|
const msg = JSON.parse(e.data);
|
||||||
if ("ports" in msg) {
|
if ("ports" in msg) {
|
||||||
@ -21,31 +41,9 @@ function openWebSocket() {
|
|||||||
single.loadRoute("/webui/cheats");
|
single.loadRoute("/webui/cheats");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
if ("auth_succ" in msg) {
|
||||||
window.ws.onclose = function () {
|
auth_pending = false;
|
||||||
setTimeout(openWebSocket, 3000);
|
const data = msg.auth_succ;
|
||||||
};
|
|
||||||
}
|
|
||||||
openWebSocket();
|
|
||||||
|
|
||||||
let loginOrRegisterPending = false;
|
|
||||||
window.registerSubmit = false;
|
|
||||||
|
|
||||||
function doLogin() {
|
|
||||||
if (loginOrRegisterPending) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
loginOrRegisterPending = true;
|
|
||||||
localStorage.setItem("email", $("#email").val());
|
|
||||||
localStorage.setItem("password", $("#password").val());
|
|
||||||
loginFromLocalStorage();
|
|
||||||
registerSubmit = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
function loginFromLocalStorage() {
|
|
||||||
const isRegister = registerSubmit;
|
|
||||||
doLoginRequest(
|
|
||||||
data => {
|
|
||||||
if (single.getCurrentPath() == "/webui/") {
|
if (single.getCurrentPath() == "/webui/") {
|
||||||
single.loadRoute("/webui/inventory");
|
single.loadRoute("/webui/inventory");
|
||||||
}
|
}
|
||||||
@ -55,55 +53,74 @@ function loginFromLocalStorage() {
|
|||||||
if (window.dict) {
|
if (window.dict) {
|
||||||
updateLocElements();
|
updateLocElements();
|
||||||
}
|
}
|
||||||
updateInventory();
|
if (!did_initial_auth) {
|
||||||
},
|
did_initial_auth = true;
|
||||||
() => {
|
updateInventory();
|
||||||
logout();
|
}
|
||||||
alert(loc(isRegister ? "code_regFail" : "code_loginFail"));
|
|
||||||
}
|
}
|
||||||
);
|
if ("auth_fail" in msg) {
|
||||||
|
auth_pending = false;
|
||||||
|
logout();
|
||||||
|
if (single.getCurrentPath() == "/webui/") {
|
||||||
|
alert(loc(msg.auth_fail.isRegister ? "code_regFail" : "code_loginFail"));
|
||||||
|
} else {
|
||||||
|
single.loadRoute("/webui/");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ("logged_out" in msg) {
|
||||||
|
sendAuth();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
window.ws.onclose = function () {
|
||||||
|
window.ws = undefined;
|
||||||
|
setTimeout(openWebSocket, 3000);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
openWebSocket();
|
||||||
|
|
||||||
|
function getWebSocket() {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
let interval;
|
||||||
|
interval = setInterval(() => {
|
||||||
|
if (window.ws) {
|
||||||
|
clearInterval(interval);
|
||||||
|
resolve(window.ws);
|
||||||
|
}
|
||||||
|
}, 10);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function doLoginRequest(succ_cb, fail_cb) {
|
window.registerSubmit = false;
|
||||||
const req = $.post({
|
|
||||||
url: "/api/login.php",
|
function doLogin() {
|
||||||
contentType: "text/plain",
|
if (auth_pending) {
|
||||||
data: JSON.stringify({
|
return;
|
||||||
email: localStorage.getItem("email").toLowerCase(),
|
}
|
||||||
password: wp.encSync(localStorage.getItem("password"), "hex"),
|
localStorage.setItem("email", $("#email").val());
|
||||||
time: parseInt(new Date() / 1000),
|
localStorage.setItem("password", $("#password").val());
|
||||||
s: "W0RFXVN0ZXZlIGxpa2VzIGJpZyBidXR0cw==", // signature of some kind
|
sendAuth(registerSubmit);
|
||||||
lang: "en",
|
window.registerSubmit = false;
|
||||||
// eslint-disable-next-line no-loss-of-precision
|
|
||||||
date: 1501230947855458660, // ???
|
|
||||||
ClientType: registerSubmit ? "webui-register" : "webui",
|
|
||||||
PS: "W0RFXVN0ZXZlIGxpa2VzIGJpZyBidXR0cw==" // anti-cheat data
|
|
||||||
})
|
|
||||||
});
|
|
||||||
req.done(succ_cb);
|
|
||||||
req.fail(fail_cb);
|
|
||||||
req.always(() => {
|
|
||||||
loginOrRegisterPending = false;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function revalidateAuthz(succ_cb) {
|
function revalidateAuthz(succ_cb) {
|
||||||
return doLoginRequest(
|
getWebSocket().then(() => {
|
||||||
data => {
|
// We have a websocket connection, so authz should be good.
|
||||||
window.authz = "accountId=" + data.id + "&nonce=" + data.Nonce;
|
succ_cb();
|
||||||
succ_cb();
|
});
|
||||||
},
|
|
||||||
() => {
|
|
||||||
logout();
|
|
||||||
alert(loc("code_nonValidAuthz"));
|
|
||||||
single.loadRoute("/webui/"); // Show login screen
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function logout() {
|
function logout() {
|
||||||
localStorage.removeItem("email");
|
localStorage.removeItem("email");
|
||||||
localStorage.removeItem("password");
|
localStorage.removeItem("password");
|
||||||
|
did_initial_auth = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function doLogout() {
|
||||||
|
logout();
|
||||||
|
if (window.ws) {
|
||||||
|
// Unsubscribe from notifications about nonce invalidation
|
||||||
|
window.ws.send(JSON.stringify({ logout: true }));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function renameAccount() {
|
function renameAccount() {
|
||||||
@ -129,10 +146,6 @@ function deleteAccount() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (localStorage.getItem("email") && localStorage.getItem("password")) {
|
|
||||||
loginFromLocalStorage();
|
|
||||||
}
|
|
||||||
|
|
||||||
single.on("route_load", function (event) {
|
single.on("route_load", function (event) {
|
||||||
if (event.route.paths[0] != "/webui/") {
|
if (event.route.paths[0] != "/webui/") {
|
||||||
// Authorised route?
|
// Authorised route?
|
||||||
|
Loading…
x
Reference in New Issue
Block a user