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:
- Secret key — qayerdan olish va to'g'ri ishlatish
- postMessage payload tuzilishi va maydonlari
- Token verifikatsiya qadamlari va claim'lar
- Xavfsizlik tavsiyalari
- Tez-tez beriladigan savollar (FAQ)
1. Umumiy tasvir
- CRM frontend
GET /resolve?page=...ni chaqiradi, UYSOT BE sizning ilovangiz uchun JWT access token + kontekstni qaytaradi. - CRM frontend sizning
endpointURL'ingizniiframesifatida ochadi (URL'ga token qo'shilmaydi — toza endpoint). iframeyuklangach, CRM frontendwindow.postMessage(...)orqali sizningiframeingizga payload yuboradi. Payload ichidaaccessTokenvaleadContextbo'ladi.- Sizning ilovangiz (frontend yoki uning backendi) bu payload'ni qabul qiladi. URL toza
endpointbo'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
| Maydon | Tip | Doimo bormi? | Ma'nosi |
|---|---|---|---|
embeddedApplicationEvent | string | ✅ Ha | Xabar turi (event nomi). Doimo "uysot:embedded-application:context-update". Boshqa message event'laridan ajratish uchun shu maydonni filtrlang. |
openApiTokenId | number (int) | ✅ Ha | Sizning ilovangizga biriktirilgan OpenApiToken id'si |
name | string | ✅ Ha | Biriktirilgan OpenApiToken nomi (display uchun) |
avatar | string | null | ⚠️ Bo'lishi mumkin | Avatar (rasm) yo'li; bo'lmasa null |
url | string | ✅ Ha | Sizning iframe URL'ingiz (endpoint) |
accessToken | string (JWT) | ✅ Ha | Eng muhim — qisqa muddatli imzolangan JWT. Xavfsizlikni shu orqali boshqarasiz (4-bo'lim) |
expiresIn | number | ✅ Ha | Token amal qilish muddati soniyada (900 = 15 daqiqa) |
leadContext | object | null | ⚠️ Kontekstga bog'liq | Lid konteksti. Lid sahifasida bo'ladi; aks holda null |
leadContext maydonlari
| Maydon | Tip | Izoh |
|---|---|---|
leadId | number (long) | Lid identifikatori |
name | string | Lid nomi |
status | string | Pipeline status nomi (lokalizatsiyalangan matn, masalan "Yangi aloqa") |
responsibleId | number (int) | null | Mas'ul xodim id'si (bo'lmasa null) |
phone | string | null | Asosiy kontaktning birinchi telefoni (bo'lmasa null) |
phones | string[] | Lidning barcha telefon raqamlari (trim + takrorsiz) |
balance | number | Lid 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)
| Claim | Tip | Doimo bormi? | Ma'nosi |
|---|---|---|---|
companyId | number (int) | ✅ Ha | UYSOT'dagi kompaniya identifikatori (qaysi tenant) |
page | string | ✅ Ha | Qaysi sahifa ochilgani (enum, masalan LEAD_WIDGET_PANEL, STATS) |
exp | number (NumericDate) | ✅ Ha | Token muddati tugash vaqti (Unix epoch, soniyada) |
employeeId | number (int) | ⚠️ Bo'lishi mumkin | Tokenni so'ragan xodim id'si (mavjud bo'lsa) |
userId | number (int) | ⚠️ Ixtiyoriy | Foydalanuvchi id'si (hozircha odatda yuborilmaydi) |
leadId | number (long) | ⚠️ Kontekstga bog'liq | Qaysi 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)
postMessagepayload'idagiaccessTokenni oling (frontend → backend orqali).- JWT signature'ni HS256 va secret key (UTF-8 string) bilan tekshiring.
expni tekshiring — muddati o'tgan bo'lsa rad eting (401).companyIdvapageni o'qing — kerakli kontentni ko'rsating. Kontekst kerak bo'lsaleadIddan foydalaning.- Tavsiya:
pageqiymati siz kutgan enum'lardan biri ekanini tekshiring (LEAD_WIDGET_PANEL,STATS). - Tavsiya: agar
leadContextishlatsangiz,leadContext.leadIdni token'dagileadIdclaim 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)
- Node.js
- Java
- Python
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);
}
}
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
import java.nio.charset.StandardCharsets;
String secret = System.getenv("UYSOT_EMBEDDED_SECRET"); // 64-belgili hex STRING
// MUHIM: secret string'ning UTF-8 baytlari (hex decode EMAS)
Algorithm algorithm = Algorithm.HMAC256(secret.getBytes(StandardCharsets.UTF_8));
DecodedJWT jwt = JWT.require(algorithm)
.build()
.verify(token); // exp avtomatik tekshiriladi; muddati o'tgan bo'lsa exception
Integer companyId = jwt.getClaim("companyId").asInt();
String page = jwt.getClaim("page").asString();
Long leadId = jwt.getClaim("leadId").asLong(); // bo'lmasa null
import jwt # PyJWT
SECRET = os.environ["UYSOT_EMBEDDED_SECRET"] # 64-belgili hex string
def verify_access_token(token: str) -> dict:
# PyJWT kalitni str sifatida UTF-8 ga o'giradi — to'g'ri xatti-harakat
return jwt.decode(token, SECRET, algorithms=["HS256"])
# exp avtomatik tekshiriladi; muddati o'tgan bo'lsa jwt.ExpiredSignatureError
6. Xavfsizlik tavsiyalari
messageevent'ida origin'ni har doim tekshiring.event.originfaqat sizga berilgan UYSOT CRM domeni ekaniga ishonch hosil qiling. Aks holda istalgan sayt sizning iframe'ingizga soxta xabar yuborishi mumkin.embeddedApplicationEventmaydonini filtrlang. Faqat"uysot:embedded-application:context-update"qiymatli xabarlarni qayta ishlang.- Xavfsizlikni token orqali boshqaring. payload'dagi ochiq maydonlarga (
leadContext,companyIdko'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.
expni 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.