temp Revert "cleanup, js classes"

This reverts commit 8c6c423ec7.
This commit is contained in:
creeper123123321 2021-08-06 22:53:55 -03:00
parent 8c6c423ec7
commit 37ca5e22f6
10 changed files with 363 additions and 493 deletions

View File

@ -169,6 +169,13 @@ script-src 'self' https://*.cloudflare.com/ https://alcdn.msauth.net/ https://*.
<script src="https://alcdn.msauth.net/browser/2.15.0/js/msal-browser.min.js" integrity="sha384-/weuqUPkC0P9JxnstihEV1GHdWrheU9Qo3MbdTuxxKJM8l/cSTE5zGP5VBIM4TZN" <script src="https://alcdn.msauth.net/browser/2.15.0/js/msal-browser.min.js" integrity="sha384-/weuqUPkC0P9JxnstihEV1GHdWrheU9Qo3MbdTuxxKJM8l/cSTE5zGP5VBIM4TZN"
crossorigin="anonymous"></script> crossorigin="anonymous"></script>
<script type="module" src="js/page.js"></script> <script src="js/account_manager.js"></script>
<script src="js/auth_ms.js"></script>
<script src="js/cors_proxy.js"></script>
<script src="js/minecraft_id.js"></script>
<script src="js/notification.js"></script>
<script src="js/page.js"></script>
<script src="js/util.js"></script>
<script src="js/websocket.js"></script>
</body> </body>
</html> </html>

View File

