Last active
October 22, 2023 21:20
-
-
Save jonbarrow/5fec3abebff2a2613851a6ab2864d543 to your computer and use it in GitHub Desktop.
Discord remote login script example
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
Discord is able to log users in by scanning QR codes on the login screen, | |
these are randomly generated by the server and are sent to the client | |
over a secure web socket as a fingerprint, which gets scanned in by the app | |
and then used to login remotely. The QR codes actual contents are just | |
https://discord.com/ra/FINGERPRINT | |
This script shows how to connect to Discord and generate these | |
fingerprints automatically | |
*/ | |
const crypto = require('crypto'); | |
const WebSocket = require('ws'); | |
const remoteAuthGatewayUrl = 'wss://remote-auth-gateway.discord.gg/?v=1'; | |
// Generate a new random RSA key pair for the connection | |
const rsaKeyPair = crypto.generateKeyPairSync('rsa', { | |
modulusLength: 2048 | |
}); | |
// Export the public key as SPKI | |
const publicKey = rsaKeyPair.publicKey.export({ | |
type: 'spki', | |
format: 'der' | |
}); | |
// Connect to gateway, 'Origin' header must be set or connection refused | |
const socket = new WebSocket(remoteAuthGatewayUrl, [], { | |
headers: { | |
'Origin': 'https://discord.com', | |
} | |
}); | |
socket.on('error', error => { | |
console.log(error); | |
}); | |
socket.on('close', (code, reason) => { | |
console.log(reason.toString()); | |
}); | |
socket.on('message', handleMessage); | |
function handleMessage(data) { | |
const message = JSON.parse(data.toString()); | |
switch (message.op) { | |
// Server has accepted our connection request | |
// Initialize fingerprint request | |
case 'hello': | |
// Send the server our public key | |
socket.send(JSON.stringify({ | |
op: 'init', | |
encoded_public_key: publicKey.toString('base64') | |
})); | |
break; | |
// Server has accepted the public key and encrypted | |
// a proof of identity check with it. Decrypt the | |
// nonce and create the proof to prove it was our | |
// public key used | |
case 'nonce_proof': | |
const encryptedNonce = Buffer.from(message.encrypted_nonce, 'base64'); | |
const decryptedNonce = crypto.privateDecrypt({ | |
key: rsaKeyPair.privateKey, | |
oaepHash: 'sha256' | |
}, encryptedNonce); | |
// Proof is the SHA256 hash of the decrypted nonce | |
// as base64 with a different alphabet | |
const hash = crypto.createHash('sha256').update(decryptedNonce).digest('base64'); | |
const proof = hash.replace(/\//g, '_').replace(/\+/g, '-').replace(/={1,2}$/, ''); | |
// Send the server our proof | |
socket.send(JSON.stringify({ | |
op: 'nonce_proof', | |
proof | |
})); | |
break; | |
// Server has accepted our proof and sent the new QR | |
// fingerprint | |
case 'pending_remote_init': | |
// Grab the fingerprint and close the socket | |
const { fingerprint } = message; | |
console.log(fingerprint); | |
socket.close(); | |
break; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
This script shows how to use the fingerprint to login remotely using the | |
Discord API | |
*/ | |
const got = require('got'); | |
const token = 'USER AUTH TOKEN'; | |
const fingerprint = 'FINGER PRINT FROM WEB SOCKET CONNECTION'; | |
const urlRemoteAuth = 'https://discord.com/api/v9/users/@me/remote-auth'; | |
const urlRemoteAuthFinish = 'https://discord.com/api/v9/users/@me/remote-auth/finish'; | |
async function main() { | |
// Send the server the fingerprint to request a handshake token | |
const response = await got.post(urlRemoteAuth, { | |
headers: { | |
authorization: token | |
}, | |
json: { | |
fingerprint | |
} | |
}).json(); | |
const handshakeToken = response.handshake_token; | |
// Confirm the login request by sending the server back the handshake token. | |
// The user should not be logged in remotely | |
await got.post(urlRemoteAuthFinish, { | |
headers: { | |
authorization: token | |
}, | |
json: { | |
handshake_token: handshakeToken, | |
temporary_token: false | |
} | |
}); | |
} | |
main(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment