diff --git a/docs/API.md b/docs/API.md index 3ce15de..e82ebaf 100644 --- a/docs/API.md +++ b/docs/API.md @@ -74,7 +74,7 @@ Returns a `Client` instance and perform login. `options` is an object containing the properties : * username * port : default to 25565 - * password : can be omitted (if the tokens and profilesFolder are also omitted then it tries to connect in offline mode) + * password : can be omitted (if the tokens are also omitted then it tries to connect in offline mode) * host : default to localhost * clientToken : generated if a password is given * accessToken : generated if a password is given @@ -92,7 +92,6 @@ Returns a `Client` instance and perform login. * connect : a function taking the client as parameter and that should client.setSocket(socket) and client.emit('connect') when appropriate (see the proxy examples for an example of use) * agent : a http agent that can be used to set proxy settings for yggdrasil authentication (see proxy-agent on npm) - * profilesFolder : the path to the folder that contains your `launcher_profiles.json`. defaults to your minecraft folder if it exists, otherwise the local directory. set to `false` to disable managing profiles ## mc.Client(isServer,version,[customPackets]) diff --git a/examples/client_echo/client_echo.js b/examples/client_echo/client_echo.js index 213f532..971d167 100644 --- a/examples/client_echo/client_echo.js +++ b/examples/client_echo/client_echo.js @@ -1,17 +1,15 @@ const mc = require('minecraft-protocol') -if (process.argv.length !== 4) { - console.log('Usage : node echo.js []') +if (process.argv.length < 4 || process.argv.length > 6) { + console.log('Usage : node echo.js [] []') process.exit(1) } const client = mc.createClient({ host: process.argv[2], port: parseInt(process.argv[3]), - username: process.argv[4] ? process.argv[4] : 'echo' -}) -client.on('error', function (err) { - console.error(err) + username: process.argv[4] ? process.argv[4] : 'echo', + password: process.argv[5] }) client.on('connect', function () { diff --git a/package.json b/package.json index ec8bb9f..a14747a 100644 --- a/package.json +++ b/package.json @@ -44,18 +44,17 @@ }, "dependencies": { "aes-js": "^3.1.2", + "yggdrasil": "^1.3.0", "buffer-equal": "^1.0.0", "debug": "^4.1.0", "endian-toggle": "^0.0.0", "lodash.get": "^4.1.2", "lodash.merge": "^4.3.0", "minecraft-data": "^2.70.0", - "minecraft-folder-path": "^1.1.0", "node-rsa": "^0.4.2", "prismarine-nbt": "^1.3.0", "protodef": "^1.8.0", "readable-stream": "^3.0.6", - "uuid-1345": "^1.0.1", - "yggdrasil": "^1.4.0" + "uuid-1345": "^1.0.1" } } diff --git a/src/client/auth.js b/src/client/auth.js index 58d4738..1837b8a 100644 --- a/src/client/auth.js +++ b/src/client/auth.js @@ -1,98 +1,16 @@ const UUID = require('uuid-1345') const yggdrasil = require('yggdrasil') -const fs = require('fs').promises -const mcDefaultFolderPath = require('minecraft-folder-path') -const path = require('path') - -module.exports = async function (client, options) { - if (!options.profilesFolder && options.profilesFolder !== false) { // not defined, but not explicitly false. fallback to default - let mcFolderExists = true - try { - await fs.access(mcDefaultFolderPath) - } catch (ignoreErr) { - mcFolderExists = false - } - options.profilesFolder = mcFolderExists ? mcDefaultFolderPath : '.' // local folder if mc folder doesn't exist - } +module.exports = function (client, options) { const yggdrasilClient = yggdrasil({ agent: options.agent, host: options.authServer || 'https://authserver.mojang.com' }) - const clientToken = options.clientToken || (options.session && options.session.clientToken) || (options.profilesFolder && (await getLauncherProfiles()).clientToken) || UUID.v4().toString().replace(/-/g, '') + const clientToken = options.clientToken || (options.session && options.session.clientToken) || UUID.v4().toString() const skipValidation = false || options.skipValidation options.accessToken = null - options.haveCredentials = options.password != null || (clientToken != null && options.session != null) || (options.profilesFolder && await hasProfileCredentials()) - - async function getLauncherProfiles () { // get launcher profiles - try { - return JSON.parse(await fs.readFile(path.join(options.profilesFolder, 'launcher_profiles.json'), 'utf8')) - } catch (err) { - await fs.mkdir(options.profilesFolder, { recursive: true }) - await fs.writeFile(path.join(options.profilesFolder, 'launcher_profiles.json'), '{}') - return { authenticationDatabase: {} } - } - } - - async function hasProfileCredentials () { - try { - const auths = await getLauncherProfiles() - - const lowerUsername = options.username.toLowerCase() - return !!Object.keys(auths.authenticationDatabase).find(key => - auths.authenticationDatabase[key].username.toLowerCase() === lowerUsername || - Object.values(auths.authenticationDatabase[key].profiles)[0].displayName.toLowerCase() === lowerUsername - ) - } catch (err) { - return false - } - } + options.haveCredentials = options.password != null || (clientToken != null && options.session != null) if (options.haveCredentials) { // make a request to get the case-correct username before connecting. const cb = function (err, session) { - if (options.profilesFolder) { - getLauncherProfiles().then((auths) => { - try { - const lowerUsername = options.username.toLowerCase() - let profile = Object.keys(auths.authenticationDatabase).find(key => - auths.authenticationDatabase[key].username.toLowerCase() === lowerUsername || - Object.values(auths.authenticationDatabase[key].profiles)[0].displayName.toLowerCase() === lowerUsername - ) - if (err) { - if (profile) { // profile is invalid, remove - delete auths.authenticationDatabase[profile] - } - } else { // successful login - if (!profile) { - profile = UUID.v4().toString().replace(/-/g, '') // create new profile - } - if (!auths.clientToken) { - auths.clientToken = clientToken - } - - if (clientToken === auths.clientToken) { // only do something when we can save a new clienttoken or they match - const oldProfileObj = auths.authenticationDatabase[profile] - const newProfileObj = { - accessToken: session.accessToken, - profiles: {}, - properties: oldProfileObj ? (oldProfileObj.properties || []) : [], - username: options.username - } - newProfileObj.profiles[session.selectedProfile.id] = { - displayName: session.selectedProfile.name - } - auths.authenticationDatabase[profile] = newProfileObj - } - } - } catch (ignoreErr) { - // again, silently fail, just don't save anything - } - fs.writeFile(path.join(options.profilesFolder, 'launcher_profiles.json'), JSON.stringify(auths, null, 2)).then(() => {}, (ignoreErr) => { - // console.warn("Couldn't save tokens:\n", err) // not any error, we just don't save the file - }) - }, (ignoreErr) => { - // console.warn("Skipped saving tokens because of error\n", err) // not any error, we just don't save the file - }) - } - if (err) { client.emit('error', err) } else { @@ -104,38 +22,6 @@ module.exports = async function (client, options) { } } - if (!options.session && options.profilesFolder) { - try { - const auths = await getLauncherProfiles() - - const lowerUsername = options.username.toLowerCase() - const profile = Object.keys(auths.authenticationDatabase).find(key => - auths.authenticationDatabase[key].username.toLowerCase() === lowerUsername || - Object.values(auths.authenticationDatabase[key].profiles)[0].displayName.toLowerCase() === lowerUsername - ) - - if (profile) { - const newUsername = auths.authenticationDatabase[profile].username - const uuid = Object.keys(auths.authenticationDatabase[profile].profiles)[0] - const displayName = auths.authenticationDatabase[profile].profiles[uuid].displayName - const newProfile = { - name: displayName, - id: uuid - } - - options.session = { - accessToken: auths.authenticationDatabase[profile].accessToken, - clientToken: auths.clientToken, - selectedProfile: newProfile, - availableProfiles: [newProfile] - } - options.username = newUsername - } - } catch (ignoreErr) { - // skip the error :/ - } - } - if (options.session) { if (!skipValidation) { yggdrasilClient.validate(options.session.accessToken, function (err) { @@ -147,8 +33,7 @@ module.exports = async function (client, options) { yggdrasilClient.auth({ user: options.username, pass: options.password, - token: clientToken, - requestUser: true + token: clientToken }, cb) } else { cb(err, data)