Пропустить навигацию

JWT-аутентификация для чата трансляций

Обновлено: 28.12.2025

JWT-аутентификация позволяет автоматически авторизовывать пользователей в чате трансляции и контролировать доступ на стороне вашего сервера. Это особенно полезно, если вы хотите интегрировать чат с вашей системой учёта пользователей.

Кому подходит эта статья

  • Разработчикам платформ — нужно интегрировать чат трансляций с системой аутентификации
  • Администраторам систем — требуется контроль доступа к чату трансляций
  • Backend-разработчикам — нужно настроить безопасную авторизацию пользователей

Когда вам нужна JWT-аутентификация чата

Используйте JWT-аутентификацию, если:

  • Нужен контроль доступа — вы хотите решать на своём сервере, кто может писать в чат
  • Интеграция с вашей системой — нужно связать пользователей чата с вашей системой аутентификации
  • Автоматическая авторизация — пользователи должны авторизовываться без дополнительных действий
  • Привязка к системе учёта — нужно отслеживать, кто из ваших пользователей пишет в чат

Как работает JWT в чате (5 шагов)

JWT-аутентификация использует асимметричную криптографию (RSA) для безопасной передачи данных о пользователе:

  1. Вы создаёте пару ключей (приватный и публичный) на вашем сервере
  2. Публичный ключ сохраняется в Kinescope через API (приватный остаётся только у вас)
  3. Ваш сервер создаёт JWT-токен с данными пользователя и подписывает его приватным ключом
  4. Токен передаётся в URL чата как параметр token
  5. Kinescope проверяет подпись публичным ключом и авторизует пользователя
Преимущество асимметричной схемы: Приватный ключ никогда не покидает ваш сервер. Kinescope получает только публичный ключ и может проверять подписи, но не может создавать поддельные токены. Это безопаснее, чем симметричная схема (HS256), где секретный ключ нужно было бы передавать в Kinescope.

Как включить JWT-аутентификацию

Аутентификация настраивается индивидуально для каждой трансляции через API Kinescope. Передайте параметр chat_jwt_required: true для включения:

Важно: API-токен в заголовке Authorization: Bearer должен быть в формате UUID. Получить токен можно в Dashboard Kinescope в разделе Настройки → API-токены. Подробнее об авторизации в общих правилах API.

curl --location --request PUT 'https://api.kinescope.io/v2/live/events/{{event_id}}' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer your_api_token_here' \
--data '{
	"chat_jwt_required": true
}'

После включения пользователи будут автоматически авторизованы при переходе по ссылке с токеном:

https://kinescope.io/chat/{{event_id}}?token={{jwt}}

Теперь разберём, как настроить всё с нуля.

Настройка: шаг 1 — генерация ключей

Для JWT-аутентификации используется асимметричная криптография RSA, которая требует пару ключей:

  • Приватный ключ — остаётся на вашем сервере, используется для подписи JWT-токенов
  • Публичный ключ — передаётся в Kinescope через API, используется для проверки подписи токенов

Что такое JWK?

JWK (JSON Web Key) — это стандартизированный формат представления криптографических ключей (RFC 7517). Он позволяет безопасно обмениваться ключами между системами:

  1. Безопасность — приватный ключ никогда не покидает ваш сервер
  2. Ротация ключей — легко заменить ключи, просто загрузив новый публичный ключ
  3. Отзыв доступа — при компрометации ключа можно немедленно удалить публичный ключ из Kinescope
  4. Стандартизация — JWK поддерживается большинством библиотек и систем
Почему не симметричный ключ (HS256)? При использовании симметричной схемы вам пришлось бы передавать секретный ключ в Kinescope. Это создаёт риск компрометации: если ключ будет скомпрометирован в Kinescope, злоумышленник сможет создавать поддельные токены. С асимметричной схемой (RS256) даже при компрометации публичного ключа злоумышленник не сможет создавать токены, так как для этого нужен приватный ключ, который остаётся только у вас.

Пример генерации JWK

Вот как выглядит генерация ключевой пары:

package main

