التوثيق

التحقق من الهوية

تعرّف على شخص مسجّل الدخول واستعِد سجل محادثته عبر الأجهزة، باستخدام توقيع تتحكم فيه خلفيتك.

لماذا التحقق#

افتراضياً تكون حزمة SDK مجهولة: فهي تولّد visitor_id ثابتاً وتحتفظ به في مخزن مفاتيح المنصة. وهذا يكفي لمحادثة واحدة على جهاز واحد. أما للتعرّف على مستخدم بعينه، واستعادة السجل عند إعادة تسجيل الدخول أو على جهاز آخر، وربط محادثة بملف جهة اتصال بشكل موثوق، فتحتاج الخلفية إلى إثبات أن العميل هو من يدّعي أنه هو. لو أن حزمة SDK أرسلت ببساطة «أنا المستخدم 42»، لأمكن لأي أحد انتحال معرّف آخر وقراءة محادثة شخص آخر — لذا يشترط Respondo توقيعاً تعموياً، userHash، لا يمكن أن تنتجه سوى خلفيتك.

الحصول على identity_secret#

الـ identity_secret هو سلسلة سرية مرتبطة بـوكيلك (العامل الذكي المتصل به قناة الأداة لديك). ولّده من لوحة التحكّم عبر القنوات ← الأداة ← التحقق من الهوية. وبما أنه يقيم على الوكيل، فإن كل قناة يقف خلفها ذلك الوكيل — أداة الويب وحزمة SDK للجوال على حد سواء — تتشارك السر نفسه. وهو يمنح الحق في توقيع الهويات، لذا يجب أن يبقى على خلفيتك فقط ويجب ألا يُشحن أبداً في التطبيق.

صيغة userHash#

الـ userHash هو HMAC-SHA256 على سلسلة هوية موقّعة واحدة، مُرمَّزة كنظام ست عشري بأحرف صغيرة:

الصيغةtext
userHash = HMAC_SHA256( identity_secret, payload )

payload = userId          // إذا كان userId مضبوطاً
        = email           // وإلا، إذا كان email مضبوطاً
        = (غير صالح)      // إذا كان كلاهما فارغاً، فلا شيء لتوقيعه
  • السر هو مفتاح HMAC؛ وسلسلة الهوية هي الرسالة — لا العكس.
  • وقّع سلسلة واحدة بالضبط — قيمة userId الخام (أو البريد الإلكتروني)، بلا مِلح ولا غلاف JSON.
  • إذا مرّرت كلًّا من userId والبريد الإلكتروني، فوقّع userId (له الأولوية).
  • الخرج ست عشري (64 حرفاً لـ SHA-256)، وليس base64.

أمثلة الخلفية (backend)#

احسب التوقيع على خلفيتك وسلّم userHash الجاهز إلى التطبيق.

Gogo
import (
    "crypto/hmac"
    "crypto/sha256"
    "encoding/hex"
)

// HMAC_SHA256(identity_secret, userId) كنظام ست عشري بأحرف صغيرة.
func ComputeUserHash(identitySecret, userID string) string {
    mac := hmac.New(sha256.New, []byte(identitySecret))
    mac.Write([]byte(userID))
    return hex.EncodeToString(mac.Sum(nil))
}
Node.jsjavascript
const crypto = require("crypto");

// يعيد userHash لتمريره إلى Respondo.identify على عميل الجوال.
function computeUserHash(identitySecret, userId) {
  return crypto
    .createHmac("sha256", identitySecret)
    .update(userId, "utf8")
    .digest("hex");
}
Pythonpython
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()
PHPphp
<?php
// يعيد userHash لتمريره إلى Respondo.identify على عميل الجوال.
function computeUserHash(string $identitySecret, string $userId): string {
    return hash_hmac('sha256', $userId, $identitySecret);
}

كيف يصل الهاش إلى Respondo#

لا يوفّر Respondo أي نقطة نهاية للهوية. فلا يوجد مسار /identity لاستدعائه ولا شيء لتسجيله. الـ userHash هو حقل يركب مع الطلبات التي ترسلها حزمة SDK أصلاً.

قفزتان، والأولى وحدها هي ما عليك بناؤه:

  1. خلفيتك ← تطبيقك. توصِّل الهاش بالطريقة التي تناسبك. وأرخص خيار هو حقل إضافي في استجابة تسجيل الدخول/الإقلاع التي تعيدها أصلاً — بلا رحلة ذهاب وإياب إضافية. وتصلح كذلك نقطة نهاية مخصصة على خلفيتك أنت (مثل POST /myapp/identity تعيد { userId, userHash }). لا يستضيف Respondo تلك النقطة — أنت من ينفّذها.
  2. تطبيقك ← Respondo. يتكفّل به النظام نيابة عنك. فبمجرد استدعاء identify، ترفق حزمة SDK الهاش بكل طلب ذي صلة وتعيد الخلفية التحقق منه في كل مرة.
أين تضعه حزمة SDK (للاطلاع فقط — لا ترسل هذه يدوياً)text
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 المسروق عديم الفائدة بمفرده.

إذا لم تُبنَ بعد نقطة نهاية التوصيل على خلفيتك أنت، فسيعيد طلب تطبيقك نفسه 404، ولن يُستدعى identify أبداً، وستعمل المحادثة بشكل مجهول. هذا سلوك متوقع لا خطأ من Respondo — فالـ 404 على مسارك الخاص مهمة معلّقة لديك، لا تكاملاً معطوباً.

تمرير الهوية إلى SDK#

اجلب الهوية الموقّعة من خلفيتك، ثم مرّر userHash إلى identify على أي منصة:

Kotlinkotlin
Respondo.identify(
    RespondoIdentity(userId = "42", email = "user@example.com", userHash = hash),
)
Swiftswift
Respondo.identify(
    RespondoIdentity(userId: "42", email: "user@example.com", userHash: hash)
)
Dartdart
Respondo.identify(RespondoIdentity(
  userId: '42', email: 'user@example.com', userHash: hash,
));

سلوك userHash غير الصالح#

عند تفعيل التحقق على الوكيل وكون التوقيع مفقوداً أو خاطئاً، تُجري الخلفية تخفيضاً صامتاً إلى مجهول: تظل المحادثة تعمل، ويُحتفظ بـ visitor_id، وتُربط المحادثة بجهة الاتصال المجهولة — لكن لا رابط بالملف ولا سجل عبر الأجهزة. إذا «لم يُتعرَّف» على مستخدم، فالسبب دائماً تقريباً هو التوقيع: تحقّق من أنك وقّعت userId (لا البريد الإلكتروني أو JSON)، واستخدمت identity_secret الصحيح، وأصدرت نظاماً ست عشرياً بأحرف صغيرة. وإذا كان identity_secret الخاص بالوكيل فارغاً، فالتحقق معطّل ويُقبل userId / البريد الإلكتروني كما هما.

تدوير identity_secret يُعد فعلياً تحوّلاً قاطعاً: إذ يتوقف كل تجزئة أُنتجت بالسر القديم عن التحقق فوراً، فيهبط المستخدمون المسجّلون بالفعل بصمت إلى مجهول حتى تُعيد خلفيتك حساب userHash الخاص بهم وتزوّدهم به من جديد بالسر الجديد. لا تُدوّر السر إلا حين تستطيع تحديث جانب التوقيع في النافذة نفسها.