Autorizando canais
Qualquer cliente que possua a chave do app pode se inscrever em canais públicos. Dois prefixos de nome de canal exigem que o seu backend autorize a inscrição. Você não configura canais separadamente.
Um canal chamado private-… exige que o seu backend aprove cada inscrição. Um canal chamado presence-… faz o mesmo e também associa uma identidade ao inscrito, para que todos no canal possam ver quem mais está lá. Qualquer outro nome é público.
Apenas o seu backend possui o segredo do app. O cliente pede ao seu servidor para assinar uma inscrição específica, e o edge do Realtime verifica essa assinatura antes de aceitá-la. Seu servidor decide se o chamador pode se inscrever sem expor o segredo ao cliente.
Aponte o cliente para o seu endpoint
Forneça ao cliente um authEndpoint no seu próprio 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");import BirdRealtime
let bird = BirdRealtime(options: .init(
appKey: "your-app-key",
region: "us1",
authEndpoint: URL(string: "https://your-backend.example.com/bird/auth")
))
let room = bird.subscribe("presence-room-1")import com.bird.realtime.BirdRealtime
import com.bird.realtime.BirdRealtimeOptions
val bird = BirdRealtime(
BirdRealtimeOptions(
appKey = "your-app-key",
region = "us1",
authEndpoint = "https://your-backend.example.com/bird/auth",
)
)
val room = bird.subscribe("presence-room-1")O cliente chama esse endpoint para cada inscrição privada ou de presença, incluindo inscrições restauradas após uma reconexão. A autorização se aplica a uma conexão porque a assinatura inclui o ID de conexão dela.
O cliente de navegador exige um endpoint de mesma origem por padrão. Defina allowCrossOriginAuth: true para usar um authEndpoint de origem cruzada. O cliente de navegador envia authHeaders configurados apenas para endpoints de mesma origem.
O que o seu endpoint recebe e retorna
O cliente envia via POST JSON:
Exemplo de código
{ "connection_id": "26896.319537", "channel_name": "presence-room-1" }Responda com a assinatura:
Exemplo de código
{ "auth": "your-app-key:8f9a…" }Para um canal de presença, retorne também a identidade do membro como uma string JSON, a mesma string que você assinou:
Exemplo de código
{
"auth": "your-app-key:8f9a…",
"member_data": "{\"member_id\":\"u_42\",\"member_info\":{\"name\":\"Ada\"}}"
}member_id é a identidade que os outros membros veem e o valor que a operação de desconexão utiliza. member_info são dados JSON opcionais entregues a cada membro do canal. Tem um limite de 1 KB, então inclua apenas dados de perfil pequenos e não sensíveis.
Autorize o chamador nesse endpoint usando o cookie de sessão ou o token bearer dele. Retorne 403 Forbidden quando o chamador não puder entrar no canal. Para canais de presença, atribua a identidade na mesma resposta.
A string que você assina
Concatene com dois-pontos, depois aplique HMAC-SHA256 com o segredo do app e codifique em hexadecimal. Prefixe o resultado com a chave do app e dois-pontos.
| Tipo de canal | String a assinar |
|---|---|
| private-… | <connection_id>:<channel_name> |
| private-encrypted-… | <connection_id>:<channel_name> |
| presence-… | <connection_id>:<channel_name>:<member_data> |
Para canais de presença, assine exatamente a string member_data que você retorna. Resserializar o mesmo objeto pode alterar a ordem das chaves ou o espaçamento e invalidar a assinatura.
Um canal criptografado assina como um privado, e a resposta de autenticação dele também retorna a chave de descriptografia do canal como shared_secret. O helper SDK adiciona isso automaticamente; Canais criptografados cobre a derivação e o comportamento do canal.
Cada SDK de servidor fornece um helper authorizeChannel. Ele assina com as credenciais configuradas do app e retorna o corpo da resposta sem fazer uma requisição de rede. Para canais criptografados, o helper também adiciona shared_secret.
app.post("/bird/auth", async (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 memberData = channel_name.startsWith("presence-")
? JSON.stringify({ member_id: user.id, member_info: { name: user.name } })
: undefined;
res.json(
await bird.realtime.authorizeChannel({
connectionId: connection_id,
channelName: channel_name,
memberData,
}),
);
});import json
@app.post("/bird/auth")
def bird_auth():
body = request.get_json()
channel_name = body["channel_name"]
# Your own authorization decision goes here.
user = get_user_from_session()
if user is None or not may_join(user, channel_name):
abort(403)
member_data = None
if channel_name.startswith("presence-"):
member_data = json.dumps({"member_id": user.id, "member_info": {"name": user.name}})
return client.realtime.authorize_channel(
connection_id=body["connection_id"],
channel_name=channel_name,
member_data=member_data,
)func birdAuth(w http.ResponseWriter, r *http.Request) {
var body struct {
ConnectionID string `json:"connection_id"`
ChannelName string `json:"channel_name"`
}
if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
http.Error(w, "bad request", http.StatusBadRequest)
return
}
// Your own authorization decision goes here.
user, ok := userFromSession(r)
if !ok || !mayJoin(user, body.ChannelName) {
http.Error(w, "forbidden", http.StatusForbidden)
return
}
params := bird.RealtimeChannelAuthorizationParams{
ConnectionID: body.ConnectionID,
ChannelName: body.ChannelName,
}
if strings.HasPrefix(body.ChannelName, "presence-") {
memberData, _ := json.Marshal(map[string]any{
"member_id": user.ID,
"member_info": map[string]string{"name": user.Name},
})
params.MemberData = string(memberData)
}
auth, err := client.Realtime.AuthorizeChannel(params)
if err != nil {
http.Error(w, "authorization failed", http.StatusInternalServerError)
return
}
json.NewEncoder(w).Encode(auth)
}function birdAuth(string $connectionId, string $channelName, User $user): array
{
// Your own authorization decision goes here.
if (!mayJoin($user, $channelName)) {
http_response_code(403);
exit;
}
$memberData = null;
if (str_starts_with($channelName, 'presence-')) {
$memberData = json_encode(['member_id' => $user->id, 'member_info' => ['name' => $user->name]]);
}
return $bird->realtime->authorizeChannel($connectionId, $channelName, $memberData);
}O contrato de assinatura é o mesmo em uma linguagem sem um SDK: aplique HMAC-SHA256 na string com o segredo do app, codifique em hexadecimal e prefixe com a chave do app e dois-pontos.
Membros e conexões
Um membro é uma identidade, enquanto uma conexão é um WebSocket aberto. Se alguém abrir o seu app em três abas, um membro terá três conexões. member_added dispara quando a primeira conexão se inscreve, e member_removed dispara quando a última sai. Outras conexões alteram a contagem de conexões do canal sem produzir eventos de membro.
Falhas comuns
Uma inscrição rejeitada chega como um erro no cliente. Verifique estas causas comuns:
- Assinatura inválida. A string que você assinou não corresponde. Quase sempre um member_data resserializado, ou uma assinatura calculada sobre o nome do canal sem o prefixo private- ou presence-.
- Chave inválida. A chave em auth pertence a um app diferente, ou foi revogada. Rotacionar chaves significa atualizar tanto o appKey do cliente quanto o segredo com o qual o seu endpoint assina.
- Dados de membro ausentes. Uma inscrição de presença chegou sem member_data. Canais de presença não podem ser acessados anonimamente.
- Um 403 do seu próprio endpoint. Sua decisão de autorização recusou, que é o resultado esperado para um usuário que não pode entrar.
Próximos passos
- Envie seu primeiro evento em tempo real é o passo a passo completo no qual este guia se baseia.
- Canais criptografados se baseiam nesta assinatura para também fornecer aos inscritos aprovados uma chave de descriptografia.
- Canais de presença cobrem a lista de membros, eventos de membros e a leitura de presença a partir do seu servidor.
- Encerrando conexões de membros é a outra assinatura que o seu backend calcula, e a que permite fechar as conexões de um membro.
- Webhooks e eventos cobre eventos realtime.*, incluindo membros entrando e saindo, no seu próprio endpoint.
Recursos relacionados
Continue com a documentação, guias e exemplos sobre este tópico. Os recursos estão em inglês.