개발자 레퍼런스

백엔드에서 토큰 발급하기

입장 토큰을 발급하는 지원되는 방식 — 그리고 빠지는 순간 단일 세션 강제가 조용히 꺼지는 단 하나의 필드.

개발자 제품으로 돌아가기

왜 백엔드에서?

서버 시크릿은 절대 앱에 담기지 않습니다. APK에 포함된 키는 추출되어 누구의 신원으로든 도용될 수 있습니다.

기기 정보를 전달하세요

서버는 기기를 볼 수 없습니다. 앱이 값을 여러분의 서버로 보내고, 서버가 같은 요청에 담아 전달합니다.

실패는 조용합니다

device_id가 없으면 검사가 오류도 로그도 없이 일찍 반환됩니다. 토큰은 성공하고 보호만 꺼집니다.

추정이 아니라 측정

실제 입장 3시간 기준: 백엔드 발급 토큰 3,224건 — 100%가 기기 식별자 없음. 키트 발급 토큰 1,641건 — 0%.

토큰 요청 필드

헤더에 서버 시크릿을 담아 /api/v1/token 으로 POST 하세요.

필드수준의미
user_idstring필수Who the token is for. Your own user id. (Previously `identity` — still accepted.)
room_idstring필수Which room they are joining. (Previously `room_name` — still accepted.)
device_idstring권장A stable id for the PHYSICAL DEVICE — not the user, and not the session. This is what enforces one account on one device: when the same user_id joins from a different device_id, the previous device is messaged and removed. Omit it and that enforcement silently does nothing: the check returns early, logs nothing, and the old device stays signed in.
device_modelstring권장e.g. SM-A175F. Feeds per-handset quality analysis — which models have audio or video trouble.
osstring권장android | ios.
os_versionstring권장e.g. 14.
app_versionstring권장Your app's version, so a regression can be traced to a release.
display_namestring선택Shown to other participants. Omit it and the name stays empty — we never substitute the user id for it.
rolestring선택Only honoured from a server-signed request, and only while your project still carries the client-asserted-role exception. The supported path is PUT /rooms/:room/participants/:id/role.
typestring선택audio_room | live_stream. Legacy kits send `service` (+ `kind`) instead and the type is derived.

서버 쪽

시크릿은 여기에만 존재합니다. 표시된 필드는 앱에서 오며, 서버가 스스로 알 수 없습니다.

mint-token.js
// Your backend — the app never sees the server secret.
const res = await fetch("https://engine.udt-stream.com/api/v1/token", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "X-App-Secret": process.env.UTD_SERVER_SECRET,   // never ship this in the app
  },
  body: JSON.stringify({
    user_id: currentUser.id,
    room_id: roomId,

    // 🔴 Forwarded FROM THE APP. Your server cannot know these on its own,
    // and without device_id one-account-one-device stops working for your users.
    device_id: body.device_id,
    device_model: body.device_model,
    os: body.os,
    os_version: body.os_version,
    app_version: body.app_version,
  }),
});

앱 쪽

기기 정보를 여러분의 백엔드로 보내면 그대로 전달됩니다. 기기 식별자는 앱 재시작 후에도 동일해야 합니다.

request_token.dart
// Your app — send the device facts to YOUR backend, which forwards them to us.
final deviceId = await MyDeviceIdentity.stableId(); // persisted, survives app restarts
await myApi.post("/rooms/$roomId/token", body: {
  "device_id": deviceId,
  "device_model": deviceInfo.model,
  "os": Platform.isAndroid ? "android" : "ios",
  "os_version": deviceInfo.version,
  "app_version": packageInfo.version,
});

거부 코드 (403)

모든 거부에는 code가 있습니다. 코드를 읽으세요 — "퇴장되었습니다" 메시지는 첫 번째에만 띄워야 합니다.

코드의미조치
user_bannedThis user is banned from this room.Show them they were removed. This is the ONLY code that should produce that message.
room_type_disabledThe project does not have this room type enabled.A configuration problem, not a user problem. Never show a removal notice.
streaming_disabledThe streaming service is not enabled for this project.Same — configuration, not the user.
appkey_identity_mint_disabledYou tried to mint an identity-bearing token with the publishable app_key.Mint from your backend with the server secret instead. This is the path this page describes.

인증 모드

엔진은 모든 입장에 인증 모드를 기록하므로, 실제로 어느 경로인지 확인할 수 있습니다.

secret / signature / bearer

Your backend, authenticated with your server secret. The recommended path.

app_key

The device, using the publishable app key. The legacy path — being closed.

UTD와 함께 빌드할 준비가 되셨나요?

계정을 만들고, 마스터 지갑을 충전한 뒤, 필요한 서비스를 켜세요.