Terminating member connections
Channel authorization decides who may subscribe. It does not tell the edge who a connection belongs to, so there is nothing to aim at when you need that person gone: a signed-out user keeps their socket, and keeps receiving events on channels they were already admitted to.
Signing in fixes that. A signed-in connection carries a member_id your backend assigned, and one API call closes every connection that member holds on the app.
Use it when a session ends outside the tab that owns it: a sign-out elsewhere, a revoked token, a ban, a password change, a seat removed from a workspace.
1. Sign the connection in
Point the client at an endpoint on your own backend and call signin() once:
Exemplo de código
import { BirdRealtime } from "@messagebird/realtime";
const bird = new BirdRealtime({
appKey: "your-app-key",
region: "us1",
memberAuthEndpoint: "/bird/auth/member",
});
const member = await bird.signin();
console.log("signed in as", member.member_id);The identity belongs to the connection, not to the page, so the client signs in again automatically after a reconnect. Call signin() once and let it handle the rest.
Signing in is independent of subscribing. A signed-in connection still authorizes each private or presence subscription the usual way, and a connection that never signs in can still subscribe to everything it is authorized for. See Authorizing channels.
2. Sign the identity from your backend
The client POSTs the connection id:
Exemplo de código
{ "connection_id": "26896.319537" }Your endpoint answers with the identity as a JSON string, plus a signature over that exact string:
Exemplo de código
{
"auth": "your-app-key:8f9a…",
"member_data": "{\"member_id\":\"u_42\",\"member_info\":{\"name\":\"Ada\"}}"
}The string to sign is <connection_id>::member::<member_data>, with two colons on each side of member. HMAC-SHA256 it with the app secret, hex-encode, and prefix the app key:
Exemplo de código
import { createHmac } from "node:crypto";
app.post("/bird/auth/member", (req, res) => {
const { connection_id } = req.body;
// Your own decision: who is this caller, and may they connect at all?
const user = getUserFromSession(req);
if (!user) return res.sendStatus(403);
const memberData = JSON.stringify({
member_id: user.id,
member_info: { name: user.name },
});
const sig = createHmac("sha256", process.env.BIRD_REALTIME_SECRET)
.update(`${connection_id}::member::${memberData}`)
.digest("hex");
res.json({ auth: `${process.env.BIRD_REALTIME_KEY}:${sig}`, member_data: memberData });
});Python:
Exemplo de código
import hmac, hashlib, json, os
def sign_in(connection_id: str, user) -> dict:
key = os.environ["BIRD_REALTIME_KEY"]
secret = os.environ["BIRD_REALTIME_SECRET"].encode()
member_data = json.dumps({"member_id": user.id, "member_info": {"name": user.name}})
to_sign = f"{connection_id}::member::{member_data}"
sig = hmac.new(secret, to_sign.encode(), hashlib.sha256).hexdigest()
return {"auth": f"{key}:{sig}", "member_data": member_data}Sign the exact string you return. A re-serialized copy of the same object can differ in key order or spacing, and the signature is over bytes.
member_id is the value disconnect acts on, so use the identifier your own system already has for that person, as long as it fits: up to 64 characters of letters, digits, and _ - = @ , . ; and nothing else. That covers most user ids and plain email addresses, but not a + tag, a colon, or anything with a space, so map those to a safe form (a hash, or your internal numeric id) before signing. A member id outside the set is rejected at signin, which leaves the connection anonymous and nothing for disconnect to act on.
member_info is optional and, unlike on a presence channel, it stays between the client and the edge.
The string signed here is deliberately not the string a presence subscription signs, so a presence authorization can never be replayed to claim an identity, even though both payloads are called member_data.
3. Disconnect the member
Exemplo de código
await bird.realtime.members.disconnect("rap_01krdgeqcxet5s7t44vh8rt9mg", "u_42");Exemplo de código
curl -X POST \
https://us1.platform.bird.com/v1/realtime/apps/rap_01krdgeqcxet5s7t44vh8rt9mg/members/u_42/disconnect \
-H "Authorization: Bearer $BIRD_API_KEY" \
-H "X-Realtime-Key: $BIRD_REALTIME_KEY" \
-H "X-Realtime-Secret: $BIRD_REALTIME_SECRET"Every connection that member holds on the app closes, wherever it is: other tabs, other devices, other networks. Connections that never signed in are unaffected, and so is a different member on the same channel.
What the client sees
The connection closes with code 4009 and does not reconnect. A terminated connection is refused rather than dropped, so the automatic reconnection that follows a network blip does not apply here:
Exemplo de código
bird.connection.bind("error", (e) => {
if (e.code === 4009) showSignedOutScreen();
});Reacting is your app's decision. Showing a signed-out state is usually right. Calling bird.connect() again works, but the new connection signs in through your endpoint, so it is worth doing only if you expect that endpoint to refuse the caller now.
A signin that fails for any other reason is reported separately, because it is not a connection failure: the socket stays up, it simply has no identity. That matters most after a reconnect, where the client re-signs in on its own and there is no promise left to reject.
Exemplo de código
bird.connection.bind("signin_error", (e) => {
console.warn("connection has no identity:", e.message);
});While a connection is in that state it still receives events on every channel it is authorized for, but the API cannot address or disconnect it. bird.signedInMember is null whenever that is the case.
Disconnecting is not banning
disconnect closes the connections that exist. It does not remember the member, and nothing stops the same browser from connecting again a second later.
Your member auth endpoint is the gate. Revoke the session (or record the ban) first, then disconnect, so the reconnect that follows is refused with a 403 instead of handed a fresh identity.
Next steps
- Authorizing channels covers the other signature your backend computes, for private and presence subscriptions.
- Disconnect a member is the full API reference for the request.
- Presence channels explain why one member can hold several connections.