Authentication
The onBind hook — async credential checks with inbound PDUs held.
Every bind (bind_transmitter, bind_receiver, bind_transceiver) flows through the
onBind hook. Its return value decides the response:
| Return | Response |
|---|---|
true or undefined | ESME_ROK — session becomes bound |
false | ESME_RBINDFAIL |
a CommandStatus number | that exact status (e.g. ESME_RINVSYSID, ESME_RINVPASWD) |
| the promise rejects | ESME_RBINDFAIL (and the session emits error) |
import { CommandStatus } from "@better-smpp/core";
import { SmppServer } from "@better-smpp/server";
const server = new SmppServer({
systemId: "MYSMSC",
interfaceVersion: 0x50,
onBind: async (session, pdu) => {
const account = await accounts.find(pdu.systemId);
if (!account) return CommandStatus.ESME_RINVSYSID;
if (!(await account.verify(pdu.password))) return CommandStatus.ESME_RINVPASWD;
if (!account.allows(session.remoteAddress)) return false;
return true;
},
});The hook can be slow — safely
While onBind is pending, inbound PDU processing is paused: a client that
optimistically pipelines submit_sm right behind its bind won't get anything processed
(or lost) until authentication finishes. The original node-smpp made you orchestrate
this manually with pause()/resume(); here it's built in.
What the hook can see
The pdu argument is the fully-typed bind request: systemId, password,
systemType, interfaceVersion, addrTon, addrNpi, addressRange. The session
argument gives you transport context before accepting:
session.remoteAddress; // real client IP (PROXY-protocol aware)
session.secure; // TLS?After a successful bind
- The response carries the server's
systemId(andscInterfaceVersionwheninterfaceVersionis configured). session.systemIdandsession.mode("transmitter" | "receiver" | "transceiver") are set, andsession.sessionStatereflects the direction — note it's the server's perspective: a client bound as transmitter puts the server session inBOUND_RX.- The session emits
boundwith the state and the bind PDU:
server.on("session", (session) => {
session.on("bound", (state, pdu) => {
log.info(`${pdu.systemId} bound as ${session.mode} from ${session.remoteAddress}`);
});
});Re-binding an already-bound session is rejected automatically with ESME_RALYBND.