Documentation
Sign inGet started

Authorizing channels

Public channels are open: any client holding the app key can subscribe. Two channel-name prefixes change that, and the prefix is the whole switch. There is nothing to configure per channel.
A channel named private-… requires your backend to approve each subscription. A channel named presence-… does the same and also attaches an identity to the subscriber, so everyone on the channel can see who else is there. Any other name is public.
Approval works this way because only your backend holds the app secret. The client asks your server to sign a specific subscription, your server decides whether that user may subscribe, and the edge verifies the signature before letting the client in. The secret never reaches the browser.

Point the client at your endpoint

Give the client an authEndpoint on your own backend:
कोड उदाहरण
import { BirdRealtime } from "@messagebird/realtime";

const bird = new BirdRealtime({
  appKey: "your-app-key",
  region: "us1",
  authEndpoint: "/bird/auth",
});

const room = bird.subscribe("presence-room-1");
The client calls that endpoint whenever it subscribes to a private or presence channel, including after a reconnect. A subscription is re-authorized against the new connection, so authorization is per connection rather than once per session.
The endpoint is same-origin by default. A cross-origin authEndpoint requires allowCrossOriginAuth: true, and any authHeaders you configure are sent only to a same-origin endpoint, so cookies and tokens are never handed to another origin by accident.

What your endpoint receives and returns

The client POSTs JSON:
कोड उदाहरण
{ "connection_id": "26896.319537", "channel_name": "presence-room-1" }
Respond with the signature:
कोड उदाहरण
{ "auth": "your-app-key:8f9a…" }
For a presence channel, also return the member identity as a JSON string, the same string you signed:
कोड उदाहरण
{
  "auth": "your-app-key:8f9a…",
  "member_data": "{\"member_id\":\"u_42\",\"member_info\":{\"name\":\"Ada\"}}"
}
member_id is the identity other members see, and the value disconnect acts on. member_info is optional free-form data that rides along to every member of the channel, so keep it small and non-sensitive.
This is where authorization actually happens. Your endpoint has the session cookie or bearer token of the caller, so it decides whether this user may join this channel, and which identity they get. Return a 403 and the client's subscription fails.

The string you sign

Concatenate with colons, then HMAC-SHA256 with the app secret and hex-encode. Prefix the result with the app key and a colon.
Channel typeString to sign
private-…<connection_id>:<channel_name>
presence-…<connection_id>:<channel_name>:<member_data>
For presence channels, member_data in the signed string must be byte-identical to the member_data you return. Sign the exact string you send, never a re-serialized copy of the same object, because key order or spacing changes the signature.
Node:
कोड उदाहरण
import { createHmac } from "node:crypto";

app.post("/bird/auth", (req, res) => {
  const { connection_id, channel_name } = req.body;

  // Your own authorization decision goes here.
  const user = getUserFromSession(req);
  if (!user || !mayJoin(user, channel_name)) return res.sendStatus(403);

  const key = process.env.BIRD_REALTIME_KEY;
  const secret = process.env.BIRD_REALTIME_SECRET;

  if (channel_name.startsWith("presence-")) {
    const memberData = JSON.stringify({
      member_id: user.id,
      member_info: { name: user.name },
    });
    const sig = createHmac("sha256", secret)
      .update(`${connection_id}:${channel_name}:${memberData}`)
      .digest("hex");
    return res.json({ auth: `${key}:${sig}`, member_data: memberData });
  }

  const sig = createHmac("sha256", secret).update(`${connection_id}:${channel_name}`).digest("hex");
  res.json({ auth: `${key}:${sig}` });
});
Python:
कोड उदाहरण
import hmac, hashlib, json, os

def authorize(connection_id: str, channel_name: str, user) -> dict:
    key = os.environ["BIRD_REALTIME_KEY"]
    secret = os.environ["BIRD_REALTIME_SECRET"].encode()

    if channel_name.startswith("presence-"):
        member_data = json.dumps({"member_id": user.id, "member_info": {"name": user.name}})
        to_sign = f"{connection_id}:{channel_name}:{member_data}"
        sig = hmac.new(secret, to_sign.encode(), hashlib.sha256).hexdigest()
        return {"auth": f"{key}:{sig}", "member_data": member_data}

    to_sign = f"{connection_id}:{channel_name}"
    sig = hmac.new(secret, to_sign.encode(), hashlib.sha256).hexdigest()
    return {"auth": f"{key}:{sig}"}

Members and connections are not the same thing

A member is an identity. A connection is one open WebSocket. Someone with your app open in three tabs is one member with three connections, and presence events reflect the member rather than the connection. member_added fires when that person's first tab subscribes, and member_removed only when the last one leaves. Tabs in between change the channel's connection count without producing a member event.

Common failures

A rejected subscription arrives as an error on the client. These are the four causes worth knowing:
  • Invalid signature. The string you signed doesn't match. Almost always a re-serialized member_data, or a signature computed over the channel name without its private- or presence- prefix.
  • Invalid key. The key in auth belongs to a different app, or it has been revoked. Rotating keys means updating both the client's appKey and the secret your endpoint signs with.
  • Missing member data. A presence subscription arrived without member_data. Presence channels cannot be joined anonymously.
  • A 403 from your own endpoint. Your authorization decision refused, which is the intended outcome for a user who may not join.

Next steps