تأیید هویت
کسی را که وارد حسابش شده بشناسید و تاریخچهٔ گفتوگویش را روی هر دستگاهی بازگردانید — با امضایی که در اختیار backend خودتان است.
چرا تأیید لازم است#
SDK بهطور پیشفرض ناشناس کار میکند: یک visitor_id پایدار میسازد و آن را در keystore پلتفرم نگه میدارد. همین برای یک گفتوگو روی یک دستگاه کافی است. اما برای شناختن یک کاربر مشخص، بازگرداندن تاریخچه هنگام ورود دوباره یا روی دستگاهی دیگر، و پیوند مطمئن یک گفتوگو به پروندهٔ مخاطب، backend به مدرکی نیاز دارد که ثابت کند کلاینت همان کسی است که ادعا میکند. اگر SDK فقط میگفت «من کاربر 42 هستم»، هر کسی میتوانست شناسهٔ دیگری جا بزند و چت شخص دیگری را بخواند — به همین دلیل Respondo یک امضای رمزنگاشتی میخواهد، یعنی userHash، که فقط backend شما میتواند بسازد.
دریافت identity secret#
identity_secret رشتهای محرمانه است که به عامل شما گره خورده (همان کارگر هوش مصنوعی که کانال ویجت شما به آن وصل است). آن را در پنل مدیریت، زیر کانالها ← ویجت ← تأیید هویت بسازید. چون این راز روی خود عامل مینشیند، هر کانالی که پشتش همان عامل باشد — چه ویجت وب و چه SDK موبایل — همان راز مشترک را دارد. این راز حق امضای هویتها را میدهد، پس باید فقط روی backend شما بماند و هرگز نباید داخل اپلیکیشن منتشر شود.
فرمول userHash#
userHash یک HMAC-SHA256 روی تنها یک رشتهٔ هویتِ امضاشونده است که بهصورت hex با حروف کوچک کد میشود:
userHash = HMAC_SHA256( identity_secret, payload )
payload = userId // اگر userId تنظیم شده باشد
= email // وگرنه، اگر email تنظیم شده باشد
= (invalid) // اگر هر دو خالی باشند، چیزی برای امضا نیست- راز همان کلید HMAC است و رشتهٔ هویت همان پیام — نه برعکس.
- دقیقاً یک رشته را امضا کنید: خودِ
userIdخام (یا ایمیل)، بدون نمک (salt) و بدون بستهبندی JSON. - اگر هم
userIdو هم ایمیل را میفرستید،userIdرا امضا کنید (اولویت با اوست). - خروجی hex است (64 کاراکتر برای SHA-256)، نه base64.
نمونههای backend#
امضا را روی backend خودتان حساب کنید و userHash آماده را به اپلیکیشن بدهید.
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
)
// HMAC_SHA256(identity_secret, userId) بهصورت hex با حروف کوچک.
func ComputeUserHash(identitySecret, userID string) string {
mac := hmac.New(sha256.New, []byte(identitySecret))
mac.Write([]byte(userID))
return hex.EncodeToString(mac.Sum(nil))
}const crypto = require("crypto");
// مقدار userHash را برمیگرداند تا در کلاینت موبایل به Respondo.identify داده شود.
function computeUserHash(identitySecret, userId) {
return crypto
.createHmac("sha256", identitySecret)
.update(userId, "utf8")
.digest("hex");
}import hmac
import hashlib
# مقدار userHash را برمیگرداند تا در کلاینت موبایل به Respondo.identify داده شود.
def compute_user_hash(identity_secret: str, user_id: str) -> str:
return hmac.new(
identity_secret.encode(),
user_id.encode(),
hashlib.sha256,
).hexdigest()<?php
// مقدار userHash را برمیگرداند تا در کلاینت موبایل به Respondo.identify داده شود.
function computeUserHash(string $identitySecret, string $userId): string {
return hash_hmac('sha256', $userId, $identitySecret);
}هش چطور به Respondo میرسد#
/identity هست که صدایش بزنید و نه چیزی برای ثبتکردن. userHash یک فیلد است که سوار همان درخواستهایی میشود که SDK از پیش میفرستد.دو پرش در کار است و فقط اولی را شما باید بسازید:
- backend شما → اپلیکیشن شما. هش را هر جور خواستید تحویل بدهید. ارزانترین گزینه یک فیلد اضافه روی همان پاسخ ورود یا راهاندازی است که از قبل برمیگردانید — بدون رفتوبرگشت اضافه. یک endpoint اختصاصی روی backend خودتان (مثلاً
POST /myapp/identityکه{ userId, userHash }برمیگرداند) هم به همان اندازه خوب کار میکند. Respondo میزبان آن endpoint نیست — پیادهسازیاش با شماست. - اپلیکیشن شما → Respondo. این یکی برایتان انجام شده است. همینکه
identifyرا صدا بزنید، SDK هش را به هر درخواست مربوطه میچسباند و backend هر بار دوباره آن را وارسی میکند.
POST /api/v1/chat body identity.userHash
GET /api/v1/chat/resume query user_hash
GET /api/v1/chat/history query user_hash
GET /api/v1/chat/ws frames user_hash (subscribe/identify JSON frames after connect — not in the handshake URL)
GET /api/v1/widget/tours query user_hash
GET /api/v1/widget/checklists query user_hash
POST /api/v1/widget/push/register body user_hashهش در هر درخواست دوباره بررسی میشود و یک بار برای همیشه با یک نشست معاوضه نمیشود — همین است که یک userId دزدیدهشده را بهتنهایی بیفایده میکند.
identify هرگز صدا زده نمیشود و چت بهصورت ناشناس کار میکند. این رفتار مورد انتظار است، نه خطای Respondo — یک 404 روی مسیر خودتان یعنی کاری که سمت شما مانده، نه یکپارچهسازی خراب.ارسال هویت به SDK#
هویت امضاشده را از backend خودتان بگیرید، سپس userHash را روی هر پلتفرمی به identify بدهید:
Respondo.identify(
RespondoIdentity(userId = "42", email = "user@example.com", userHash = hash),
)Respondo.identify(
RespondoIdentity(userId: "42", email: "user@example.com", userHash: hash)
)Respondo.identify(RespondoIdentity(
userId: '42', email: 'user@example.com', userHash: hash,
));رفتار در برابر userHash نامعتبر#
وقتی تأیید روی عامل فعال باشد و امضا نباشد یا نادرست باشد، backend یک تنزل بیسروصدا به حالت ناشناس انجام میدهد: چت همچنان کار میکند، visitor_id حفظ میشود و گفتوگو به مخاطب ناشناس بسته میشود — اما هیچ پیوندی به پروفایل و هیچ تاریخچهٔ بیندستگاهی در کار نیست. اگر کاربری «شناخته نمیشود»، تقریباً همیشه مقصر امضاست: بررسی کنید که userId را امضا کرده باشید (نه ایمیل و نه JSON)، از identity_secret درست استفاده کرده باشید و خروجی را hex با حروف کوچک داده باشید. اگر identity_secret عامل خالی باشد، تأیید خاموش است و userId / ایمیل همانطور که هستند پذیرفته میشوند.
چرخاندن identity_secret عملاً یک قطعوصل ناگهانی است: هر هشی که با راز قدیمی ساخته شده بیدرنگ از اعتبار میافتد، پس کاربرانی که از قبل وارد شدهاند بیسروصدا به حالت ناشناس برمیگردند تا وقتی backend شما userHash آنها را با راز جدید دوباره حساب و ارسال کند. راز را فقط وقتی عوض کنید که بتوانید سمت امضا را در همان بازه بهروز کنید.