Skip to main content

Embedded Application — Access Token integratsiyasi (Third-party backend uchun)

Bu hujjat embedded ilovani ishlab chiqayotgan tashqi (third-party) jamoa uchun. UYSOT CRM sizning ilovangizni iframe ichida ochadi va kontekst ma'lumotlarini (lid, foydalanuvchi, va eng muhimi — qisqa muddatli JWT access token) iframega postMessage orqali uzatadi. Sizning ilovangiz shu xabarni qabul qiladi, tokenni verifikatsiya qiladi va undagi ma'lumotni (kontekstni) ishlatadi.

Bu sahifada:


1. Umumiy tasvir

  1. CRM frontend GET /resolve?page=... ni chaqiradi, UYSOT BE sizning ilovangiz uchun JWT access token + kontekstni qaytaradi.
  2. CRM frontend sizning endpoint URL'ingizni iframe sifatida ochadi (URL'ga token qo'shilmaydi — toza endpoint).
  3. iframe yuklangach, CRM frontend window.postMessage(...) orqali sizning iframeingizga payload yuboradi. Payload ichida accessToken va leadContext bo'ladi.
  4. Sizning ilovangiz (frontend yoki uning backendi) bu payload'ni qabul qiladi. URL toza endpoint bo'ladi.

:::info Token va xavfsizlik Token sizning secret kalitingiz bilan imzolanadi. Ya'ni faqat siz va UYSOT bir xil kalitga ega — bu ikki tomon o'rtasidagi ishonchni ta'minlaydi. Xavfsizlik aynan shu token orqali boshqariladi: sizdagi kelishilgan key bilan tokenni validatsiya qilasiz. :::


2. Secret key (eng muhim qism)

Qayerdan olasiz

UYSOT administratori sizning ilovangiz uchun konfiguratsiya yaratganda secret key generatsiya qilinadi va sizga bir marta beriladi (config yaratish yoki "regenerate" javobida). Buni xavfsiz saqlang — keyin qayta ko'rsatilmaydi.

Formati

  • 64 belgidan iborat kichik harfli hex string.
  • Misol: 9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08
  • Bu 32 ta tasodifiy bayt'ning hex ko'rinishi.

⚠️ Kalit baytlari — diqqat!

:::warning Hex decode qilmang JWT HMAC kaliti sifatida secret string'ning o'zining UTF-8 baytlari ishlatiladi (64 ta ASCII bayt), hex'dan dekodlangan 32 bayt EMAS.

HMAC key = UTF-8_bytes("9f86d081...a08") // ✅ to'g'ri (64 bayt)
HMAC key = hex_decode("9f86d081...a08") // ❌ noto'g'ri (32 bayt)

Kalitni oddiy string sifatida ishlating, uni hex'dan decode qilmang. :::

Regeneratsiya

Admin "regenerate secret" qilsa — eski kalit ishlamay qoladi, yangi token'lar yangi kalit bilan keladi. Bunday holatda UYSOT sizga yangi kalitni beradi va siz uni yangilashingiz kerak.


3. postMessage payload (CRM frontend → sizning iframe)

iframe yuklanganidan keyin UYSOT CRM frontend sizning iframe'ingizga window.postMessage(payload, targetOrigin) orqali quyidagi JSON'ni yuboradi:

{
"embeddedApplicationEvent": "uysot:embedded-application:context-update",
"openApiTokenId": 34,
"name": "test 1😉",
"avatar": "files/com-22/smallest_format/260609-044427-955997fc-41be-4c66.png",
"url": "http://localhost:59517/",
"accessToken": "eyJhbGciOi...",
"expiresIn": 900,
"leadContext": {
"leadId": 99436,
"name": "test in the old version",
"status": "Yangi aloqa",
"responsibleId": 252,
"phone": "+998332132132",
"phones": [
"+998332132132",
"991119090"
],
"balance": 0
}
}

Payload maydonlari

