UNPKG

onelnchr-mc-auth

Version:

Package to authenticate with minecraft. Fork of minecraft-auth by dommilosz which uses my own appID by default.

310 lines 13.4 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.authFlowXBL = exports.authFlowRefresh = exports.authFlow = exports.getMinecraftToken = exports.authXSTS = exports.authXBL = exports.getTokenRefresh = exports.getToken = exports.createUrl = exports.generatePKCEPair = exports.listenForCode = exports.setup = void 0; const http_1 = __importDefault(require("http")); const http_client_methods_1 = require("http-client-methods"); const types_1 = require("../types"); const node_crypto_1 = require("node:crypto"); const dotenv = __importStar(require("dotenv")); dotenv.config(); const appSecret = process.env.MSA_APPSECRET || ""; let config = { scope: "XboxLive.signin offline_access", redirectURL: "http://localhost:2626/token", appID: "055b9745-e08e-4b35-a819-de33bddb4a6d", appSecret: appSecret, mode: "Web", selectAccount: true }; function setup(_config) { config = { ...config, mode: "Web", ..._config }; } exports.setup = setup; async function createServer(serverConfig) { return await new Promise((r, j) => { // @ts-ignore const server = http_1.default.createServer(); let _success = false; server.listen(serverConfig.port, serverConfig.host, function () { if (serverConfig.onstart) { serverConfig.onstart(serverConfig.host, serverConfig.port); } else { console.log(`MS Token Server is running on http://${serverConfig.host}:${serverConfig.port}`); } r(server); }); server.on("close", function () { if (serverConfig.onclose) { serverConfig.onclose(_success); } }); server.on("error", (err) => { j(err); }); server.fullClose = function (success) { _success = success; if (server.abort) { serverConfig.abort?.removeEventListener("abort", server.abort); } if (server.serverTimeout) { clearTimeout(server.serverTimeout); server.serverTimeout = undefined; } server.close(); }; return server; }); } async function _listenForCode(server, serverConfig) { return await new Promise((r, j) => { server.serverTimeout = setTimeout(async () => { server.fullClose(false); j("Timeout error"); }, serverConfig.timeout); if (serverConfig.abort) { server.abort = function () { server.fullClose(false); j("Aborted"); }; if (serverConfig.abort.aborted) { server.abort(); } else { serverConfig.abort.addEventListener("abort", server.abort); } } async function requestListener(req, res) { if (!req.url) return; res.setHeader("Connection", "close"); switch (req.url.split('?')[0]) { case '/token': if (serverConfig.redirectAfterAuth) { res.writeHead(301, { Location: serverConfig.redirectAfterAuth, }); } res.end(); server.fullClose(true); if (req.url.includes('?code')) { let code = req.url.split('?code=')[1]; if (serverConfig.oncode) { serverConfig.oncode(code); } r(code); } if (req.url.includes('?error')) { const error = req.url.split('?error=')[1].split('&')[0]; const error_description = decodeURIComponent(req.url.split('&error_description=')[1]); j(new types_1.AuthenticationError(error, error_description, '')); } break; case '/url': res.writeHead(200); res.end(createUrl(serverConfig.pkcePair)); break; case '/close': res.writeHead(200); res.end(); server.fullClose(false); j("Closed"); break; case '/auth': res.writeHead(302, { Location: createUrl(serverConfig.pkcePair), }); res.end(); break; default: res.writeHead(302, { Location: createUrl(serverConfig.pkcePair), }); res.end(); break; } } server.on('request', requestListener); }); } async function listenForCode(_serverConfig = {}) { const serverConfig = { port: 2626, host: "localhost", timeout: 200 * 1000, redirectAfterAuth: "https://onelauncher.zmito.eu/launcher/afterauth", ..._serverConfig }; const server = await createServer(serverConfig); return await _listenForCode(server, serverConfig); } exports.listenForCode = listenForCode; function generatePKCEPair() { const NUM_OF_BYTES = 32; const HASH_ALG = "sha256"; const randomVerifier = (0, node_crypto_1.randomBytes)(NUM_OF_BYTES).toString('hex'); const hash = (0, node_crypto_1.createHash)(HASH_ALG).update(randomVerifier).digest('base64'); const challenge = hash.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, ''); // Clean base64 to make it URL safe return { verifier: randomVerifier, challenge }; } exports.generatePKCEPair = generatePKCEPair; function createUrl(PKCEPair) { let encodedID = encodeURIComponent(config.appID ?? ""); let encodedUrl = encodeURIComponent(config.redirectURL); let encodedScope = encodeURIComponent(config.scope); let url = `https://login.microsoftonline.com/consumers/oauth2/v2.0/authorize?client_id=${encodedID}&response_type=code&redirect_uri=${encodedUrl}&scope=${encodedScope}`; if (PKCEPair) { let encodedChallenge = encodeURIComponent(PKCEPair.challenge); url += `&code_challenge=${encodedChallenge}&code_challenge_method=S256`; } if (config.selectAccount) { url += `&prompt=select_account`; } return url; } exports.createUrl = createUrl; async function getToken(authCode, PKCEPair) { let encodedID = encodeURIComponent(config.appID); let encodedUrl = encodeURIComponent(config.redirectURL); let url = 'https://login.microsoftonline.com/consumers/oauth2/v2.0/token'; let body = `client_id=${encodedID}&code=${authCode}&grant_type=authorization_code&redirect_uri=${encodedUrl}`; if (config.mode === "Web") { if (!config.appSecret) { throw new types_1.AuthenticationError("App secret was not provided", "App secret was not provided in getToken"); } let encodedSecret = encodeURIComponent(config.appSecret); url = "https://login.live.com/oauth20_token.srf"; body = `client_id=${encodedID}&client_secret=${encodedSecret}&code=${authCode}&grant_type=authorization_code&redirect_uri=${encodedUrl}`; } if (PKCEPair) { let encodedVerifier = encodeURIComponent(PKCEPair.verifier); body += `&code_verifier=${encodedVerifier}&code_challenge_method=S256`; } let headers = { "Content-Type": "application/x-www-form-urlencoded" }; if (config.mode === "SPA") { headers["Origin"] = config.redirectURL; } let response = await (0, http_client_methods_1.HttpPost)(url, body, headers); let jsonResponse = JSON.parse(response); if (jsonResponse.error) { throw new types_1.AuthenticationError(jsonResponse.error, jsonResponse.error_description, jsonResponse.correlation_id); } return jsonResponse; } exports.getToken = getToken; async function getTokenRefresh(refreshToken) { let encodedID = encodeURIComponent(config.appID ?? ""); let encodedUrl = encodeURIComponent(config.redirectURL); let url = 'https://login.live.com/oauth20_token.srf'; let body = `client_id=${encodedID}&refresh_token=${refreshToken}&grant_type=refresh_token&redirect_uri=${encodedUrl}`; if (config.mode === "Web") { if (!config.appSecret) { throw new types_1.AuthenticationError("App secret was not provided", "App secret was not provided in getToken"); } let encodedSecret = encodeURIComponent(config.appSecret); url = "https://login.live.com/oauth20_token.srf"; body = `client_id=${encodedID}&client_secret=${encodedSecret}&refresh_token=${refreshToken}&grant_type=refresh_token&redirect_uri=${encodedUrl}`; } let headers = { "Content-Type": "application/x-www-form-urlencoded" }; if (config.mode === "SPA") { headers["Origin"] = config.redirectURL; } const response = await (0, http_client_methods_1.HttpPost)(url, body, headers); const jsonResponse = JSON.parse(response); if (jsonResponse.error) { throw new types_1.AuthenticationError(jsonResponse.error, jsonResponse.error_description, jsonResponse.correlation_id); } return jsonResponse; } exports.getTokenRefresh = getTokenRefresh; async function authXBL(accessToken) { const body = { Properties: { AuthMethod: 'RPS', SiteName: 'user.auth.xboxlive.com', RpsTicket: `d=${accessToken}`, // your access token from step 2 here }, RelyingParty: 'http://auth.xboxlive.com', TokenType: 'JWT', }; const response = await (0, http_client_methods_1.HttpPost)('https://user.auth.xboxlive.com/user/authenticate', JSON.stringify(body), { 'Content-Type': 'application/json', Accept: 'application/json', }); const jsonResponse = JSON.parse(response); return jsonResponse; } exports.authXBL = authXBL; async function authXSTS(xblToken) { const body = { Properties: { SandboxId: 'RETAIL', UserTokens: [`${xblToken}`], }, RelyingParty: 'rp://api.minecraftservices.com/', TokenType: 'JWT', }; const response = await (0, http_client_methods_1.HttpPost)('https://xsts.auth.xboxlive.com/xsts/authorize', JSON.stringify(body), { 'Content-Type': 'application/json', Accept: 'application/json', }); const jsonResponse = JSON.parse(response); if (jsonResponse.XErr) { throw new types_1.AuthenticationError(String(jsonResponse.XErr), jsonResponse.Message, jsonResponse.Redirect); } return jsonResponse; } exports.authXSTS = authXSTS; async function getMinecraftToken(xstsToken, uhs) { const body = { identityToken: `XBL3.0 x=${uhs};${xstsToken}`, }; const response = await (0, http_client_methods_1.HttpPost)('https://api.minecraftservices.com/authentication/login_with_xbox', JSON.stringify(body), { 'Content-Type': 'application/json', Accept: 'application/json', }); const jsonResponse = JSON.parse(response); if (jsonResponse.errorMessage) { throw new types_1.AuthenticationError("Error when getting minecraft token", jsonResponse.errorMessage, jsonResponse.path); } return jsonResponse; } exports.getMinecraftToken = getMinecraftToken; async function authFlow(authCode, PKCEPair) { const tokenRes = await getToken(authCode, PKCEPair); return await authFlowXBL(tokenRes.access_token, tokenRes.refresh_token); } exports.authFlow = authFlow; async function authFlowRefresh(refresh_token) { const tokenRes = await getTokenRefresh(refresh_token); return await authFlowXBL(tokenRes.access_token, tokenRes.refresh_token); } exports.authFlowRefresh = authFlowRefresh; async function authFlowXBL(token, refresh_token) { const xblRes = await authXBL(token); const xstsRes = await authXSTS(xblRes.Token); const mcToken = await getMinecraftToken(xstsRes.Token, xblRes.DisplayClaims.xui[0].uhs); return { access_token: mcToken.access_token, refresh_token }; } exports.authFlowXBL = authFlowXBL; //# sourceMappingURL=MicrosoftAuth.js.map