@ -1,226 +1,35 @@
import {getCorsProxy} from "./cors_proxy.js"; // Account storage
import {checkFetchSuccess, filterNot, isSuccess} from "./util.js"; function storeMcAccount(accessToken, clientToken, name, id, msUser = null) {
import {addToast, refreshAccountList} from "./page.js"; let accounts = JSON.parse(localStorage.getItem("viaaas_mc_accounts")) || [];
let account = {accessToken: accessToken, clientToken: clientToken, name: name, id: id, msUser: msUser};
let activeAccounts = []; accounts.push(account);
localStorage.setItem("viaaas_mc_accounts", JSON.stringify(accounts));
function loadAccounts() { refreshAccountList();
(JSON.parse(localStorage.getItem("viaaas_mc_accounts")) || []).forEach(it => { return account;
if (it.clientToken) {
addActiveAccount(new MojangAccount(it.id, it.name, it.accessToken, it.clientToken))
} else if (it.msUser) {
addActiveAccount(new MicrosoftAccount(it.id, it.name, it.accessToken, it.msUser))
}
})
} }
$(() => loadAccounts()); function removeMcAccount(id) {
let accounts = getMcAccounts();
function saveRefreshAccounts() { accounts = accounts.filter(it => it.id != id);
localStorage.setItem("viaaas_mc_accounts", JSON.stringify(getActiveAccounts())) localStorage.setItem("viaaas_mc_accounts", JSON.stringify(accounts));
refreshAccountList() refreshAccountList();
} }
export function getActiveAccounts() { function getMcAccounts() {
return activeAccounts; return JSON.parse(localStorage.getItem("viaaas_mc_accounts")) || [];
} }
export function getMicrosoftUsers() { function findAccountByMcName(name) {
return (myMSALObj.getAllAccounts() || []).map(it => it.username); return getMcAccounts().reverse().find(it => it.name.toLowerCase() == name.toLowerCase());
}
function findAccountByMs(username) {
return getMcAccounts().filter(isNotMojang).find(it => it.msUser == username);
} }
export class McAccount { // Mojang account
id; function loginMc(user, pass) {
name; var clientToken = uuid.v4();
accessToken;
loggedOut = false;
constructor(id, username, accessToken) {
this.id = id;
this.name = username;
this.accessToken = accessToken;
}
logout() {
activeAccounts = filterNot(activeAccounts, this);
saveRefreshAccounts();
this.loggedOut = true;
}
checkActive() {
return fetch(getCorsProxy() + "https://authserver.mojang.com/validate", {
method: "post",
body: JSON.stringify({accessToken: this.accessToken}),
headers: {"content-type": "application/json"}
}).then(data => isSuccess(data.status));
}
joinGame(hash) {
return this.acquireActiveToken()
.then(() => fetch(getCorsProxy() + "https://sessionserver.mojang.com/session/minecraft/join", {
method: "post",
body: JSON.stringify({
accessToken: this.accessToken,
selectedProfile: this.id,
serverId: hash
}),
headers: {"content-type": "application/json"}
})).then(checkFetchSuccess("Failed to join session"));
}
refresh() {
}
acquireActiveToken() {
return this.checkActive().then(success => {
if (!success) {
return this.refresh();
}
return this;
}).catch(e => addToast("Failed to refresh token!", e));
}
}
export class MojangAccount extends McAccount {
clientToken;
constructor(id, username, accessToken, clientToken) {
super(id, username, accessToken);
this.clientToken = clientToken;
}
logout() {
super.logout();
fetch(getCorsProxy() + "https://authserver.mojang.com/invalidate", {
method: "post",
body: JSON.stringify({
accessToken: this.accessToken,
clientToken: this.clientToken
}),
headers: {"content-type": "application/json"}
}).then(checkFetchSuccess("not success logout"));
}
checkActive() {
return fetch(getCorsProxy() + "https://authserver.mojang.com/validate", {
method: "post",
body: JSON.stringify({
accessToken: this.accessToken,
clientToken: this.clientToken
}),
headers: {"content-type": "application/json"}
}).then(data => isSuccess(data.status));
}
refresh() {
super.refresh();
console.log("refreshing " + this.id);
return fetch(getCorsProxy() + "https://authserver.mojang.com/refresh", {
method: "post",
body: JSON.stringify({
accessToken: this.accessToken,
clientToken: this.clientToken
}),
headers: {"content-type": "application/json"},
})
.then(checkFetchSuccess("code"))
.then(r => r.json())
.then(json => {
console.log("refreshed " + json.selectedProfile.id);
this.accessToken = json.accessToken;
this.clientToken = json.clientToken;
this.name = json.selectedProfile.name;
this.id = json.id;
saveRefreshAccounts();
});
}
}
export class MicrosoftAccount extends McAccount {
msUser;
constructor(id, username, accessToken, msUser) {
super(id, username, accessToken);
this.msUser = msUser;
}
logout() {
super.logout();
let msAccount = myMSALObj.getAccountByUsername(this.msUser);
if (!msAccount) return;
const logoutRequest = {account: msAccount};
myMSALObj.logout(logoutRequest);
}
refresh() {
super.refresh();
return getTokenPopup(this.msUser, loginRequest)
.then(response => {
// this supports CORS
return fetch("https://user.auth.xboxlive.com/user/authenticate", {
method: "post",
body: JSON.stringify({
Properties: {
AuthMethod: "RPS", SiteName: "user.auth.xboxlive.com",
RpsTicket: "d=" + response.accessToken
}, RelyingParty: "http://auth.xboxlive.com", TokenType: "JWT"
}),
headers: {"content-type": "application/json"}
}).then(checkFetchSuccess("xbox response not success"))
.then(r => r.json());
}).then(json => {
return fetch("https://xsts.auth.xboxlive.com/xsts/authorize", {
method: "post",
body: JSON.stringify({
Properties: {SandboxId: "RETAIL", UserTokens: [json.Token]},
RelyingParty: "rp://api.minecraftservices.com/", TokenType: "JWT"
}),
headers: {"content-type": "application/json"}
}).then(checkFetchSuccess("xsts response not success"))
.then(r => r.json());
}).then(json => {
return fetch(getCorsProxy() + "https://api.minecraftservices.com/authentication/login_with_xbox", {
method: "post",
body: JSON.stringify({identityToken: "XBL3.0 x=" + json.DisplayClaims.xui[0].uhs + ";" + json.Token}),
headers: {"content-type": "application/json"}
}).then(checkFetchSuccess("mc response not success"))
.then(r => r.json());
}).then(json => {
return fetch(getCorsProxy() + "https://api.minecraftservices.com/minecraft/profile", {
method: "get",
headers: {"content-type": "application/json", "authorization": "Bearer " + json.access_token}
}).then(profile => {
if (profile.status === 404) return {id: "MHF_Exclamation", name: "[DEMO]", access_token: ""};
if (!isSuccess(profile.status)) throw "profile response not success";
return profile.json();
}).then(jsonProfile => {
this.accessToken = json.access_token;
this.name = jsonProfile.name;
this.id = jsonProfile.id;
saveRefreshAccounts();
});
});
}
}
export function findAccountByMcName(name) {
return activeAccounts.find(it => it.name.toLowerCase() === name.toLowerCase());
}
export function findAccountByMs(username) {
return getActiveAccounts().find(it => it.msUser === username);
}
function addActiveAccount(acc) {
activeAccounts.push(acc)
saveRefreshAccounts()
}
export function loginMc(user, pass) {
const clientToken = uuid.v4();
fetch(getCorsProxy() + "https://authserver.mojang.com/authenticate", { fetch(getCorsProxy() + "https://authserver.mojang.com/authenticate", {
method: "post", method: "post",
body: JSON.stringify({ body: JSON.stringify({
@ -231,74 +40,123 @@ export function loginMc(user, pass) {
}), }),
headers: {"content-type": "application/json"} headers: {"content-type": "application/json"}
}).then(checkFetchSuccess("code")) }).then(checkFetchSuccess("code"))
.then(r => r.json()) .then(r => r.json())
.then(data => { .then(data => {
let acc = new MojangAccount(data.selectedProfile.id, data.selectedProfile.name, data.accessToken, data.clientToken); storeMcAccount(data.accessToken, data.clientToken, data.selectedProfile.name, data.selectedProfile.id);
addActiveAccount(acc); }).catch(e => addToast("Failed to login", e));
return acc;
}).catch(e => addToast("Failed to login", e));
$("#form_add_mc input").val(""); $("#form_add_mc input").val("");
} }
// https://docs.microsoft.com/en-us/azure/active-directory/develop/tutorial-v2-javascript-auth-code function logoutMojang(id) {
const azureClientId = "a370fff9-7648-4dbf-b96e-2b4f8d539ac2"; getMcAccounts().filter(isMojang).filter(it => it.id == id).forEach(it => {
const whitelistedOrigin = [ fetch(getCorsProxy() + "https://authserver.mojang.com/invalidate", {method: "post",
"https://via-login.geyserconnect.net", body: JSON.stringify({
"https://via.re.yt.nom.br", accessToken: it.accessToken,
"https://viaaas.noxt.cf" clientToken: it.clientToken
]; }),
const loginRequest = {scopes: ["XboxLive.signin"]}; headers: {"content-type": "application/json"}
let redirectUrl = "https://viaversion.github.io/VIAaaS/src/main/resources/web/"; })
if (location.hostname === "localhost" || whitelistedOrigin.includes(location.origin)) { .then(checkFetchSuccess("not success logout"))
redirectUrl = location.origin + location.pathname; .finally(() => removeMcAccount(id));
}
const msalConfig = {
auth: {
clientId: azureClientId,
authority: "https://login.microsoftonline.com/consumers/",
redirectUri: redirectUrl,
},
cache: {
cacheLocation: "sessionStorage",
storeAuthStateInCookie: false,
}
};
const myMSALObj = new msal.PublicClientApplication(msalConfig);
export function loginMs() {
myMSALObj.loginRedirect(loginRequest);
}
$(() => myMSALObj.handleRedirectPromise().then((resp) => {
if (resp) {
let found = findAccountByMs(resp.account.username)
if (!found) {
let accNew = new MicrosoftAccount("", "", "", resp.account.username);
accNew.refresh()
.then(() => addActiveAccount(accNew))
.catch(e => addToast("Failed to get token", e));
} else {
found.refresh()
.catch(e => addToast("Failed to refresh token", e));
}
}
}));
function getTokenPopup(username, request) {
/**
* See here for more info on account retrieval:
* https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-common/docs/Accounts.md
*/
request.account = myMSALObj.getAccountByUsername(username);
return myMSALObj.acquireTokenSilent(request).catch(error => {
console.warn("silent token acquisition fails.");
if (error instanceof msal.InteractionRequiredAuthError) {
// fallback to interaction when silent call fails
return myMSALObj.acquireTokenPopup(request).catch(error => console.error(error));
} else {
console.warn(error);
}
}); });
} }
function refreshMojangAccount(it) {
console.log("refreshing " + it.id);
return fetch(getCorsProxy() + "https://authserver.mojang.com/refresh", {
method: "post",
body: JSON.stringify({
accessToken: it.accessToken,
clientToken: it.clientToken
}),
headers: {"content-type": "application/json"},
}).then(checkFetchSuccess("code"))
.then(r => r.json())
.then(json => {
console.log("refreshed " + json.selectedProfile.id);
removeMcAccount(json.selectedProfile.id);
return storeMcAccount(json.accessToken, json.clientToken, json.selectedProfile.name, json.selectedProfile.id);
});
}
// Generic
function getMcUserToken(account) {
return validateToken(account.accessToken, account.clientToken || undefined).then(data => {
if (!isSuccess(data.status)) {
if (isMojang(account)) {
return refreshMojangAccount(account);
} else {
return refreshTokenMs(account.msUser);
}
}
return account;
}).catch(e => addToast("Failed to refresh token!", e));
}
function validateToken(accessToken, clientToken) {
return fetch(getCorsProxy() + "https://authserver.mojang.com/validate", {
method: "post",
body: JSON.stringify({
accessToken: accessToken,
clientToken: clientToken
}),
headers: {"content-type": "application/json"}
});
}
function joinGame(token, id, hash) {
return fetch(getCorsProxy() + "https://sessionserver.mojang.com/session/minecraft/join", {
method: "post",
body: JSON.stringify({
accessToken: token,
selectedProfile: id,
serverId: hash
}),
headers: {"content-type": "application/json"}
});
}
// Microsoft auth
function refreshTokenMs(username) {
return getTokenPopup(username, loginRequest)
.then(response => {
// this supports CORS
return fetch("https://user.auth.xboxlive.com/user/authenticate", {method: "post",
body: JSON.stringify({Properties: {AuthMethod: "RPS", SiteName: "user.auth.xboxlive.com",
RpsTicket: "d=" + response.accessToken}, RelyingParty: "http://auth.xboxlive.com", TokenType: "JWT"}),
headers: {"content-type": "application/json"}})
.then(checkFetchSuccess("xbox response not success"))
.then(r => r.json());
}).then(json => {
return fetch("https://xsts.auth.xboxlive.com/xsts/authorize", {method: "post",
body: JSON.stringify({Properties: {SandboxId: "RETAIL", UserTokens: [json.Token]},
RelyingParty: "rp://api.minecraftservices.com/", TokenType: "JWT"}),
headers: {"content-type": "application/json"}})
.then(checkFetchSuccess("xsts response not success"))
.then(r => r.json());
}).then(json => {
return fetch(getCorsProxy() + "https://api.minecraftservices.com/authentication/login_with_xbox", {method: "post",
body: JSON.stringify({identityToken: "XBL3.0 x=" + json.DisplayClaims.xui[0].uhs + ";" + json.Token}),
headers: {"content-type": "application/json"}})
.then(checkFetchSuccess("mc response not success"))
.then(r => r.json());
}).then(json => {
return fetch(getCorsProxy() + "https://api.minecraftservices.com/minecraft/profile", {
method: "get", headers: {"content-type": "application/json", "authorization": "Bearer " + json.access_token}}).then(profile => {
if (profile.status == 404) return {id: "MHF_Exclamation", name: "[DEMO]"};
if (!isSuccess(profile.status)) throw "profile response not success";
return profile.json();
}).then(jsonProfile => {
removeMcAccount(jsonProfile.id);
return storeMcAccount(json.access_token, null, jsonProfile.name, jsonProfile.id, username);
});
});
}
function isMojang(it) {
return !!it.clientToken;
}
function isNotMojang(it) {
return !isMojang(it);
}