import (
    "crypto/rand"
    "crypto/rsa"
    "crypto/x509"
    "encoding/base64"
    "encoding/json"
    "time"
    
    "github.com/go-jose/go-jose/v3"
)

type JWK struct {
    Kty string `json:"kty"` // тип ключа (RSA)
    Kid string `json:"kid"` // идентификатор ключа (уникальный)
    Use string `json:"use"` // назначение (sig для подписи)
    Alg string `json:"alg"` // алгоритм (RS256)
    N   string `json:"n"`   // модуль RSA ключа (base64url-encoded)
    E   string `json:"e"`   // экспонента (обычно "AQAB")
}

// Генерация RSA ключевой пары размером 2048 бит
func generateRSAKeyPair() (*rsa.PrivateKey, *JWK, error) {
    // Генерируем RSA ключевую пару
    privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
    if err != nil {
        return nil, nil, err
    }
    
    // Генерируем уникальный Key ID (kid)
    kid := "key-" + time.Now().Format("2006-01-02")
    
    // Формируем публичный ключ в формате JWK
    publicKeyJWK := &JWK{
        Kty: "RSA",
        Kid: kid,
        Use: "sig",  // назначение - подпись
        Alg: "RS256", // алгоритм - RSA с SHA-256
        N:   base64.RawURLEncoding.EncodeToString(privateKey.PublicKey.N.Bytes()),
        E:   base64.RawURLEncoding.EncodeToString([]byte{1, 0, 1}), // 65537 = AQAB
    }
    
    return privateKey, publicKeyJWK, nil
}

// Сохранение приватного ключа в PEM формате
func savePrivateKey(key *rsa.PrivateKey) ([]byte, error) {
    return x509.MarshalPKCS8PrivateKey(key)
}

Пример готового публичного JWK в JSON:

{
  "kty": "RSA",
  "kid": "key-2024-12-25",
  "use": "sig",
  "alg": "RS256",
  "n": "0vx7agoebGcQSuuPiLJXZptN9nndrQmbwE...",
  "e": "AQAB"
}

Что означают поля:

  • kty — тип ключа (RSA)
  • kid — идентификатор ключа (уникальный в вашей системе)
  • use — назначение ключа (sig для подписи)
  • alg — алгоритм подписи (RS256 для RSA с SHA-256)
  • n — модуль RSA ключа (base64url-encoded)
  • e — экспонента RSA ключа (обычно AQAB, что означает 65537)

Сохранение публичного ключа в Kinescope

После генерации JWK сохраните публичную часть ключа в Kinescope через API. Обязательно передайте все параметры: kty, e, use, kid, alg, n и expires_at (дата истечения ключа в формате ISO 8601):

curl --location 'https://api.kinescope.io/v1/jwk' \
--request POST \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer your_api_token_here' \
--data '{
	"kty": "RSA",
	"e": "AQAB",
	"use": "sig",
	"kid": "key-2024-12-25",
	"alg": "RS256",
	"n": "0vx7agoebGcQSuuPiLJXZptN9nndrQmbwE...",
	"expires_at": "2026-12-31T23:59:59Z"
}'

Пример успешного ответа:

{
  "id": "jwk_abc123def456",
  "kty": "RSA",
  "kid": "key-2024-12-25",
  "created_at": "2024-12-25T10:00:00Z",
  "expires_at": "2026-12-31T23:59:59Z"
}

Управление ключами

Просмотреть все активные ключи:

curl --location 'https://api.kinescope.io/v1/jwk' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer your_api_token_here'

Получить данные по конкретному ключу:

curl --location 'https://api.kinescope.io/v1/jwk/{{jwk_id}}' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer your_api_token_here'

Удалить ключ:

curl --location --request DELETE 'https://api.kinescope.io/v1/jwk/{{jwk_id}}' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer your_api_token_here'

Настройка: шаг 2 — генерация JWT-токена

Создайте JWT-токен (JSON Web Token, RFC 7519) с обязательными полями и подпишите его приватным ключом.

