From 9f6b0d56929a1ff10d4393524f25a763cbd83583 Mon Sep 17 00:00:00 2001 From: q13x <84165981+WorldEditAxe@users.noreply.github.com> Date: Wed, 5 Jul 2023 11:40:26 -0700 Subject: [PATCH] shut up prismarine libraries' hardcoded loggers --- src/plugins/EagProxyAAS/CustomAuthflow.ts | 225 ++++++++++++++++++++++ src/plugins/EagProxyAAS/auth.ts | 4 +- src/plugins/EagProxyAAS/index.ts | 4 +- src/plugins/EagProxyAAS/utils.ts | 18 +- 4 files changed, 238 insertions(+), 13 deletions(-) create mode 100644 src/plugins/EagProxyAAS/CustomAuthflow.ts diff --git a/src/plugins/EagProxyAAS/CustomAuthflow.ts b/src/plugins/EagProxyAAS/CustomAuthflow.ts new file mode 100644 index 0000000..94d61e3 --- /dev/null +++ b/src/plugins/EagProxyAAS/CustomAuthflow.ts @@ -0,0 +1,225 @@ +import fs from "fs"; +import path from "path"; +import crypto from "crypto"; + +import { createHash } from "prismarine-auth/src/common/Util.js"; +import Constants from "prismarine-auth/src/common/Constants.js"; +const { Endpoints, msalConfig } = Constants; + +import LiveTokenManager from "prismarine-auth/src/TokenManagers/LiveTokenManager.js"; +import JavaTokenManager from "prismarine-auth/src/TokenManagers/MinecraftJavaTokenManager.js"; +import XboxTokenManager from "prismarine-auth/src/TokenManagers/XboxTokenManager.js"; +import MsaTokenManager from "prismarine-auth/src/TokenManagers/MsaTokenManager.js"; +import BedrockTokenManager from "prismarine-auth/src/TokenManagers/MinecraftBedrockTokenManager.js"; + +/* +MIT License + +Copyright (c) 2020 PrismarineJS + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +async function retry(methodFn, beforeRetry, times) { + for (let attempts = 0; attempts < times; attempts++) { + try { + return await methodFn(); + } catch (err) { + if (err instanceof URIError) { + throw err; + } + } + await new Promise((resolve) => setTimeout(resolve, 2000)); + await beforeRetry(); + } +} + +export class CustomAuthflow { + username: string; + options: any; + codeCallback: any; + msa: any; + doTitleAuth: boolean; + xbl: any; + mba: any; + mca: any; + + constructor(username = "", cache, options, codeCallback) { + this.username = username; + if (options && !options.flow) { + throw new Error( + "Missing 'flow' argument in options. See docs for more information." + ); + } + this.options = options || { flow: "msal" }; + this.initTokenManagers(username, cache); + this.codeCallback = codeCallback; + } + + initTokenManagers(username, cache) { + if (this.options.flow === "live" || this.options.flow === "sisu") { + if (!this.options.authTitle) + throw new Error( + `Please specify an "authTitle" in Authflow constructor when using ${this.options.flow} flow` + ); + this.msa = new LiveTokenManager( + this.options.authTitle, + ["service::user.auth.xboxlive.com::MBI_SSL"], + cache({ cacheName: this.options.flow, username }) + ); + this.doTitleAuth = true; + } else if (this.options.flow === "msal") { + const config = Object.assign( + { ...msalConfig }, + this.options.authTitle + ? { auth: { ...msalConfig.auth, clientId: this.options.authTitle } } + : {} + ); + this.msa = new MsaTokenManager( + config, + ["XboxLive.signin", "offline_access"], + cache({ cacheName: "msal", username }) + ); + } else { + throw new Error( + `Unknown flow: ${this.options.flow} (expected "live", "sisu", or "msal")` + ); + } + + const keyPair = crypto.generateKeyPairSync("ec", { namedCurve: "P-256" }); + this.xbl = new XboxTokenManager( + keyPair, + cache({ cacheName: "xbl", username }) + ); + this.mba = new BedrockTokenManager(cache({ cacheName: "bed", username })); + this.mca = new JavaTokenManager(cache({ cacheName: "mca", username })); + } + + static resetTokenCaches(cache) { + if (!cache) throw new Error("You must provide a cache directory to reset."); + if (fs.existsSync(cache)) { + fs.rmSync(cache, { recursive: true }); + return true; + } + } + + async getMsaToken() { + if (await this.msa.verifyTokens()) { + const { token } = await this.msa.getAccessToken(); + return token; + } else { + const ret = await this.msa.authDeviceCode((response) => { + if (this.codeCallback) return this.codeCallback(response); + console.info("[msa] First time signing in. Please authenticate now:"); + console.info(response.message); + }); + + return ret.accessToken; + } + } + + async getXboxToken( + relyingParty = this.options.relyingParty || Endpoints.XboxRelyingParty + ) { + const options = { ...this.options, relyingParty }; + if (await this.xbl.verifyTokens(relyingParty)) { + const { data } = await this.xbl.getCachedXstsToken(relyingParty); + return data; + } else if (options.password) { + const xsts = await this.xbl.doReplayAuth( + this.username, + options.password, + options + ); + return xsts; + } else { + return await retry( + async () => { + const msaToken = await this.getMsaToken(); + + if (options.flow === "sisu") { + const deviceToken = await this.xbl.getDeviceToken(options); + const sisu = await this.xbl.doSisuAuth( + msaToken, + deviceToken, + options + ); + return sisu; + } + + const userToken = await this.xbl.getUserToken( + msaToken, + options.flow === "msal" + ); + + if (this.doTitleAuth) { + const deviceToken = await this.xbl.getDeviceToken(options); + const titleToken = await this.xbl.getTitleToken( + msaToken, + deviceToken + ); + const xsts = await this.xbl.getXSTSToken( + { userToken, deviceToken, titleToken }, + options + ); + return xsts; + } else { + const xsts = await this.xbl.getXSTSToken({ userToken }, options); + return xsts; + } + }, + () => { + this.msa.forceRefresh = true; + }, + 2 + ); + } + } + + async getMinecraftJavaToken(options: any = {}) { + const response: any = { token: "", entitlements: {}, profile: {} }; + if (await this.mca.verifyTokens()) { + const { token } = await this.mca.getCachedAccessToken(); + response.token = token; + } else { + await retry( + async () => { + const xsts = await this.getXboxToken(Endpoints.PCXSTSRelyingParty); + response.token = await this.mca.getAccessToken(xsts); + }, + () => { + this.xbl.forceRefresh = true; + }, + 2 + ); + } + + if (options.fetchEntitlements) { + response.entitlements = await this.mca.fetchEntitlements(response.token); + } + if (options.fetchProfile) { + response.profile = await this.mca.fetchProfile(response.token); + } + if (options.fetchCertificates) { + response.certificates = await this.mca.fetchCertificates(response.token); + } + + return response; + } +} diff --git a/src/plugins/EagProxyAAS/auth.ts b/src/plugins/EagProxyAAS/auth.ts index 5160565..7637ce5 100644 --- a/src/plugins/EagProxyAAS/auth.ts +++ b/src/plugins/EagProxyAAS/auth.ts @@ -1,7 +1,7 @@ import { randomUUID } from "crypto"; import EventEmitter from "events"; import pauth from "prismarine-auth"; -import debug from "debug"; +import { CustomAuthflow } from "./CustomAuthflow.js"; const { Authflow, Titles } = pauth; const Enums = PLUGIN_MANAGER.Enums; @@ -34,7 +34,7 @@ class InMemoryCache { export function auth(): EventEmitter { const emitter = new EventEmitter(); const userIdentifier = randomUUID(); - const flow = new Authflow( + const flow = new CustomAuthflow( userIdentifier, ({ username, cacheName }) => new InMemoryCache(), { diff --git a/src/plugins/EagProxyAAS/index.ts b/src/plugins/EagProxyAAS/index.ts index 5a88841..e29223a 100644 --- a/src/plugins/EagProxyAAS/index.ts +++ b/src/plugins/EagProxyAAS/index.ts @@ -2,7 +2,7 @@ import metadata from "./metadata.json" assert { type: "json" }; import { config } from "./config.js"; import { createServer } from "minecraft-protocol"; import { ClientState, ConnectionState, ServerGlobals } from "./types.js"; -import { handleConnect, setSG } from "./utils.js"; +import { handleConnect, hushConsole, setSG } from "./utils.js"; const PluginManager = PLUGIN_MANAGER; @@ -16,6 +16,8 @@ const MineProtocol = PluginManager.MineProtocol; const EaglerSkins = PluginManager.EaglerSkins; const Util = PluginManager.Util; +hushConsole(); + const logger = new Logger("EaglerProxyAAS"); logger.info(`Starting ${metadata.name} v${metadata.version}...`); logger.info( diff --git a/src/plugins/EagProxyAAS/utils.ts b/src/plugins/EagProxyAAS/utils.ts index e9bce0d..0dbbf60 100644 --- a/src/plugins/EagProxyAAS/utils.ts +++ b/src/plugins/EagProxyAAS/utils.ts @@ -23,6 +23,14 @@ const logger = new PLUGIN_MANAGER.Logger("PlayerHandler"); let SERVER: ServerGlobals = null; +export function hushConsole() { + const ignoredMethod = () => {}; + global.console.info = ignoredMethod; + global.console.warn = ignoredMethod; + global.console.error = ignoredMethod; + global.console.debug = ignoredMethod; +} + export function setSG(svr: ServerGlobals) { SERVER = svr; } @@ -479,11 +487,6 @@ export async function onConnect(client: ClientState) { }, ], }); - sendCustomMessage( - client.gameClient, - "Attempting to switch servers, please wait... (if you don't get connected to the target server after a while, the server might not be a Minecraft server at all)", - "gray" - ); const player = PLUGIN_MANAGER.proxy.players.get( client.gameClient.username ); @@ -782,11 +785,6 @@ export async function onConnect(client: ClientState) { }, ], }); - sendCustomMessage( - client.gameClient, - "Attempting to switch servers, please wait... (if you don't get connected to the target server for a while, the server might be online only)", - "gray" - ); const player = PLUGIN_MANAGER.proxy.players.get( client.gameClient.username );