View File

@ -0,0 +1,65 @@
// https://docs.microsoft.com/en-us/azure/active-directory/develop/tutorial-v2-javascript-auth-code
const azureClientId = "a370fff9-7648-4dbf-b96e-2b4f8d539ac2";
const whitelistedOrigin = ["https://localhost:25543", "https://via-login.geyserconnect.net", "https://via.re.yt.nom.br", "https://viaaas.noxt.cf"];
let redirectUrl = "https://viaversion.github.io/VIAaaS/src/main/resources/web/";
if (whitelistedOrigin.includes(location.origin)) {
redirectUrl = location.origin + location.pathname;
}
const msalConfig = {
auth: {
clientId: azureClientId,
authority: "https://login.microsoftonline.com/consumers/",
redirectUri: redirectUrl,
},
cache: {
cacheLocation: "sessionStorage",
storeAuthStateInCookie: false,
}
};
const myMSALObj = new msal.PublicClientApplication(msalConfig);
const loginRequest = {
scopes: ["XboxLive.signin"]
};
function loginMs() {
myMSALObj.loginRedirect(loginRequest);
}
$(() => myMSALObj.handleRedirectPromise().then((resp) => {
if (resp) {
refreshTokenMs(resp.account.username).catch(e => addToast("Failed to get token", e));
refreshAccountList();
}
}));
function getTokenPopup(username, request) {
/**
* See here for more info on account retrieval:
* https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-common/docs/Accounts.md
*/
request.account = myMSALObj.getAccountByUsername(username);
return myMSALObj.acquireTokenSilent(request).catch(error => {
console.warn("silent token acquisition fails.");
if (error instanceof msal.InteractionRequiredAuthError) {
// fallback to interaction when silent call fails
return myMSALObj.acquireTokenPopup(request).catch(error => console.error(error));
} else {
console.warn(error);
}
});
}
function logoutMs(username) {
let mcAcc = findAccountByMs(username) || {};
removeMcAccount(mcAcc.id);
const logoutRequest = {
account: myMSALObj.getAccountByUsername(username)
};
myMSALObj.logout(logoutRequest);
}