Обязательные поля

  • aud (audience) — значение "chat" (обязательно). Kinescope проверяет, что токен предназначен для чата.
  • user_id — уникальный идентификатор пользователя в вашей системе. Используется для идентификации в чате.
  • username — имя пользователя для отображения в чате. Будет показано рядом с сообщениями.
  • event_id — ID трансляции (обязательно). Можно найти в личном кабинете или в ссылке на трансляцию.

Важно: event_id необходим для эмбеда чата — чат может быть встроен на страницу без JWT-аутентификации, но при использовании JWT требуется event_id для привязки токена к конкретной трансляции.

Стандартные JWT-поля (рекомендуется)

  • exp (expiration) — время истечения токена (Unix timestamp). Рекомендуется устанавливать короткий срок жизни (например, 1 час).
  • nbf (not before) — время, до которого токен недействителен (Unix timestamp). Полезно для токенов, которые должны стать активными в будущем.
  • iat (issued at) — время создания токена (Unix timestamp). Помогает отслеживать возраст токена.

Все стандартные JWT-поля будут проверены системой Kinescope при валидации токена.

Пример генерации JWT

Вот как выглядит генерация и подпись JWT:

package main

import (
    "crypto/rsa"
    "time"
    
    "github.com/golang-jwt/jwt/v5"
)

type ChatClaims struct {
    UserID  string `json:"user_id"`
    Username string `json:"username"`
    EventID  string `json:"event_id"`
    jwt.RegisteredClaims
}

// Генерация JWT токена
func generateJWT(privateKey *rsa.PrivateKey, kid string, userID, username, eventID string) (string, error) {
    now := time.Now()
    
    claims := ChatClaims{
        UserID:   userID,
        Username: username,
        EventID:  eventID,
        RegisteredClaims: jwt.RegisteredClaims{
            Audience:  []string{"chat"}, // обязательно "chat"
            IssuedAt:  jwt.NewNumericDate(now),
            ExpiresAt: jwt.NewNumericDate(now.Add(1 * time.Hour)), // по умолчанию 1 час
        },
    }
    
    token := jwt.NewWithClaims(jwt.SigningMethodRS256, claims)
    token.Header["kid"] = kid // Key ID
    
    return token.SignedString(privateKey)
}

// Пример использования
func main() {
    privateKey, publicKeyJWK, err := generateRSAKeyPair()
    if err != nil {
        panic(err)
    }
    
    kid := publicKeyJWK.Kid
    
    // Генерируем токен
    jwtToken, err := generateJWT(
        privateKey,
        kid,
        "user-12345",      // ID пользователя
        "Иван Иванов",     // Имя пользователя
        "event-abc-123",   // ID трансляции
    )
    if err != nil {
        panic(err)
    }
    
    println("JWT токен:", jwtToken)
}

Для разработчиков: В реальной реализации используйте библиотеки для генерации JWT:

  • Node.js: node-jsonwebtoken (jwt.sign()), jose (new SignJWT().setProtectedHeader().sign())
  • Браузер: Web Crypto API (crypto.subtle.sign())
  • Python: PyJWT (jwt.encode())
  • Go: github.com/golang-jwt/jwt/v5 (jwt.NewWithClaims().SignedString())

Структура JWT-токена

JWT состоит из трёх частей, разделённых точками: header.payload.signature

Header (заголовок):

{
  "alg": "RS256",
  "typ": "JWT",
  "kid": "key-2024-12-25"
}

Payload (данные):

{
  "aud": "chat",
  "user_id": "user-12345",
  "username": "Иван Иванов",
  "event_id": "event-abc-123",
  "iat": 1703500800,
  "exp": 1703504400
}

Signature (подпись): Подпись создаётся путём хеширования base64(header) + "." + base64(payload) с использованием приватного ключа по алгоритму RS256.

Готовый токен (пример):

eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImtleS0yMDI0LTEyLTI1In0.eyJhdWQiOiJjaGF0IiwidXNlcl9pZCI6InVzZXItMTIzNDUiLCJ1c2VybmFtZSI6ItCY0LLQvdCwINCY0LLQvdCw0L3QvtCyIiwiZXZlbnRfaWQiOiJldmVudC1hYmMtMTIzIiwiaWF0IjoxNzAzNTAwODAwLCJleHAiOjE3MDM1MDQ0MDB9.signature_here

