feat(webui): handle auth via websocket
	
		
			
	
		
	
	
		
	
		
			All checks were successful
		
		
	
	
		
			
				
	
				Build / build (pull_request) Successful in 48s
				
			
		
		
	
	
				
					
				
			
		
			All checks were successful
		
		
	
	Build / build (pull_request) Successful in 48s
				
			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.
This commit is contained in:
		
							parent
							
								
									f84cc54c97
								
							
						
					
					
						commit
						e151f97380
					
				@ -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,9 +96,68 @@ 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;
 | 
				
			||||||
};*/
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					interface IWsMsgFromClient {
 | 
				
			||||||
 | 
					    auth?: {
 | 
				
			||||||
 | 
					        email: string;
 | 
				
			||||||
 | 
					        password: string;
 | 
				
			||||||
 | 
					        isRegister: 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
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    })
 | 
				
			||||||
 | 
					                );
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                ws.send(
 | 
				
			||||||
 | 
					                    JSON.stringify({
 | 
				
			||||||
 | 
					                        auth_fail: {
 | 
				
			||||||
 | 
					                            isRegister: data.auth.isRegister
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    })
 | 
				
			||||||
 | 
					                );
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const sendWsBroadcast = <T>(data: T): void => {
 | 
					export const sendWsBroadcast = <T>(data: T): void => {
 | 
				
			||||||
    const msg = JSON.stringify(data);
 | 
					    const msg = JSON.stringify(data);
 | 
				
			||||||
@ -109,3 +172,21 @@ export const sendWsBroadcast = <T>(data: T): void => {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const sendWsBroadcastTo = <T>(accountId: string, data: T): 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;
 | 
				
			||||||
 | 
				
			|||||||
@ -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 => {
 | 
				
			||||||
 | 
					    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 = () => {
 | 
				
			||||||
 | 
					        if (localStorage.getItem("email") && localStorage.getItem("password")) {
 | 
				
			||||||
 | 
					            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,62 @@ 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();
 | 
				
			||||||
 | 
					            alert(loc(msg.auth_fail.isRegister ? "code_regFail" : "code_loginFail"));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        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 renameAccount() {
 | 
					function renameAccount() {
 | 
				
			||||||
@ -129,10 +134,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