POST /api/messenger/conferences/{conferenceId}/media-token — Обновить токен входа в конференцию
Активный участник запрашивает свежий дескриптор входа в медиа-комнату конференции: короткоживущий AccessToken
истекает по ходу длинной встречи, и клиент обновляет его заранее либо после реконнекта. Возвращает
MediaJoinModel (дом — messenger-hub.md) без изменения состояния сессии.
Назначение
Продлить присутствие участника в идущей конференции, не заводя новую строку ростера: выдать новый токен доступа и
актуальные параметры входа (SignalingUrl, IceServers, ExpiresAt). Нужен для длинных встреч (истечение TTL) и
сетевых реконнектов. Owning-сервис — messenger, feature group conferences. Аналог для 1:1 —
../calls/media-token.md.
Metadata
| Поле | Значение |
|---|---|
| Сервис/контекст | messenger |
| API area | conferences |
| Feature group | conferences |
| Статус | draft |
| Документ | docs/06-services/messenger/api/conferences/conference-media-token.md |
Актор и доступ
| Проверка | Значение |
|---|---|
| Actor type | User — активный участник конференции (State = Joined) |
| Auth policy / Permissions | permission messenger.conferences.use |
| Scope (RBAC) | не применяется — доступ по членству в чате |
| Record-level | активная строка ростера актора (Joined) и членство в Deal-чате (INV-CHAT-4) |
| Tenant isolation | не применяется (кросс-tenant); срез — по членству |
| Audit | выдача токена доступа (Sensitive) — факт выпуска логируется без значения токена |
HTTP-контракт
| Поле | Значение |
|---|---|
| Method | POST |
| Route | /api/messenger/conferences/{conferenceId}/media-token |
| Success status | 200 (OK) |
| Idempotency header | не применяется — каждый вызов минтит новый короткоживущий токен (состояние сессии не меняется) |
| Correlation | сквозной trace через traceparent |
Входные данные / параметры запроса
| Поле | Источник | Тип | Обязательность | Валидация / Ограничения | Безопасность |
|---|---|---|---|---|---|
ConferenceId | route | Guid | Да | != Empty; конференция в Active, актор — активный участник (шаги 3–4) | — |
ActorUserId | identity claims | Guid | Да | из JWT; строка ростера актора со State = Joined | — |
Тела запроса нет.
Модель ответа MediaTokenModel
Обёртка над дескриптором входа: единственное поле Room (тип MediaJoinModel, дом —
messenger-hub.md).
| Поле | Тип | Обязательность | Источник | Masking | Описание |
|---|---|---|---|---|---|
Room | MediaJoinModel | Да | вычисляется | Sensitive: AccessToken/TURN-креды не логируются | Свежий дескриптор: RoomName, AccessToken (короткий TTL), SignalingUrl, MediaDomain, IceServers, ExpiresAt. |
RoomName/MediaDomain берутся из персистентного MediaRoom; AccessToken/TURN-креды — эфемерные, минтятся на
запрос и в БД не хранятся (INV-CONF-7).
Предусловия и постусловия
Предусловия. Актор авторизован и имеет messenger.conferences.use; флаг CONFERENCE_CALLS_ENABLED включён.
Конференция существует и в статусе Active (INV-CONF-4). Актор — активный участник Deal-чата (INV-CHAT-4) и
имеет строку ростера со State = Joined (уже вошёл через join-conference.md).
Постусловия. Состояние сессии и ростер не изменены (чтение + выпуск токена). Актору выдан новый
эфемерный MediaJoinModel со свежим AccessToken/TURN-кредами и ExpiresAt; токен в БД/realtime_outbox не
сохранён и в логи не попадает (INV-CONF-7). Realtime-push и Kafka-события не порождаются.
Алгоритм
- Контекст и доступ. Операцию выполняет активный участник конференции (
User);ActorUserId— из JWT; требуется правоmessenger.conferences.use. Конференции включены флагомCONFERENCE_CALLS_ENABLED, иначе —MSG_CONFERENCES_DISABLED. - Проверка входа.
ConferenceIdнепустой; иначе ошибка валидации. - Загрузка сессии и доступ. Конференция по
ConferenceId; нет →MSG_CONFERENCE_NOT_FOUND. Актор — активный участник Deal-чата (INV-CHAT-4); иначеMSG_CHAT_ACCESS_DENIED. Актор — активный участник самой конференции (строка ростераState = Joined); иначеMSG_CONFERENCE_FORBIDDEN(для первого входа — join-conference.md). - Состояние. Конференция должна быть
Active(INV-CONF-4); прочие статусы →MSG_CONFERENCE_NOT_ACTIVE. - Выпуск токена. Минтится новый
MediaJoinModelиз персистентногоMediaRoom(тот жеRoomName) со свежимAccessToken/TURN-кредами иExpiresAt. Состояние сессии и ростер не меняются (чтение + выпуск токена). - Результат.
MediaTokenModel { Room }→ HTTP200 OK. Токен эфемерный, не персистится, в логи не пишется.
Побочные эффекты
| Тип | Детали |
|---|---|
| Integration events | none |
| Realtime push (hub) | none — токен возвращается синхронно в ответе |
| Токены доступа | новый Room.AccessToken/TURN-креды минтятся на запрос, в БД/realtime_outbox не хранятся, не логируются |
| External calls | выпуск токена доступа к медиа-комнате в media-стеке (SFU) |
Ошибки и failure modes
| Код | HTTP status | Условие | Сообщение | Retry |
|---|---|---|---|---|
VALIDATION_FAILED | 400 | ConferenceId = Empty | «Проверьте параметры запроса» | no retry |
MSG_CONFERENCE_NOT_FOUND | 404 | конференции нет | «Конференция не найдена» | no retry |
MSG_CHAT_ACCESS_DENIED | 403 | актор не участник Deal-чата | «Нет доступа к конференции» | no retry |
MSG_CONFERENCE_FORBIDDEN | 403 | актор не активный участник конференции (не вошёл) | «Сначала присоединитесь к конференции» | no retry (join) |
MSG_CONFERENCE_NOT_ACTIVE | 409 | статус не Active | «Конференция уже завершена» | no retry (перечитать) |
MSG_CONFERENCES_DISABLED | 503 | CONFERENCE_CALLS_ENABLED = false | «Конференции временно недоступны» | retry позже |
Acceptance Criteria
- Обновление токена (happy). Дано: актор — активный участник (
State = Joined) идущейActive-конференции. Когда запрашивает media-token. Тогда200с новымRoom.AccessTokenиExpiresAtв будущем; предыдущий токен не отзывается сервером (истечёт по TTL), строка ростера не меняется. - Реконнект. Дано: у актора истёк
AccessTokenпосле разрыва сети, строка ростера ещёJoined. Когда запрашивает media-token. Тогда200со свежим дескриптором — без повторного join (новой строки ростера не заводится). - Ещё не вошёл. Дано: актор — участник Deal-чата, но строки ростера
Joinedу него нет. Когда запрашивает media-token. Тогда403 MSG_CONFERENCE_FORBIDDEN(сначала join). - Не участник чата. Дано: актор не активный участник Deal-чата. Когда запрашивает media-token. Тогда
403 MSG_CHAT_ACCESS_DENIED. - Конференция завершена. Дано: конференция в
Ended/Failed. Когда актор запрашивает media-token. Тогда409 MSG_CONFERENCE_NOT_ACTIVE. - Флаг выключен. Дано:
CONFERENCE_CALLS_ENABLED = false. Когда актор запрашивает media-token. Тогда503 MSG_CONFERENCES_DISABLED.
Совместимость и наблюдаемость
- Новые optional поля дескриптора — additive; смена маршрута/семантики — breaking. Kafka-контракта нет.
- Logs:
conference media-token issued(ConferenceId,ActorUserId,ExpiresAt; сам токен — никогда в логи); metrics:messenger_conference_media_token_total{result}, latency p95; traces — сквозной trace (traceparent); dashboardpin-messenger.
Обратные ссылки
Автоссылки: где используется этот документ.
- API (3): GET /api/messenger/conferences/{conferenceId} — Карточка конференции, POST /api/messenger/chats/{chatId}/conferences — Начать конференцию в чате сделки, POST /api/messenger/conferences/{conferenceId}/join — Присоединиться к конференции
- Use Cases (1): UC-MSG-006. Групповой звонок-конференция в чате сделки
- calls (1): Жизненный цикл сессии звонка — 1:1 и конференция (messenger)
- registries (1): Реестр API-методов PIN
Связанные документы
- Дескриптор входа
MediaJoinModel(дом): messenger-hub.md; сущность (INV-CONF-4/7): conference-session.md; ростер: call-participant.md. - 1:1-аналог: ../calls/media-token.md; первый вход: join-conference.md; соседние операции: get-conference.md, end-conference.md; контекст: README.
- ADR: 0062 медиа-стек и запись, 0055 свой мини-мессенджер.