Использование токена

После генерации токена передайте его в URL чата:

https://kinescope.io/chat/event-abc-123?token=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImtleS0yMDI0LTEyLTI1In0...

Пример полного URL с токеном:

https://kinescope.io/chat/event-abc-123?token=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImtleS0yMDI0LTEyLTI1In0.eyJhdWQiOiJjaGF0IiwidXNlcl9pZCI6InVzZXItMTIzNDUiLCJ1c2VybmFtZSI6ItCY0LLQvdCwINCY0LLQvdCw0L3QvtCyIiwiZXZlbnRfaWQiOiJldmVudC1hYmMtMTIzIiwiaWF0IjoxNzAzNTAwODAwLCJleHAiOjE3MDM1MDQ0MDB9.signature_here

Готово! Теперь ваши пользователи смогут автоматически авторизовываться в чате трансляции через JWT-токены.

Безопасность

Ротация ключей

Рекомендуется регулярно обновлять ключи для повышения безопасности. Процесс ротации:

  1. Сгенерируйте новую пару ключей (приватный и публичный)
  2. Загрузите новый публичный ключ в Kinescope через API (старый ключ останется активным)
  3. Начните использовать новый приватный ключ для подписи новых токенов
  4. После периода перекрытия (когда все старые токены истекут) удалите старый публичный ключ из Kinescope
Период перекрытия: Рекомендуется оставлять старый ключ активным на время, равное максимальному сроку жизни токенов (например, если токены живут 24 часа, оставьте старый ключ на 24 часа после начала использования нового).

Действия при компрометации ключа

Если приватный ключ был скомпрометирован (утечка, подозрение на взлом):

  1. Немедленно удалите публичный ключ из Kinescope через API:
    curl --location --request DELETE 'https://api.kinescope.io/v1/jwk/{{jwk_id}}' \
    --header 'Content-Type: application/json' \
    --header 'Authorization: Bearer your_api_token_here'
    
  2. Сгенерируйте новую пару ключей и загрузите новый публичный ключ
  3. Уведомите пользователей о необходимости повторной авторизации (если требуется)
  4. Проверьте логи на наличие подозрительной активности
Важно: После удаления ключа все токены, подписанные этим ключом, станут недействительными. Пользователям потребуется получить новые токены.

Рекомендации по безопасности

  • Размер ключа: Используйте ключи размером минимум 2048 бит (рекомендуется для RSA)
  • Срок жизни токенов: Устанавливайте короткий срок жизни токенов (1-24 часа). Для долгоживущих сессий используйте механизм обновления токенов
  • Срок жизни ключей: Устанавливайте expires_at для публичных ключей (например, 1-2 года) и планируйте ротацию до истечения
  • Хранение приватного ключа: Храните приватный ключ в безопасном хранилище (например, в секретах Kubernetes, AWS Secrets Manager, или зашифрованном хранилище)
  • Мониторинг: Отслеживайте использование ключей и подозрительную активность
  • Алгоритм: Используйте только RS256 (RSA с SHA-256). Другие алгоритмы не поддерживаются

Ограничения

  • Поддерживаемые алгоритмы: Только RS256 (RSA с SHA-256)
  • Минимальный размер ключа: 2048 бит
  • Максимальное количество активных ключей: Ограничение может применяться на уровне аккаунта (уточните в поддержке)

Решение проблем

Токен не принимается системой

Проблема: Пользователь не может авторизоваться, токен отклоняется.

