@@ -3,6 +3,7 @@ const yggdrasil = require('yggdrasil')
33const fs = require ( 'fs' ) . promises
44const mcDefaultFolderPath = require ( 'minecraft-folder-path' )
55const path = require ( 'path' )
6+ const crypto = require ( 'crypto' )
67
78const launcherDataFile = 'launcher_accounts.json'
89
@@ -33,6 +34,32 @@ module.exports = async function (client, options) {
3334 }
3435 }
3536
37+ // Adapted from https://github.com/PrismarineJS/prismarine-auth/blob/1aef6e1387d94fca839f2811d17ac6659ae556b4/src/TokenManagers/MinecraftJavaTokenManager.js#L101
38+ const toDER = pem => pem . split ( '\n' ) . slice ( 1 , - 1 ) . reduce ( ( acc , cur ) => Buffer . concat ( [ acc , Buffer . from ( cur , 'base64' ) ] ) , Buffer . alloc ( 0 ) )
39+ async function fetchCertificates ( accessToken ) {
40+ const servicesServer = options . servicesServer ?? 'https://api.minecraftservices.com'
41+ const headers = {
42+ 'Content-Type' : 'application/json' ,
43+ Authorization : `Bearer ${ accessToken } `
44+ }
45+ const res = await fetch ( `${ servicesServer } /player/certificates` , { headers, method : 'post' } )
46+ if ( ! res . ok ) throw Error ( `Certificates request returned status ${ res . status } ` )
47+ const cert = await res . json ( )
48+ const profileKeys = {
49+ publicPEM : cert . keyPair . publicKey ,
50+ privatePEM : cert . keyPair . privateKey ,
51+ publicDER : toDER ( cert . keyPair . publicKey ) ,
52+ privateDER : toDER ( cert . keyPair . privateKey ) ,
53+ signature : Buffer . from ( cert . publicKeySignature , 'base64' ) ,
54+ signatureV2 : Buffer . from ( cert . publicKeySignatureV2 , 'base64' ) ,
55+ expiresOn : new Date ( cert . expiresAt ) ,
56+ refreshAfter : new Date ( cert . refreshedAfter )
57+ }
58+ profileKeys . public = crypto . createPublicKey ( { key : profileKeys . publicDER , format : 'der' , type : 'spki' } )
59+ profileKeys . private = crypto . createPrivateKey ( { key : profileKeys . privateDER , format : 'der' , type : 'pkcs8' } )
60+ return { profileKeys }
61+ }
62+
3663 function getProfileId ( auths ) {
3764 try {
3865 const lowerUsername = options . username . toLowerCase ( )
@@ -105,8 +132,25 @@ module.exports = async function (client, options) {
105132 client . session = session
106133 client . username = session . selectedProfile . name
107134 options . accessToken = session . accessToken
108- client . emit ( 'session' , session )
109- options . connect ( client )
135+
136+ const finishCb = function ( certificates ) {
137+ if ( certificates !== null ) {
138+ Object . assign ( client , certificates )
139+ }
140+ client . emit ( 'session' , session )
141+ options . connect ( client )
142+ }
143+
144+ if ( options . disableChatSigning ) {
145+ finishCb ( null )
146+ } else {
147+ fetchCertificates ( session . accessToken )
148+ . catch ( e => {
149+ console . warn ( `Failed to fetch player certificates: ${ e } ` )
150+ return null
151+ } )
152+ . then ( finishCb )
153+ }
110154 }
111155 }
112156
0 commit comments