View File

@ -1,14 +1,10 @@
import {refreshCorsStatus} from "./page.js";
function defaultCors() { function defaultCors() {
return "https://crp123-cors.herokuapp.com/"; return "https://crp123-cors.herokuapp.com/";
} }
function getCorsProxy() {
export function getCorsProxy() {
return localStorage.getItem("viaaas_cors_proxy") || defaultCors(); return localStorage.getItem("viaaas_cors_proxy") || defaultCors();
} }
function setCorsProxy(url) {
export function setCorsProxy(url) {
localStorage.setItem("viaaas_cors_proxy", url); localStorage.setItem("viaaas_cors_proxy", url);
refreshCorsStatus(); refreshCorsStatus();
} }

View File

@ -0,0 +1,19 @@
// Minecraft.id
var mcIdUsername = null;
var mcauth_code = null;
var mcauth_success = null;
$(() => {
let urlParams = new URLSearchParams();
window.location.hash.substr(1).split("?").map(it => new URLSearchParams(it).forEach((a, b) => urlParams.append(b, a)));
mcIdUsername = urlParams.get("username");
mcauth_code = urlParams.get("mcauth_code");
mcauth_success = urlParams.get("mcauth_success");
if (mcauth_success == "false") {
addToast("Couldn't authenticate with Minecraft.ID", urlParams.get("mcauth_msg"));
}
if (mcauth_code != null) {
history.replaceState(null, null, "#");
renderActions();
}
});

View File

@ -1,8 +1,7 @@
// Notification // Notification
let notificationCallbacks = {}; var notificationCallbacks = {};
$(() => { $(() => {
new BroadcastChannel("viaaas-notification") new BroadcastChannel("viaaas-notification").addEventListener("message", handleSWMsg);
.addEventListener("message", handleSWMsg);
}) })
function handleSWMsg(event) { function handleSWMsg(event) {
@ -14,8 +13,8 @@ function handleSWMsg(event) {
callback(data.action); callback(data.action);
} }
export function authNotification(msg, yes, no) { function authNotification(msg, yes, no) {
if (!navigator.serviceWorker || Notification.permission !== "granted") { if (!navigator.serviceWorker || Notification.permission != "granted") {
if (confirm(msg)) yes(); else no(); if (confirm(msg)) yes(); else no();
return; return;
} }
@ -24,21 +23,21 @@ export function authNotification(msg, yes, no) {
r.showNotification("Click to allow auth impersionation", { r.showNotification("Click to allow auth impersionation", {
body: msg, body: msg,
tag: tag, tag: tag,
vibrate: [200, 10, 100, 200, 100, 10, 100, 10, 200], vibrate: [200,10,100,200,100,10,100,10,200],
actions: [ actions: [
{action: "reject", title: "Reject"}, {action: "reject", title: "Reject"},
{action: "confirm", title: "Confirm"} {action: "confirm", title: "Confirm"}
] ]
}); });
notificationCallbacks[tag] = action => { notificationCallbacks[tag] = action => {
if (action === "reject") { if (action == "reject") {
no(); no();
} else if (!action || action === "confirm") { } else if (!action || action == "confirm") {
yes(); yes();
} else {
return;
} }
}; };
setTimeout(() => { setTimeout(() => { delete notificationCallbacks[tag] }, 30 * 1000);
delete notificationCallbacks[tag]
}, 30 * 1000);
}); });
} }

View File