Возможные причины и решения:

  1. Неверная подпись токена

    • Убедитесь, что используете правильный приватный ключ для подписи
    • Проверьте, что публичный ключ загружен в Kinescope и активен
    • Убедитесь, что используете алгоритм RS256
  2. Истёкший ключ

    • Проверьте поле expires_at публичного ключа в Kinescope
    • Если ключ истёк, загрузите новый публичный ключ
  3. Неверный event_id

    • Убедитесь, что event_id в токене соответствует ID трансляции
    • Проверьте, что трансляция существует и JWT-аутентификация включена для неё
    • Важно: event_id обязателен для эмбеда чата — чат может быть встроен без JWT, но при использовании JWT требуется корректный event_id
  4. Истёкший токен

    • Проверьте поле exp в токене (время истечения)
    • Убедитесь, что системное время на сервере синхронизировано (NTP)
    • Генерируйте новые токены с актуальным временем истечения
  5. Неверное поле aud

    • Убедитесь, что поле aud имеет значение "chat" (строго в нижнем регистре)

Как проверить валидность токена

Вы можете проверить токен локально перед отправкой пользователю. Вот пример функции проверки:

package main

import (
    "crypto/rsa"
    "errors"
    
    "github.com/golang-jwt/jwt/v5"
)

// Проверка валидности JWT токена
func verifyJWT(tokenString string, publicKey *rsa.PublicKey) (*ChatClaims, error) {
    token, err := jwt.ParseWithClaims(tokenString, &ChatClaims{}, func(token *jwt.Token) (interface{}, error) {
        // Проверяем алгоритм
        if _, ok := token.Method.(*jwt.SigningMethodRSA); !ok {
            return nil, errors.New("неподдерживаемый алгоритм")
        }
        return publicKey, nil
    })
    
    if err != nil {
        return nil, err
    }
    
    if claims, ok := token.Claims.(*ChatClaims); ok && token.Valid {
        // Проверяем audience
        if !claims.VerifyAudience("chat", true) {
            return nil, errors.New("неверный audience")
        }
        return claims, nil
    }
    
    return nil, errors.New("неверный токен")
}

Для разработчиков: В реальной реализации используйте библиотеки для проверки JWT:

  • Node.js: node-jsonwebtoken (jwt.verify()), jose (jwtVerify())
  • Браузер: Web Crypto API (crypto.subtle.verify())
  • Python: PyJWT (jwt.decode())
  • Go: github.com/golang-jwt/jwt/v5 (jwt.ParseWithClaims())

Онлайн-инструменты:

  • jwt.io — декодирование и проверка структуры токена (без проверки подписи)
  • Проверьте структуру payload: должны присутствовать все обязательные поля

Частые ошибки при генерации ключей

  1. Ошибка “invalid key format”

    • Убедитесь, что используете правильный формат JWK (RFC 7517)
    • Проверьте, что все обязательные поля присутствуют: kty, e, n, kid, alg, use
  2. Ошибка “key size too small”

    • Используйте ключи размером минимум 2048 бит
    • При генерации: rsa.GenerateKey(rand.Reader, 2048)
  3. Ошибка “key expired”

    • Проверьте поле expires_at при загрузке ключа
    • Убедитесь, что дата истечения указана в формате ISO 8601: "2026-12-31T23:59:59Z"

Проблемы с эмбедом чата

Проблема: Чат не отображается или не авторизует пользователя при эмбеде.

Решения:

  1. Проверьте event_id в токене — он должен соответствовать ID трансляции, к которой привязан чат
  2. Убедитесь, что JWT-аутентификация включена для трансляции через API
  3. Проверьте формат URL — токен должен быть передан как параметр token:
    https://kinescope.io/chat/{{event_id}}?token={{jwt_token}}
    
    Пример:
    https://kinescope.io/chat/event-abc-123?token=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...
    
  4. Проверьте консоль браузера на наличие ошибок JavaScript

Если проблема не решена, обратитесь в техническую поддержку с информацией:

  • ID трансляции (event_id)
  • Пример токена (можно замаскировать чувствительные данные)
  • Описание проблемы и шаги воспроизведения

Всё! Теперь вы можете настроить JWT-аутентификацию для чата трансляций и автоматически авторизовывать пользователей.

Что дальше?

После настройки JWT-аутентификации рекомендуем:

  1. Руководство по проведению трансляций — настройка трансляций в Kinescope
  2. Общие правила API — авторизация и формат запросов
  3. Авторизационный бэкенд — контроль доступа к видео

Остались вопросы? Напишите в чат поддержки — специалисты помогут!