@@ -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 ( )
@@ -47,7 +74,7 @@ module.exports = async function (client, options) {
4774
4875 if ( options . haveCredentials ) {
4976 // make a request to get the case-correct username before connecting.
50- const cb = function ( err , session ) {
77+ const cb = async function ( err , session ) {
5178 if ( options . profilesFolder ) {
5279 getLauncherProfiles ( ) . then ( ( auths ) => {
5380 if ( ! auths . accounts ) auths . accounts = [ ]
@@ -104,6 +131,14 @@ module.exports = async function (client, options) {
104131 } else {
105132 client . session = session
106133 client . username = session . selectedProfile . name
134+ if ( ! options . disableChatSigning ) {
135+ try {
136+ const certificates = await fetchCertificates ( session . accessToken )
137+ Object . assign ( client , certificates )
138+ } catch ( e ) {
139+ console . warn ( `Failed to fetch player certificates: ${ e } ` )
140+ }
141+ }
107142 options . accessToken = session . accessToken
108143 client . emit ( 'session' , session )
109144 options . connect ( client )
0 commit comments