การยืนยันตัวตน
จดจำบุคคลที่ล็อกอินแล้วและกู้คืนประวัติบทสนทนาของพวกเขาข้ามอุปกรณ์ โดยใช้ลายเซ็นที่ backend ของคุณควบคุม
ทำไมต้องยืนยัน#
โดยค่าเริ่มต้น SDK เป็นนิรนาม: มันสร้าง visitor_id ที่เสถียรและเก็บไว้ใน keystore ของแพลตฟอร์ม นั่นเพียงพอสำหรับหนึ่งบทสนทนาบนหนึ่งอุปกรณ์ เพื่อจดจำผู้ใช้ที่เฉพาะเจาะจง กู้คืนประวัติเมื่อล็อกอินใหม่หรือบนอุปกรณ์อื่น และผูกบทสนทนาเข้ากับโปรไฟล์ contact ได้อย่างเชื่อถือได้ backend จำเป็นต้องมีหลักฐานว่า client เป็นผู้ที่อ้างว่าเป็นจริง ถ้า SDK เพียงส่งว่า "ฉันคือ user 42" ใครก็ตามก็สามารถปลอมเป็น id อื่นและอ่านแชทของคนอื่นได้ — ดังนั้น Respondo จึงกำหนดให้ต้องมีลายเซ็นเชิงเข้ารหัส userHash ที่มีเพียง backend ของคุณเท่านั้นที่สร้างได้
การได้มาซึ่ง identity secret#
identity_secret คือสตริงลับที่ผูกกับ เอเจนต์ ของคุณ (AI worker ที่ช่องวิดเจ็ตของคุณเชื่อมต่ออยู่) สร้างมันในแดชบอร์ดที่ Channels → Widget → Identity verification เนื่องจากมันอยู่บนเอเจนต์ ทุกช่องทางที่ใช้เอเจนต์นั้น — ทั้งวิดเจ็ตเว็บและ SDK มือถือ — จึงใช้ secret เดียวกันร่วมกัน มันให้สิทธิ์ในการเซ็นตัวตน จึงต้องอยู่บน backend ของคุณเท่านั้น และต้องไม่ถูกส่งไปในแอปเด็ดขาด
สูตร userHash#
userHash คือ HMAC-SHA256 บนสตริงตัวตนที่ถูกเซ็นเพียงสตริงเดียว เข้ารหัสเป็น hex ตัวพิมพ์เล็ก:
userHash = HMAC_SHA256( identity_secret, payload )
payload = userId // ถ้ามีการตั้ง userId
= email // มิฉะนั้น ถ้ามีการตั้ง email
= (invalid) // ถ้าทั้งคู่ว่าง ก็ไม่มีอะไรให้เซ็น- secret คือ key ของ HMAC; สตริงตัวตนคือ message — ไม่ใช่สลับกัน
- เซ็นเพียงสตริงเดียวเท่านั้น — คือ
userIdดิบ (หรืออีเมล) โดยไม่มี salt หรือ JSON wrapper - หากคุณส่งทั้ง
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 บน client มือถือ
function computeUserHash(identitySecret, userId) {
return crypto
.createHmac("sha256", identitySecret)
.update(userId, "utf8")
.digest("hex");
}import hmac
import hashlib
# คืน userHash เพื่อส่งเข้า Respondo.identify บน client มือถือ
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 บน client มือถือ
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 ส่วนนี้จัดการให้คุณแล้ว เมื่อคุณเรียก
identifySDK จะแนบแฮชไปกับทุกคำขอที่เกี่ยวข้อง และ 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 ถูกเก็บไว้ และบทสนทนาถูกผูกเข้ากับ contact นิรนาม — แต่ไม่มีลิงก์ไปยังโปรไฟล์และไม่มีประวัติข้ามอุปกรณ์ หากผู้ใช้ "ไม่ได้รับการจดจำ" แทบทุกครั้งมักเป็นเรื่องลายเซ็น: ตรวจสอบว่าคุณเซ็น userId (ไม่ใช่อีเมลหรือ JSON), ใช้ identity_secret ที่ถูกต้อง และสร้าง hex ตัวพิมพ์เล็ก หาก identity_secret ของเอเจนต์ว่างเปล่า การยืนยันจะปิดอยู่ และ userId / อีเมล จะถูกยอมรับตามที่เป็น
การหมุน identity_secret มีผลเป็นการตัดสลับแบบเด็ดขาด: ทุก hash ที่สร้างด้วย secret เก่าจะหยุดยืนยันในทันที ดังนั้นผู้ใช้ที่ล็อกอินอยู่แล้วจะถอยกลับเป็นนิรนามอย่างเงียบ ๆ จนกว่า backend ของคุณจะคำนวณและส่ง userHash ของพวกเขาใหม่ด้วย secret ใหม่ ควรหมุน secret เฉพาะเมื่อคุณสามารถอัปเดตฝั่งที่ทำการเซ็นได้ในช่วงเวลาเดียวกันเท่านั้น