@ -1,67 +1,35 @@
// Minecraft.id var connectionStatus = document.getElementById("connection_status");
import {icanhazepoch, icanhazip} from "./util.js"; var corsStatus = document.getElementById("cors_status");
import {getCorsProxy, setCorsProxy} from "./cors_proxy.js"; var listening = document.getElementById("listening");
import { var actions = document.getElementById("actions");
findAccountByMs, var accounts = document.getElementById("accounts-list");
getActiveAccounts, var listenVisible = false;
getMicrosoftUsers, var workers = [];
loginMc,
loginMs, MicrosoftAccount,
MojangAccount
} from "./account_manager.js";
import {connect, getWsUrl, removeToken, sendSocket, setWsUrl, unlisten} from "./websocket.js";
let mcIdUsername = null;
let mcauth_code = null;
let mcauth_success = null;
$(() => {
let urlParams = new URLSearchParams();
window.location.hash.substr(1).split("?")
.map(it => new URLSearchParams(it)
.forEach((a, b) => urlParams.append(b, a)));
mcIdUsername = urlParams.get("username");
mcauth_code = urlParams.get("mcauth_code");
mcauth_success = urlParams.get("mcauth_success");
if (mcauth_success === "false") {
addToast("Couldn't authenticate with Minecraft.ID", urlParams.get("mcauth_msg"));
}
if (mcauth_code != null) {
history.replaceState(null, null, "#");
renderActions();
}
});
let connectionStatus = document.getElementById("connection_status");
let corsStatus = document.getElementById("cors_status");
let listening = document.getElementById("listening");
let actions = document.getElementById("actions");
let accounts = document.getElementById("accounts-list");
let cors_proxy_txt = document.getElementById("cors-proxy");
let ws_url_txt = document.getElementById("ws-url");
let listenVisible = false;
let workers = [];
$(() => workers = new Array(navigator.hardwareConcurrency).fill(null).map(() => new Worker("js/worker.js"))); $(() => workers = new Array(navigator.hardwareConcurrency).fill(null).map(() => new Worker("js/worker.js")));
window.addEventListener('beforeinstallprompt', e => { window.addEventListener('beforeinstallprompt', e => {
e.preventDefault(); e.preventDefault();
}); });
// On load // On load
$(() => { $(() => {
if (navigator.serviceWorker) { if (navigator.serviceWorker) {
navigator.serviceWorker.register("sw.js").then(() => { navigator.serviceWorker.register("sw.js");
swCacheFiles(); navigator.serviceWorker.ready.then(ready => ready.active.postMessage({
}); action: "cache",
urls: performance.getEntriesByType("resource")
.map(it => it.name)
.filter(it => it.endsWith(".js") || it.endsWith(".css") || it.endsWith(".png"))
})); // https://stackoverflow.com/questions/46830493/is-there-any-way-to-cache-all-files-of-defined-folder-path-in-service-worker
} }
ohNo(); ohNo();
cors_proxy_txt.value = getCorsProxy(); $("#cors-proxy").val(getCorsProxy());
ws_url_txt.value = getWsUrl(); $("#ws-url").val(getWsUrl());
$("form").on("submit", e => e.preventDefault()); $("form").on("submit", e => e.preventDefault());
$("#form_add_mc").on("submit", () => loginMc($("#email").val(), $("#password").val())); $("#form_add_mc").on("submit", e => loginMc($("#email").val(), $("#password").val()));
$("#form_add_ms").on("submit", () => loginMs()); $("#form_add_ms").on("submit", e => loginMs());
$("#form_ws_url").on("submit", () => setWsUrl($("#ws-url").val())); $("#form_ws_url").on("submit", e => setWsUrl($("#ws-url").val()));
$("#form_cors_proxy").on("submit", () => setCorsProxy($("#cors-proxy").val())); $("#form_cors_proxy").on("submit", e => setCorsProxy($("#cors-proxy").val()));
$(".css_async").attr("disabled", null); $(".css_async").attr("disabled", null);
workers.forEach(it => it.onmessage = onWorkerMsg); workers.forEach(it => it.onmessage = onWorkerMsg);
@ -73,31 +41,14 @@ $(() => {
connect(); connect();
}); });
function swCacheFiles() { function refreshCorsStatus() {
navigator.serviceWorker.ready.then(ready => ready.active.postMessage({
action: "cache",
urls: performance.getEntriesByType("resource")
.map(it => it.name)
.filter(it => it.endsWith(".js") || it.endsWith(".css") || it.endsWith(".png"))
})); // https://stackoverflow.com/questions/46830493/is-there-any-way-to-cache-all-files-of-defined-folder-path-in-service-worker
}
export function setWsStatus(txt) {
connectionStatus.innerText = txt;
}
export function setListenVisible(visible) {
listenVisible = visible;
}
export function refreshCorsStatus() {
corsStatus.innerText = "..."; corsStatus.innerText = "...";
icanhazip(true).then(ip => { icanhazip(true).then(ip => {
return icanhazip(false).then(ip2 => corsStatus.innerText = "OK " + ip + (ip !== ip2 ? " (different IP)" : "")); return icanhazip(false).then(ip2 => corsStatus.innerText = "OK " + ip + (ip != ip2 ? " (different IP)" : ""));
}).catch(e => corsStatus.innerText = "error: " + e); }).catch(e => corsStatus.innerText = "error: " + e);
} }
function addMcAccountToList(account) { function addMcAccountToList(id, name, msUser = null) {
let p = document.createElement("li"); let p = document.createElement("li");
p.className = "input-group d-flex"; p.className = "input-group d-flex";
let shead = document.createElement("span"); let shead = document.createElement("span");
@ -108,15 +59,19 @@ function addMcAccountToList(account) {
n.className = "form-control"; n.className = "form-control";
let remove = document.createElement("a"); let remove = document.createElement("a");
remove.className = "btn btn-danger"; remove.className = "btn btn-danger";
n.innerText = " " + account.name + " " + (account instanceof MicrosoftAccount ? "(" + account.msUser + ") " : ""); n.innerText = " " + name + " " + (msUser == null ? "" : "(" + msUser + ") ");
remove.innerText = "Logout"; remove.innerText = "Logout";
remove.href = "javascript:"; remove.href = "javascript:";
remove.onclick = () => { remove.onclick = () => {
account.logout(); if (msUser == null) {
logoutMojang(id);
} else {
logoutMs(msUser);
}
}; };
head.width = 24; head.width = "24";
head.alt = account.name + "'s head"; head.alt = name + "'s head";
head.src = "https://crafthead.net/helm/" + account.id; head.src = "https://crafthead.net/helm/" + id;
//(id.length == 36 || id.length == 32) ? "https://crafatar.com/avatars/" + id + "?overlay" : "https://crafthead.net/helm/" + id; //(id.length == 36 || id.length == 32) ? "https://crafatar.com/avatars/" + id + "?overlay" : "https://crafthead.net/helm/" + id;
p.append(shead); p.append(shead);
p.append(n); p.append(n);
@ -124,35 +79,28 @@ function addMcAccountToList(account) {
accounts.appendChild(p); accounts.appendChild(p);
} }
export function refreshAccountList() { function refreshAccountList() {
accounts.innerHTML = ""; accounts.innerHTML = "";
getActiveAccounts() getMcAccounts().filter(isMojang).sort((a, b) => a.name.localeCompare(b.name)).forEach(it => addMcAccountToList(it.id, it.name));
.filter(it => it instanceof MojangAccount) (myMSALObj.getAllAccounts() || []).sort((a, b) => a.username.localeCompare(b.username)).forEach(msAccount => {
.sort((a, b) => a.name.localeCompare(b.name)) let mcAcc = findAccountByMs(msAccount.username) || {id: "MHF_Question", name: "..."};
.forEach(it => addMcAccountToList(it)); addMcAccountToList(mcAcc.id, mcAcc.name, msAccount.username);
getMicrosoftUsers() });
.sort((a, b) => a.localeCompare(b))
.forEach(username => {
let mcAcc = findAccountByMs(username);
if (!mcAcc) return;
addMcAccountToList(mcAcc);
});
} }
export function renderActions() { function renderActions() {
actions.innerHTML = ""; actions.innerHTML = "";
if (Notification.permission === "default") { if (Notification.permission == "default") {
actions.innerHTML += '<p><a href="javascript:" id="notificate">Enable notifications</a></p>'; actions.innerHTML += '<p><a href="javascript:" id="notificate">Enable notifications</a></p>';
$("#notificate").on("click", () => Notification.requestPermission().then(renderActions)); // i'm lazy $("#notificate").on("click", e => Notification.requestPermission().then(renderActions)); // i'm lazy
} }
if (listenVisible) { if (listenVisible) {
if (mcIdUsername != null && mcauth_code != null) { if (mcIdUsername != null && mcauth_code != null) {
addAction("Listen to " + mcIdUsername, () => { addAction("Listen to " + mcIdUsername, () => {
sendSocket(JSON.stringify({ socket.send(JSON.stringify({
"action": "minecraft_id_login", "action": "minecraft_id_login",
"username": mcIdUsername, "username": mcIdUsername,
"code": mcauth_code "code": mcauth_code}));
}));
mcauth_code = null; mcauth_code = null;
renderActions(); renderActions();
}); });
@ -163,7 +111,7 @@ export function renderActions() {
let callbackUrl = new URL(location); let callbackUrl = new URL(location);
callbackUrl.search = ""; callbackUrl.search = "";
callbackUrl.hash = "#username=" + encodeURIComponent(user); callbackUrl.hash = "#username=" + encodeURIComponent(user);
location.href = "https://api.minecraft.id/gateway/start/" + encodeURIComponent(user) location = "https://api.minecraft.id/gateway/start/" + encodeURIComponent(user)
+ "?callback=" + encodeURIComponent(callbackUrl); + "?callback=" + encodeURIComponent(callbackUrl);
}); });
addAction("Listen to frontend offline login in VIAaaS instance", () => { addAction("Listen to frontend offline login in VIAaaS instance", () => {
@ -178,13 +126,13 @@ export function renderActions() {
function onWorkerMsg(e) { function onWorkerMsg(e) {
console.log(e); console.log(e);
if (e.data.action === "completed_pow") onCompletedPoW(e); if (e.data.action == "completed_pow") onCompletedPoW(e);
} }
function onCompletedPoW(e) { function onCompletedPoW(e) {
addToast("Offline username", "Completed proof of work"); addToast("Offline username", "Completed proof of work");
workers.forEach(it => it.postMessage({action: "cancel", id: e.data.id})); workers.forEach(it => it.postMessage({action: "cancel", id: e.data.id}));
sendSocket(e.data.msg); socket.send(e.data.msg);
} }
function addAction(text, onClick) { function addAction(text, onClick) {
@ -197,7 +145,7 @@ function addAction(text, onClick) {
actions.appendChild(p); actions.appendChild(p);
} }
export function addListeningList(user, token) { function addListeningList(user, token) {
let p = document.createElement("p"); let p = document.createElement("p");
let head = document.createElement("img"); let head = document.createElement("img");
let n = document.createElement("span"); let n = document.createElement("span");
@ -210,7 +158,7 @@ export function addListeningList(user, token) {
listening.removeChild(p); listening.removeChild(p);
unlisten(user); unlisten(user);
}; };
head.width = 24; head.width = "24";
head.alt = user + "'s head"; head.alt = user + "'s head";
head.src = "https://crafthead.net/helm/" + user; head.src = "https://crafthead.net/helm/" + user;
p.append(head); p.append(head);
@ -219,7 +167,7 @@ export function addListeningList(user, token) {
listening.appendChild(p); listening.appendChild(p);
} }
export function addToast(title, msg) { function addToast(title, msg) {
let toast = document.createElement("div"); let toast = document.createElement("div");
document.getElementById("toasts").prepend(toast); document.getElementById("toasts").prepend(toast);
$(toast) $(toast)
@ -240,7 +188,7 @@ export function addToast(title, msg) {
new bootstrap.Toast(toast).show(); new bootstrap.Toast(toast).show();
} }
export function resetHtml() { function resetHtml() {
listening.innerHTML = ""; listening.innerHTML = "";
listenVisible = false; listenVisible = false;
renderActions(); renderActions();
@ -248,16 +196,13 @@ export function resetHtml() {
function ohNo() { function ohNo() {
try { try {
icanhazepoch().then(sec => { icanhazepoch().then(sec => {
if (Math.abs(Date.now() / 1000 - sec) > 15) { if (Math.abs(Date.now() / 1000 - sec) > 15) {
addToast("Time isn't synchronized", "Please synchronize your computer time to NTP servers"); addToast("Time isn't synchronized", "Please synchronize your computer time to NTP servers");
} else { } else {
console.log("time seems synchronized"); console.log("time seems synchronized");
} }
}) })
new Date().getDay() === 3 && console.log("it's snapshot day 🐸 my dudes"); new Date().getDay() == 3 && console.log("it's snapshot day 🐸 my dudes"); new Date().getDate() == 1 && new Date().getMonth() == 3 && addToast("WARNING", "Your ViaVersion has expired, please renew it at https://viaversion.com/ for $99");
new Date().getDate() === 1 && new Date().getMonth() === 3 && addToast("WARNING", "Your ViaVersion has expired, please renew it at https://viaversion.com/ for $99"); } catch (e) { console.log(e); }
} catch (e) {
console.log(e);
}
} }

View File

@ -1,30 +1,24 @@
import {getCorsProxy} from "./cors_proxy.js"; function isSuccess(status) {
export function isSuccess(status) {
return status >= 200 && status < 300; return status >= 200 && status < 300;
} }
export function checkFetchSuccess(msg) { function checkFetchSuccess(msg) {
return r => { return r => {
if (!isSuccess(r.status)) throw r.status + " " + msg; if (!isSuccess(r.status)) throw r.status + " " + msg;
return r; return r;
}; };
} }
export function icanhazip(cors) { function icanhazip(cors) {
return fetch((cors ? getCorsProxy() : "") + "https://ipv4.icanhazip.com") return fetch((cors ? getCorsProxy() : "") + "https://ipv4.icanhazip.com")
.then(checkFetchSuccess("code")) .then(checkFetchSuccess("code"))
.then(r => r.text()) .then(r => r.text())
.then(it => it.trim()); .then(it => it.trim());
} }
export function icanhazepoch() { function icanhazepoch() {
return fetch("https://icanhazepoch.com") return fetch("https://icanhazepoch.com")
.then(checkFetchSuccess("code")) .then(checkFetchSuccess("code"))
.then(r => r.text()) .then(r => r.text())
.then(it => parseInt(it.trim())) .then(it => parseInt(it.trim()))
}
export function filterNot(array, item) {
return array.filter(it => it !== item);
} }

View File

@ -1,30 +1,23 @@
import {authNotification} from "./notification.js"; var wsUrl = getWsUrl();
import {checkFetchSuccess} from "./util.js"; var socket = null;
import {findAccountByMcName} from "./account_manager.js";
import {addListeningList, addToast, renderActions, resetHtml, setListenVisible, setWsStatus} from "./page.js";
let wsUrl = getWsUrl();
let socket = null;
// WS url // WS url
function defaultWs() { function defaultWs() {
let url = new URL("ws", new URL(location)); let url = new URL("ws", new URL(location));
url.protocol = "wss"; url.protocol = "wss";
return window.location.host.endsWith("github.io") || !window.location.protocol.startsWith("http") return window.location.host == "viaversion.github.io" || !window.location.host ? "wss://localhost:25543/ws" : url.toString();
? "wss://localhost:25543/ws" : url.toString();
} }
export function getWsUrl() { function getWsUrl() {
return localStorage.getItem("viaaas_ws_url") || defaultWs(); return localStorage.getItem("viaaas_ws_url") || defaultWs();
} }
function setWsUrl(url) {
export function setWsUrl(url) {
localStorage.setItem("viaaas_ws_url", url); localStorage.setItem("viaaas_ws_url", url);
location.reload(); location.reload();
} }
// Tokens // Tokens
export function saveToken(token) { function saveToken(token) {
let hTokens = JSON.parse(localStorage.getItem("viaaas_tokens")) || {}; let hTokens = JSON.parse(localStorage.getItem("viaaas_tokens")) || {};
let tokens = getTokens(); let tokens = getTokens();
tokens.push(token); tokens.push(token);
@ -32,28 +25,28 @@ export function saveToken(token) {
localStorage.setItem("viaaas_tokens", JSON.stringify(hTokens)); localStorage.setItem("viaaas_tokens", JSON.stringify(hTokens));
} }
export function removeToken(token) { function removeToken(token) {
let hTokens = JSON.parse(localStorage.getItem("viaaas_tokens")) || {}; let hTokens = JSON.parse(localStorage.getItem("viaaas_tokens")) || {};
let tokens = getTokens(); let tokens = getTokens();
tokens = tokens.filter(it => it !== token); tokens = tokens.filter(it => it != token);
hTokens[wsUrl] = tokens; hTokens[wsUrl] = tokens;
localStorage.setItem("viaaas_tokens", JSON.stringify(hTokens)); localStorage.setItem("viaaas_tokens", JSON.stringify(hTokens));
} }
export function getTokens() { function getTokens() {
return (JSON.parse(localStorage.getItem("viaaas_tokens")) || {})[wsUrl] || []; return (JSON.parse(localStorage.getItem("viaaas_tokens")) || {})[wsUrl] || [];
} }
// Websocket // Websocket
export function listen(token) { function listen(token) {
socket.send(JSON.stringify({"action": "listen_login_requests", "token": token})); socket.send(JSON.stringify({"action": "listen_login_requests", "token": token}));
} }
export function unlisten(id) { function unlisten(id) {
socket.send(JSON.stringify({"action": "unlisten_login_requests", "uuid": id})); socket.send(JSON.stringify({"action": "unlisten_login_requests", "uuid": id}));
} }
export function confirmJoin(hash) { function confirmJoin(hash) {
socket.send(JSON.stringify({action: "session_hash_response", session_hash: hash})); socket.send(JSON.stringify({action: "session_hash_response", session_hash: hash}));
} }
@ -63,10 +56,12 @@ function handleJoinRequest(parsed) {
+ parsed.message.split(/[\r\n]+/).map(it => "> " + it).join('\n'), () => { + parsed.message.split(/[\r\n]+/).map(it => "> " + it).join('\n'), () => {
let account = findAccountByMcName(parsed.user); let account = findAccountByMcName(parsed.user);
if (account) { if (account) {
account.joinGame(parsed.session_hash) getMcUserToken(account).then(data => {
.then(checkFetchSuccess("code")) return joinGame(data.accessToken, data.id, parsed.session_hash);
.finally(() => confirmJoin(parsed.session_hash)) })
.catch((e) => addToast("Couldn't contact session server", "Error: " + e)); .then(checkFetchSuccess("code"))
.finally(() => confirmJoin(parsed.session_hash))
.catch((e) => addToast("Couldn't contact session server", "Error: " + e));
} else { } else {
confirmJoin(parsed.session_hash); confirmJoin(parsed.session_hash);
addToast("Couldn't find account", "Couldn't find " + parsed.user + ", check Accounts tab"); addToast("Couldn't find account", "Couldn't find " + parsed.user + ", check Accounts tab");
@ -76,51 +71,51 @@ function handleJoinRequest(parsed) {
function onSocketMsg(event) { function onSocketMsg(event) {
let parsed = JSON.parse(event.data); let parsed = JSON.parse(event.data);
if (parsed.action === "ad_minecraft_id_login") { if (parsed.action == "ad_minecraft_id_login") {
setListenVisible(true); listenVisible = true;
renderActions(); renderActions();
} else if (parsed.action === "login_result") { } else if (parsed.action == "login_result") {
if (!parsed.success) { if (!parsed.success) {
addToast("Couldn't verify Minecraft account", "VIAaaS returned failed response"); addToast("Couldn't verify Minecraft account", "VIAaaS returned failed response");
} else { } else {
listen(parsed.token); listen(parsed.token);
saveToken(parsed.token); saveToken(parsed.token);
} }
} else if (parsed.action === "listen_login_requests_result") { } else if (parsed.action == "listen_login_requests_result") {
if (parsed.success) { if (parsed.success) {
addListeningList(parsed.user, parsed.token); addListeningList(parsed.user, parsed.token);
} else { } else {
removeToken(parsed.token); removeToken(parsed.token);
} }
} else if (parsed.action === "session_hash_request") { } else if (parsed.action == "session_hash_request") {
handleJoinRequest(parsed); handleJoinRequest(parsed);
} }
} }
export function listenStoredTokens() { function listenStoredTokens() {
getTokens().forEach(listen); getTokens().forEach(listen);
} }
function onConnect() { function onConnect() {
setWsStatus("connected"); connectionStatus.innerText = "connected";
resetHtml(); resetHtml();
listenStoredTokens(); listenStoredTokens();
} }
function onWsError(e) { function onWsError(e) {
console.log(e); console.log(e);
setWsStatus("socket error"); connectionStatus.innerText = "socket error";
resetHtml(); resetHtml();
} }
function onDisconnect(evt) { function onDisconnect(evt) {
setWsStatus("disconnected with close code " + evt.code + " and reason: " + evt.reason); connectionStatus.innerText = "disconnected with close code " + evt.code + " and reason: " + evt.reason;
resetHtml(); resetHtml();
setTimeout(connect, 5000); setTimeout(connect, 5000);
} }
export function connect() { function connect() {
setWsStatus("connecting..."); connectionStatus.innerText = "connecting...";
socket = new WebSocket(wsUrl); socket = new WebSocket(wsUrl);
socket.onerror = onWsError; socket.onerror = onWsError;
@ -128,11 +123,3 @@ export function connect() {
socket.onclose = onDisconnect socket.onclose = onDisconnect
socket.onmessage = onSocketMsg; socket.onmessage = onSocketMsg;
} }
export function sendSocket(msg) {
if (!socket) {
console.error("couldn't send msg, socket isn't set");
return
}
socket.send(msg);
}

View File

@ -1,43 +1,43 @@
importScripts("https://cdnjs.cloudflare.com/ajax/libs/js-sha512/0.8.0/sha512.min.js"); importScripts("https://cdnjs.cloudflare.com/ajax/libs/js-sha512/0.8.0/sha512.min.js");
let pending = []; var pending = [];
onmessage = function (e) { onmessage = function(e) {
if (e.data.action === "listen_pow") startPoW(e); if (e.data.action == "listen_pow") startPoW(e);
if (e.data.action === "cancel") removePending(e.data.id); if (e.data.action == "cancel") removePending(e.data.id);
} }
function removePending(id) { function removePending(id) {
console.log("removing task" + id); console.log("removing task" + id);
pending = pending.filter(it => it !== id); pending = pending.filter(it => it != id);
} }
function startPoW(e) { function startPoW(e) {
pending.push(e.data.id); pending.push(e.data.id);
listenPoW(e); listenPoW(e);
} }
function listenPoW(e) { function listenPoW(e) {
let user = e.data.user; var user = e.data.user;
let msg = null; let msg = null;
let endTime = Date.now() + 1000; var endTime = Date.now() + 1000;
do { do {
if (!pending.includes(e.data.id)) return; // cancelled if (!pending.includes(e.data.id)) return; // cancelled
msg = JSON.stringify({ msg = JSON.stringify({
action: "offline_login", action: "offline_login",
username: user, username: user,
date: Date.now(), date: Date.now(),
rand: Math.random() rand: Math.random()
}); });
if (Date.now() >= endTime) { if (Date.now() >= endTime) {
setTimeout(() => listenPoW(e), 0); setTimeout(() => listenPoW(e), 0);
return; return;
} }
} while (!sha512(msg).startsWith("00000")); } while (!sha512(msg).startsWith("00000"));
postMessage({id: e.data.id, action: "completed_pow", msg: msg}); postMessage({id: e.data.id, action: "completed_pow", msg: msg});
} }
/* function sha512(s) { /* function sha512(s) {