MaydonTipDoimo bormi?Ma'nosi
embeddedApplicationEventstring✅ HaXabar turi (event nomi). Doimo "uysot:embedded-application:context-update". Boshqa message event'laridan ajratish uchun shu maydonni filtrlang.
openApiTokenIdnumber (int)✅ HaSizning ilovangizga biriktirilgan OpenApiToken id'si
namestring✅ HaBiriktirilgan OpenApiToken nomi (display uchun)
avatarstring | null⚠️ Bo'lishi mumkinAvatar (rasm) yo'li; bo'lmasa null
urlstring✅ HaSizning iframe URL'ingiz (endpoint)
accessTokenstring (JWT)✅ HaEng muhim — qisqa muddatli imzolangan JWT. Xavfsizlikni shu orqali boshqarasiz (4-bo'lim)
expiresInnumber✅ HaToken amal qilish muddati soniyada (900 = 15 daqiqa)
leadContextobject | null⚠️ Kontekstga bog'liqLid konteksti. Lid sahifasida bo'ladi; aks holda null

leadContext maydonlari

MaydonTipIzoh
leadIdnumber (long)Lid identifikatori
namestringLid nomi
statusstringPipeline status nomi (lokalizatsiyalangan matn, masalan "Yangi aloqa")
responsibleIdnumber (int) | nullMas'ul xodim id'si (bo'lmasa null)
phonestring | nullAsosiy kontaktning birinchi telefoni (bo'lmasa null)
phonesstring[]Lidning barcha telefon raqamlari (trim + takrorsiz)
balancenumberLid balansi

:::warning leadContext imzolanmagan leadContext JWT ichida emas, payload'da alohida (ochiq) keladi va imzolanmagan. Shuning uchun uni faqat ko'rsatish (display) uchun ishlating. Xavfsizlik uchun muhim qiymatlar (companyId, leadId) — token claim'laridan oling, payloaddan emas. leadContext.leadId ni token'dagi leadId claim bilan solishtirib tekshirishingiz mumkin. :::

Xabarni qabul qilish (iframe ichidagi JS)

window.addEventListener("message", (event) => {
// 1) Origin'ni tekshiring — faqat UYSOT CRM domenidan kelgan xabarni qabul qiling
const ALLOWED_ORIGINS = ["https://crm.uysot.uz"]; // sizga berilgan UYSOT domen(lar)i
if (!ALLOWED_ORIGINS.includes(event.origin)) return;

const payload = event.data;

// 2) Event turini filtrlang — boshqa message'larni e'tiborsiz qoldiring
if (payload?.embeddedApplicationEvent !== "uysot:embedded-application:context-update") return;

// 3) accessToken ni o'z backendingizga yuborib, secret bilan verify qiling (4-bo'lim)
// Faqat verify muvaffaqiyatli bo'lgandan keyin payload'ga ishoning.
fetch("/api/verify-embedded-token", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ accessToken: payload.accessToken })
})
.then((r) => r.json())
.then((verified) => {
// verified — token'dan o'qilgan ishonchli claim'lar (companyId, page, leadId...)
// payload.leadContext — qo'shimcha display ma'lumotlari
renderWidget(verified, payload.leadContext);
})
.catch(() => showError("Invalid embedded token"));
});

:::note Secret va verifikatsiya accessToken HMAC bilan imzolangani uchun verifikatsiya secret kalitni talab qiladi. Secret faqat sizning backendingizda bo'lishi kerak (6-bo'lim), shuning uchun tokenni backendda verify qiling — frontendda emas. :::

Token yangilanishi (refresh)

accessToken muddati 15 daqiqa. Muddat tugashidan oldin UYSOT CRM frontend yangi token bilan yana context-update event yuboradi (yoki iframeni qayta yuklaydi). Ya'ni sizning ilovangiz message listener'ini doim ochiq tutsin va har bir yangi context-update xabarida tokenni qayta o'qib, qayta verify qilsin. Token saqlash yoki o'zingiz refresh qilish sizdan talab qilinmaydi — har kelgan xabardagi tokenni shunchaki verify qilasiz.


4. Token tafsilotlari va verifikatsiya

Algoritm va header

  • Algoritm: HS256 (HMAC-SHA256)
  • Header: { "alg": "HS256", "typ": "JWT" }

Payload (claims)

ClaimTipDoimo bormi?Ma'nosi
companyIdnumber (int)✅ HaUYSOT'dagi kompaniya identifikatori (qaysi tenant)
pagestring✅ HaQaysi sahifa ochilgani (enum, masalan LEAD_WIDGET_PANEL, STATS)
expnumber (NumericDate)✅ HaToken muddati tugash vaqti (Unix epoch, soniyada)
employeeIdnumber (int)⚠️ Bo'lishi mumkinTokenni so'ragan xodim id'si (mavjud bo'lsa)
userIdnumber (int)⚠️ IxtiyoriyFoydalanuvchi id'si (hozircha odatda yuborilmaydi)
leadIdnumber (long)⚠️ Kontekstga bog'liqQaysi lid kontekstida ochilgani (lid sahifasida bo'ladi)

:::note Yo'q claim'lar iat, nbf, iss, sub, aud claim'lari yuborilmaydi. Optional claim'lar mavjud bo'lmasa, payload'da umuman bo'lmaydi (null emas — yo'q). :::

Namuna (decoded payload)

{
"companyId": 12,
"page": "LEAD_WIDGET_PANEL",
"employeeId": 305,
"leadId": 100,
"exp": 1749601800
}

