Autoryzacja kanałów
Każdy klient posiadający klucz aplikacji może subskrybować kanały publiczne. Dwa prefiksy nazw kanałów wymagają autoryzacji subskrypcji przez Twój backend. Kanałów nie konfigurujesz osobno.
Kanał o nazwie private-… wymaga zatwierdzenia każdej subskrypcji przez Twój backend. Kanał o nazwie presence-… działa tak samo, ale dodatkowo przypisuje tożsamość subskrybentowi, dzięki czemu wszyscy na kanale widzą, kto jeszcze jest obecny. Każda inna nazwa oznacza kanał publiczny.
Tylko Twój backend przechowuje sekret aplikacji. Klient prosi Twój serwer o podpisanie konkretnej subskrypcji, a brzeg Realtime weryfikuje ten podpis przed jej zaakceptowaniem. Twój serwer decyduje, czy wywołujący może subskrybować, nie ujawniając sekretu klientowi.
Skieruj klienta na swój endpoint
Przekaż klientowi authEndpoint na swoim backendzie:
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")Klient wywołuje ten endpoint dla każdej subskrypcji prywatnej lub presence, w tym subskrypcji przywróconych po ponownym połączeniu. Autoryzacja dotyczy jednego połączenia, ponieważ podpis zawiera jego identyfikator połączenia.
Klient przeglądarkowy domyślnie wymaga endpointu w tej samej domenie. Ustaw allowCrossOriginAuth: true, aby użyć authEndpoint w innej domenie. Klient przeglądarkowy wysyła skonfigurowane authHeaders tylko do endpointów w tej samej domenie.
Co Twój endpoint odbiera i zwraca
Klient wysyła POSTem JSON:
Przykład kodu
{ "connection_id": "26896.319537", "channel_name": "presence-room-1" }Zwróć podpis w odpowiedzi:
Przykład kodu
{ "auth": "your-app-key:8f9a…" }Dla kanału presence zwróć dodatkowo tożsamość członka jako JSON string, ten sam ciąg, który podpisałeś:
Przykład kodu
{
"auth": "your-app-key:8f9a…",
"member_data": "{\"member_id\":\"u_42\",\"member_info\":{\"name\":\"Ada\"}}"
}member_id to tożsamość widoczna dla innych członków i wartość, na którą kierowana jest operacja rozłączenia. member_info to opcjonalne dane JSON dostarczane każdemu członkowi kanału. Limit wynosi 1 KB, więc umieszczaj tylko małe, niewrażliwe dane profilowe.
Autoryzuj wywołującego w tym endpoincie na podstawie ciasteczka sesji lub tokena bearer. Zwróć 403 Forbidden, gdy wywołujący nie powinien dołączyć do kanału. Dla kanałów presence przypisz tożsamość w tej samej odpowiedzi.
Ciąg do podpisania
Połącz dwukropkami, następnie oblicz HMAC-SHA256 z sekretem aplikacji i zakoduj szesnastkowo. Poprzedź wynik kluczem aplikacji i dwukropkiem.
| Typ kanału | Ciąg do podpisania |
|---|---|
| private-… | <connection_id>:<channel_name> |
| private-encrypted-… | <connection_id>:<channel_name> |
| presence-… | <connection_id>:<channel_name>:<member_data> |
Dla kanałów presence podpisz dokładnie ten ciąg member_data, który zwracasz. Ponowna serializacja tego samego obiektu może zmienić kolejność kluczy lub odstępy i unieważnić podpis.
Kanał szyfrowany podpisuje się tak jak prywatny, a jego odpowiedź auth dodatkowo zwraca klucz deszyfrujący kanału jako shared_secret. Helper SDK dodaje go automatycznie; Kanały szyfrowane opisują derywację i zachowanie kanału.
Każdy serwerowy SDK udostępnia helper authorizeChannel. Podpisuje on skonfigurowanymi danymi uwierzytelniającymi aplikacji i zwraca ciało odpowiedzi bez wykonywania żądania sieciowego. Dla kanałów szyfrowanych helper dodaje też 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);
}Kontrakt podpisywania jest taki sam w języku bez SDK: oblicz HMAC-SHA256 ciągu z sekretem aplikacji, zakoduj szesnastkowo i poprzedź kluczem aplikacji oraz dwukropkiem.
Członkowie i połączenia
Członek to tożsamość, a połączenie to jeden otwarty WebSocket. Jeśli ktoś otworzy Twoją aplikację w trzech kartach, jeden członek ma trzy połączenia. member_added odpala się, gdy pierwsza karta subskrybuje, a member_removed odpala się, gdy ostatnia opuszcza kanał. Pozostałe połączenia zmieniają licznik połączeń kanału bez generowania zdarzeń członków.
Częste błędy
Odrzucona subskrypcja dociera jako błąd klienta. Sprawdź te częste przyczyny:
- Nieprawidłowy podpis. Podpisany ciąg nie pasuje. Niemal zawsze chodzi o ponownie zserializowany member_data lub podpis obliczony na nazwie kanału bez prefiksu private- lub presence-.
- Nieprawidłowy klucz. Klucz w auth należy do innej aplikacji lub został unieważniony. Rotacja kluczy oznacza aktualizację zarówno appKey klienta, jak i sekretu, którym podpisuje Twój endpoint.
- Brak danych członka. Subskrypcja presence dotarła bez member_data. Do kanałów presence nie można dołączyć anonimowo.
- 403 z Twojego własnego endpointu. Twoja decyzja autoryzacyjna odmówiła dostępu. To zamierzony wynik dla użytkownika, który nie powinien dołączyć.
Kolejne kroki
- Wyślij swoje pierwsze zdarzenie realtime to kompleksowy przewodnik, na którym opiera się ten artykuł.
- Kanały szyfrowane rozszerzają ten podpis o przekazanie zatwierdzonym subskrybentom klucza deszyfrującego.
- Kanały presence opisują listę członków, zdarzenia członków i odczyt obecności z Twojego serwera.
- Zamykanie połączeń członka to drugi podpis obliczany przez Twój backend, który pozwala zamknąć połączenia członka.
- Webhooki i zdarzenia opisują zdarzenia realtime.*, w tym dołączanie i opuszczanie przez członków, na Twoim własnym endpoincie.
Powiązane zasoby
Kontynuuj z dokumentacją, przewodnikami i przykładami dotyczącymi tego tematu. Zasoby są w języku angielskim.