feat: cleaner auth state management

1. removes the "auth-state.update" event since it was bloated and fairly unnecessary
2. adds "creds.update" event that only sends the updated properties of the auth credentials
3. ensure the auth creds are not mutated anywhere, but only with the "creds.update" event
4. Separates the signal credentials from the auth credentials (kinda in progress)
5. in memory key store requires a save function to let one know when to save the keys

!BREAKING_CHANGE
This commit is contained in:
Adhiraj Singh
2021-11-20 16:21:56 +05:30
parent c2c27a5dea
commit 3d0704a317
13 changed files with 229 additions and 201 deletions

View File

@@ -4,10 +4,11 @@ import { promisify } from "util"
import WebSocket from "ws"
import { randomBytes } from 'crypto'
import { proto } from '../../WAProto'
import { DisconnectReason, SocketConfig, BaileysEventEmitter, ConnectionState } from "../Types"
import { Curve, initAuthState, generateRegistrationNode, configureSuccessfulPairing, generateLoginNode, encodeBigEndian, promiseTimeout, generateOrGetPreKeys, xmppSignedPreKey, xmppPreKey, getPreKeys, makeNoiseHandler } from "../Utils"
import { DisconnectReason, SocketConfig, BaileysEventEmitter, ConnectionState, AuthenticationCreds } from "../Types"
import { Curve, generateRegistrationNode, configureSuccessfulPairing, generateLoginNode, encodeBigEndian, promiseTimeout, generateOrGetPreKeys, xmppSignedPreKey, xmppPreKey, getPreKeys, makeNoiseHandler, useSingleFileAuthState } from "../Utils"
import { DEFAULT_ORIGIN, DEF_TAG_PREFIX, DEF_CALLBACK_PREFIX, KEY_BUNDLE_TYPE } from "../Defaults"
import { assertNodeErrorFree, BinaryNode, encodeBinaryNode, S_WHATSAPP_NET, getBinaryNodeChild } from '../WABinary'
/**
* Connects to WA servers and performs:
* - simple queries (no retry mechanism, wait for connection establishment)
@@ -39,13 +40,22 @@ export const makeSocket = ({
}
})
ws.setMaxListeners(0)
const ev = new EventEmitter() as BaileysEventEmitter
/** ephemeral key pair used to encrypt/decrypt communication. Unique for each connection */
const ephemeralKeyPair = Curve.generateKeyPair()
/** WA noise protocol wrapper */
const noise = makeNoiseHandler(ephemeralKeyPair)
const authState = initialAuthState || initAuthState()
let authState = initialAuthState
if(!authState) {
authState = useSingleFileAuthState('./auth-info-multi.json').state
logger.warn(`
Baileys just created a single file state for your credentials.
This will not be supported soon.
Please pass the credentials in the config itself
`)
}
const { creds } = authState
const ev = new EventEmitter() as BaileysEventEmitter
let lastDateRecv: Date
let epoch = 0
@@ -174,11 +184,16 @@ export const makeSocket = ({
}
/** get some pre-keys and do something with them */
const assertingPreKeys = async(range: number, execute: (keys: { [_: number]: any }) => Promise<void>) => {
const { newPreKeys, lastPreKeyId, preKeysRange } = generateOrGetPreKeys(authState, range)
creds.serverHasPreKeys = true
creds.nextPreKeyId = Math.max(lastPreKeyId+1, creds.nextPreKeyId)
creds.firstUnuploadedPreKeyId = Math.max(creds.firstUnuploadedPreKeyId, lastPreKeyId+1)
const { newPreKeys, lastPreKeyId, preKeysRange } = generateOrGetPreKeys(authState.creds, range)
const update: Partial<AuthenticationCreds> = {
nextPreKeyId: Math.max(lastPreKeyId+1, creds.nextPreKeyId),
firstUnuploadedPreKeyId: Math.max(creds.firstUnuploadedPreKeyId, lastPreKeyId+1)
}
if(!creds.serverHasPreKeys) {
update.serverHasPreKeys = true
}
await Promise.all(
Object.keys(newPreKeys).map(k => authState.keys.setPreKey(+k, newPreKeys[+k]))
)
@@ -186,7 +201,7 @@ export const makeSocket = ({
const preKeys = await getPreKeys(authState.keys, preKeysRange[0], preKeysRange[0] + preKeysRange[1])
await execute(preKeys)
ev.emit('auth-state.update', authState)
ev.emit('creds.update', update)
}
/** generates and uploads a set of pre-keys */
const uploadPreKeys = async() => {
@@ -464,10 +479,10 @@ export const makeSocket = ({
throw new Boom('Authentication failed', { statusCode: +(value.attrs.code || 500) })
}
}
Object.assign(creds, updatedCreds)
logger.info({ jid: creds.me!.id }, 'registered connection, restart server')
ev.emit('auth-state.update', authState)
logger.info({ jid: updatedCreds.me!.id }, 'registered connection, restart server')
ev.emit('creds.update', updatedCreds)
ev.emit('connection.update', { isNewLogin: true, qr: undefined })
end(new Boom('Restart Required', { statusCode: DisconnectReason.restartRequired }))
@@ -512,6 +527,8 @@ export const makeSocket = ({
process.nextTick(() => {
ev.emit('connection.update', { connection: 'connecting', receivedPendingNotifications: false, qr: undefined })
})
// update credentials when required
ev.on('creds.update', update => Object.assign(creds, update))
return {
ws,