Verifikatsiya qadamlari (sizning backend nima qilishi kerak)

  1. postMessage payload'idagi accessToken ni oling (frontend → backend orqali).
  2. JWT signature'ni HS256 va secret key (UTF-8 string) bilan tekshiring.
  3. exp ni tekshiring — muddati o'tgan bo'lsa rad eting (401).
  4. companyId va page ni o'qing — kerakli kontentni ko'rsating. Kontekst kerak bo'lsa leadId dan foydalaning.
  5. Tavsiya: page qiymati siz kutgan enum'lardan biri ekanini tekshiring (LEAD_WIDGET_PANEL, STATS).
  6. Tavsiya: agar leadContext ishlatsangiz, leadContext.leadId ni token'dagi leadId claim bilan solishtiring.

:::info Token self-contained Token o'zi-o'zini tasdiqlovchi (self-contained). Verify uchun UYSOT'ga qo'shimcha so'rov yuborish shart emas. :::


5. Kod namunalari (token verifikatsiyasi)

const jwt = require("jsonwebtoken");

const SECRET = process.env.UYSOT_EMBEDDED_SECRET; // 64-belgili hex string, STRING sifatida

function verifyAccessToken(token) {
try {
// jsonwebtoken kalitni UTF-8 string sifatida oladi — bu aynan kerakli xatti-harakat
const payload = jwt.verify(token, SECRET, { algorithms: ["HS256"] });
// payload.companyId, payload.page, payload.leadId, ...
return payload;
} catch (e) {
// TokenExpiredError yoki JsonWebTokenError
throw new Error("Invalid embedded access token: " + e.message);
}
}

6. Xavfsizlik tavsiyalari

  • message event'ida origin'ni har doim tekshiring. event.origin faqat sizga berilgan UYSOT CRM domeni ekaniga ishonch hosil qiling. Aks holda istalgan sayt sizning iframe'ingizga soxta xabar yuborishi mumkin.
  • embeddedApplicationEvent maydonini filtrlang. Faqat "uysot:embedded-application:context-update" qiymatli xabarlarni qayta ishlang.
  • Xavfsizlikni token orqali boshqaring. payload'dagi ochiq maydonlarga (leadContext, companyId ko'rinishidagi) ishonmang — ishonchli qiymatlarni faqat verify qilingan token claim'laridan oling.
  • Secret'ni hech qachon frontendga qo'ymang. U faqat sizning backend'ingizda (env/secret manager) saqlanishi kerak. Token verifikatsiyasi backend'da bo'lsin.
  • Signature'ni har doim tekshiring. Tekshirmasdan token payload'ga ishonmang — aks holda har kim soxta token yasashi mumkin.
  • exp ni tekshiring (kutubxonalar buni odatda avtomatik qiladi).
  • Faqat HTTPS ishlating.
  • accessTokenni log'larga to'liq yozmang.
  • Kalit kompromentatsiya qilingan bo'lsa — UYSOT administratoridan "regenerate secret" so'rang.

7. Tez-tez beriladigan savollar

S: Tokenni qayerdan olaman — URL'danmi?
J: Yo'q. Token endi iframe URL'ida emas. CRM frontend uni postMessage payload'i ichida (accessToken maydoni) yuboradi. window.addEventListener("message", ...) bilan qabul qiling (3-bo'limga qarang).

S: message listener ishlamayapti / xabar kelmayapti.
J: Tekshiring: (1) listener iframe yuklanguncha o'rnatilganmi; (2) event.origin filtri to'g'rimi (vaqtincha log qilib ko'ring); (3) embeddedApplicationEvent qiymatini noto'g'ri solishtirmayapsizmi.

S: Tokenni decode qildim, lekin signature noto'g'ri chiqyapti.
J: Eng ko'p uchraydigan xato — secret'ni hex'dan decode qilish. Kalitni oddiy 64-belgili string sifatida ishlating (2-bo'limga qarang).

S: Token 15 daqiqadan keyin ishlamay qoldi.
J: Bu normal. CRM frontend muddat tugashidan oldin yangi context-update xabarini yuboradi. Listener'ingiz ochiq bo'lsa, yangi token avtomatik keladi — uni qayta verify qiling.

S: leadContext yoki leadId ba'zan yo'q.
J: Kontekstga bog'liq optional. Lid sahifasi bo'lmasa leadContext: null keladi (yoki leadId claim yo'q). Kodingiz buni hisobga olsin (null/None check).

S: leadContext ma'lumotlariga ishonsam bo'ladimi?
J: Faqat display uchun. U imzolanmagan. Xavfsizlik uchun muhim qiymatlarni token claim'laridan oling va kerak bo'lsa leadContext.leadId ni token leadId bilan solishtiring.

S: Bir sahifada bir nechta embedded ilova bo'lsa-chi?
J: CRM har bir iframe uchun alohida (mos secret bilan imzolangan) token yasaydi va har biriga o'z postMessage xabarini yuboradi. Sizga esa faqat o'z kalitingiz bilan imzolangan token keladi.