mirror of
https://github.com/PrismarineJS/node-minecraft-protocol.git
synced 2024-11-14 19:04:59 -05:00
Integrate Authentication for Microsoft Accounts (#788)
* add node-fetch and @xboxreplay/xboxlive-auth for microsoft/xbox auth * decide which authentication to use based on options; if options.auth === 'microsoft' then use microsoft/xbox auth, else use Yggdrasil until they kill that. * push working auth * commentary * eslint does not like me :( * User-Agent works just fine without version * linting = 95% of development * revert changes to encrypt.js * set haveCredentials to whether or not we have a token. Technically this will always be true so...? * eslint * mod+create: api + example * mod: readme.md
This commit is contained in:
parent
72aef6788c
commit
5d723e9a04
7 changed files with 120 additions and 2 deletions
|
@ -77,7 +77,8 @@ Returns a `Client` instance and perform login.
|
|||
* password : can be omitted (if the tokens and profilesFolder 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
|
||||
* accessToken : generated if a password or microsoft account is given
|
||||
* auth : the type of auth server to use, either 'microsoft' or 'mojang'. default to 'mojang'
|
||||
* authServer : auth server, default to https://authserver.mojang.com
|
||||
* sessionServer : session server, default to https://sessionserver.mojang.com
|
||||
* keepAlive : send keep alive packets : default to true
|
||||
|
|
|
@ -65,6 +65,7 @@ var client = mc.createClient({
|
|||
port: 25565, // optional
|
||||
username: "email@example.com",
|
||||
password: "12345678",
|
||||
auth: 'mojang' // optional; by default uses mojang, if using a microsoft account, set to 'microsoft'
|
||||
});
|
||||
client.on('chat', function(packet) {
|
||||
// Listen for chat messages and echo them back.
|
||||
|
|
32
examples/client_microsoft_auth/client_microsoft_auth.js
Normal file
32
examples/client_microsoft_auth/client_microsoft_auth.js
Normal file
|
@ -0,0 +1,32 @@
|
|||
'use strict'
|
||||
|
||||
const mc = require('minecraft-protocol')
|
||||
|
||||
if (process.argv.length < 4 || process.argv.length > 6) {
|
||||
console.log('Usage : node echo.js <host> <port> [<email>] [<password>]')
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
const client = mc.createClient({
|
||||
host: process.argv[2],
|
||||
port: parseInt(process.argv[3]),
|
||||
username: process.argv[4], // your microsoft account email
|
||||
password: process.argv[5], // your microsoft account password
|
||||
auth: 'microsoft' // This option must be present and set to 'microsoft' to use Microsoft Account Authentication. Failure to do so will result in yggdrasil throwing invalid account information.
|
||||
})
|
||||
|
||||
client.on('connect', function () {
|
||||
console.info('connected')
|
||||
})
|
||||
client.on('disconnect', function (packet) {
|
||||
console.log('disconnected: ' + packet.reason)
|
||||
})
|
||||
client.on('chat', function (packet) {
|
||||
const jsonMsg = JSON.parse(packet.message)
|
||||
if (jsonMsg.translate === 'chat.type.announcement' || jsonMsg.translate === 'chat.type.text') {
|
||||
const username = jsonMsg.with[0].text
|
||||
const msg = jsonMsg.with[1]
|
||||
if (username === client.username) return
|
||||
client.write('chat', { message: msg })
|
||||
}
|
||||
})
|
8
examples/client_microsoft_auth/package.json
Normal file
8
examples/client_microsoft_auth/package.json
Normal file
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"name": "node-minecraft-protocol-example",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
},
|
||||
"description": "A node-minecraft-protocol example"
|
||||
}
|
|
@ -43,6 +43,7 @@
|
|||
"standard": "^16.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@xboxreplay/xboxlive-auth": "^3.3.0",
|
||||
"aes-js": "^3.1.2",
|
||||
"buffer-equal": "^1.0.0",
|
||||
"debug": "^4.1.0",
|
||||
|
@ -51,6 +52,7 @@
|
|||
"lodash.merge": "^4.3.0",
|
||||
"minecraft-data": "^2.70.0",
|
||||
"minecraft-folder-path": "^1.1.0",
|
||||
"node-fetch": "^2.6.1",
|
||||
"node-rsa": "^0.4.2",
|
||||
"prismarine-nbt": "^1.3.0",
|
||||
"protodef": "^1.8.0",
|
||||
|
|
72
src/client/microsoftAuth.js
Normal file
72
src/client/microsoftAuth.js
Normal file
|
@ -0,0 +1,72 @@
|
|||
const XboxLiveAuth = require('@xboxreplay/xboxlive-auth')
|
||||
const fetch = require('node-fetch')
|
||||
|
||||
const XSTSRelyingParty = 'rp://api.minecraftservices.com/'
|
||||
const MinecraftServicesLogWithXbox = 'https://api.minecraftservices.com/authentication/login_with_xbox'
|
||||
const MinecraftServicesEntitlement = 'https://api.minecraftservices.com/entitlements/mcstore'
|
||||
const MinecraftServicesProfile = 'https://api.minecraftservices.com/minecraft/profile'
|
||||
|
||||
const getFetchOptions = {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'User-Agent': 'node-minecraft-protocol'
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Authenticates with Xbox Live, then Authenticates with Minecraft, Checks Entitlements and Gets Profile.
|
||||
* @function
|
||||
* @param {object} client - The client passed to protocol
|
||||
* @param {object} options - Client Options
|
||||
*/
|
||||
|
||||
module.exports = async (client, options) => {
|
||||
// Use external library to authenticate with
|
||||
const XAuthResponse = await XboxLiveAuth.authenticate(options.username, options.password, { XSTSRelyingParty })
|
||||
.catch((err) => {
|
||||
if (err.details) throw new Error(`Unable to authenticate with Xbox Live: ${JSON.stringify(err.details)}`)
|
||||
else throw Error(err)
|
||||
})
|
||||
|
||||
const MineServicesResponse = await fetch(MinecraftServicesLogWithXbox, {
|
||||
method: 'post',
|
||||
...getFetchOptions,
|
||||
body: JSON.stringify({ identityToken: `XBL3.0 x=${XAuthResponse.userHash};${XAuthResponse.XSTSToken}` })
|
||||
}).then(checkStatus)
|
||||
|
||||
options.haveCredentials = MineServicesResponse.access_token != null
|
||||
|
||||
getFetchOptions.headers.Authorization = `Bearer ${MineServicesResponse.access_token}`
|
||||
const MineEntitlements = await fetch(MinecraftServicesEntitlement, getFetchOptions).then(checkStatus)
|
||||
if (MineEntitlements.items.length === 0) throw Error('This user does not have any items on its accounts according to minecraft services.')
|
||||
|
||||
const MinecraftProfile = await fetch(MinecraftServicesProfile, getFetchOptions).then(checkStatus)
|
||||
if (!MinecraftProfile.id) throw Error('This user does not own minecraft according to minecraft services.')
|
||||
|
||||
// This profile / session here could be simplified down to where it just passes the uuid of the player to encrypt.js
|
||||
// That way you could remove some lines of code. It accesses client.session.selectedProfile.id so /shrug.
|
||||
// - Kashalls
|
||||
const profile = {
|
||||
name: MinecraftProfile.name,
|
||||
id: MinecraftProfile.id
|
||||
}
|
||||
|
||||
const session = {
|
||||
accessToken: MineServicesResponse.access_token,
|
||||
selectedProfile: profile,
|
||||
availableProfile: [profile]
|
||||
}
|
||||
client.session = session
|
||||
client.username = MinecraftProfile.name
|
||||
options.accessToken = MineServicesResponse.access_token
|
||||
client.emit('session', session)
|
||||
options.connect(client)
|
||||
}
|
||||
|
||||
function checkStatus (res) {
|
||||
if (res.ok) { // res.status >= 200 && res.status < 300
|
||||
return res.json()
|
||||
} else {
|
||||
throw Error(res.statusText)
|
||||
}
|
||||
}
|
|
@ -7,6 +7,7 @@ const encrypt = require('./client/encrypt')
|
|||
const keepalive = require('./client/keepalive')
|
||||
const compress = require('./client/compress')
|
||||
const auth = require('./client/auth')
|
||||
const microsoftAuth = require('./client/microsoftAuth')
|
||||
const setProtocol = require('./client/setProtocol')
|
||||
const play = require('./client/play')
|
||||
const tcpDns = require('./client/tcp_dns')
|
||||
|
@ -33,7 +34,8 @@ function createClient (options) {
|
|||
const client = new Client(false, version.minecraftVersion, options.customPackets, hideErrors)
|
||||
|
||||
tcpDns(client, options)
|
||||
auth(client, options)
|
||||
if (options.auth === 'microsoft') microsoftAuth(client, options)
|
||||
else auth(client, options)
|
||||
if (options.version === false) autoVersion(client, options)
|
||||
setProtocol(client, options)
|
||||
keepalive(client, options)
|
||||
|
|
Loading…
Reference in a new issue