신원 확인
여러분의 백엔드가 제어하는 서명을 사용해, 로그인한 인물을 인식하고 여러 기기에 걸쳐 대화 이력을 복원하세요.
왜 확인이 필요한가#
기본적으로 SDK는 익명입니다: 안정적인 visitor_id를 생성해 플랫폼 키스토어에 보관합니다. 이는 한 기기에서 한 대화를 유지하기에는 충분합니다. 특정 사용자를 인식하고, 재로그인 또는 다른 기기에서 이력을 복원하며, 대화를 연락처 프로필에 안정적으로 결속하려면, 백엔드는 클라이언트가 자신이 주장하는 바로 그 사람임을 증명받아야 합니다. SDK가 단순히 “나는 사용자 42입니다”라고 보낸다면 누구든 다른 식별자를 사칭해 다른 사람의 채팅을 읽을 수 있으므로 — Respondo는 오직 여러분의 백엔드만 만들 수 있는 암호학적 서명인 userHash를 요구합니다.
identity secret 얻기#
identity_secret은 여러분의 에이전트(위젯 채널이 연결된 AI 워커)에 결속된 비밀 문자열입니다. 대시보드의 채널 → 위젯 → 신원 확인에서 생성하세요. 시크릿은 에이전트에 있으므로, 해당 에이전트를 사용하는 모든 채널 — 웹 위젯과 모바일 SDK 모두 — 이 같은 시크릿을 공유합니다. 이는 신원에 서명할 권한을 부여하므로 오직 여러분의 백엔드에만 있어야 하고 앱에 절대 포함되어서는 안 됩니다.
userHash 공식#
userHash는 단일 서명 대상 신원 문자열에 대한 HMAC-SHA256이며, 소문자 16진수로 인코딩됩니다:
userHash = HMAC_SHA256( identity_secret, payload )
payload = userId // userId가 설정되어 있으면
= email // 그렇지 않고 email이 설정되어 있으면
= (invalid) // 둘 다 비어 있으면 서명할 것이 없음- 시크릿이 HMAC 키이고, 신원 문자열이 메시지입니다 — 그 반대가 아닙니다.
- 정확히 하나의 문자열에 서명하세요 — 원시
userId(또는 email), 솔트나 JSON 래퍼 없이. userId와 email을 둘 다 전달한다면userId에 서명하세요(우선순위가 높습니다).- 출력은 16진수(SHA-256의 경우 64자)이며, base64가 아닙니다.
백엔드 예제#
여러분의 백엔드에서 서명을 계산하고 완성된 userHash를 앱에 넘기세요.
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
)
// HMAC_SHA256(identity_secret, userId)를 소문자 16진수로.
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");
// 모바일 클라이언트의 Respondo.identify에 넘길 userHash를 반환합니다.
function computeUserHash(identitySecret, userId) {
return crypto
.createHmac("sha256", identitySecret)
.update(userId, "utf8")
.digest("hex");
}import hmac
import hashlib
# 모바일 클라이언트의 Respondo.identify에 넘길 userHash를 반환합니다.
def compute_user_hash(identity_secret: str, user_id: str) -> str:
return hmac.new(
identity_secret.encode(),
user_id.encode(),
hashlib.sha256,
).hexdigest()<?php
// 모바일 클라이언트의 Respondo.identify에 넘길 userHash를 반환합니다.
function computeUserHash(string $identitySecret, string $userId): string {
return hash_hmac('sha256', $userId, $identitySecret);
}해시가 Respondo에 도달하는 방법#
/identity 경로도 없고 등록할 것도 없습니다. userHash는 SDK가 이미 보내고 있는 요청에 함께 실려 가는 필드입니다.두 구간이 있고, 여러분이 만들 것은 첫 번째뿐입니다:
- 여러분의 백엔드 → 여러분의 앱. 해시는 원하는 방식으로 전달하면 됩니다. 가장 저렴한 방법은 이미 반환하고 있는 로그인/부트스트랩 응답에 필드를 하나 추가하는 것입니다 — 추가 왕복이 없습니다. 여러분 자신의 백엔드에 전용 엔드포인트(예:
POST /myapp/identity가{ userId, userHash }를 반환)를 두는 것도 마찬가지로 잘 동작합니다. Respondo는 그 엔드포인트를 호스팅하지 않습니다 — 여러분이 구현합니다. - 여러분의 앱 → Respondo. SDK가 알아서 처리합니다.
identify를 호출하고 나면 SDK가 관련된 모든 요청에 해시를 붙이고, 백엔드는 매번 이를 다시 검증합니다.
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에 신원 전달하기#
여러분의 백엔드에서 서명된 신원을 가져온 다음, 어떤 플랫폼에서든 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 동작#
에이전트에서 검증이 활성화되어 있는데 서명이 없거나 잘못된 경우, 백엔드는 익명으로의 조용한 강등을 수행합니다: 채팅은 여전히 동작하고, visitor_id는 유지되며, 대화는 익명 연락처에 결속됩니다 — 하지만 프로필로의 연결도, 기기 간 이력도 없습니다. 사용자가 “인식되지 않는다”면 거의 항상 서명 문제입니다: userId에 서명했는지(email이나 JSON이 아니라), 올바른 identity_secret을 사용했는지, 소문자 16진수를 출력했는지 확인하세요. 에이전트의 identity_secret이 비어 있으면 검증이 꺼져 있고 userId / email이 있는 그대로 받아들여집니다.
identity_secret을 교체하는 것은 사실상 완전한 전환입니다: 이전 시크릿으로 생성된 모든 해시가 즉시 검증에 실패하므로, 이미 로그인한 사용자들은 여러분의 백엔드가 새 시크릿으로 userHash를 다시 계산해 재공급할 때까지 조용히 익명으로 되돌아갑니다. 서명 측을 같은 시간대에 업데이트할 수 있을 때만 시크릿을 교체하세요.