TRUSTTURNSecurity Verification Service

INTEGRATION GUIDE

把一次验证接入你的站点

每个业务站点拥有独立的 siteKey 和绑定域名。服务端结合会话状态、一次性凭证与风险策略保护关键流程;成功响应只返回短时 verification ticket。管理后台还提供实时运行状态、授权、审计和风险事件视图。

01 / QUICK START

快速开始

在管理后台新增站点并绑定业务域名。创建成功后立即保存一次性显示的 app_secret,并把可公开的 site_key 配置到前端和业务后端。

浏览器环境变量:TRUSTTURN_SITE_KEY=sl_your_site_key
业务后端环境变量:TRUSTTURN_SITE_KEY=sl_your_site_key
业务后端密钥:TRUSTTURN_APP_SECRET=<只显示一次的 app_secret>
site_key 只用于标识站点,可以公开;app_secret 是业务后端凭证,不能进入浏览器、客户端包、日志或版本库。

02 / SDK RENDER

动态创建 iframe 嵌入 TRUSTTURN UI

对接方提供一个容器并由 JavaScript 动态创建组件。这样可以先绑定验证事件,再加载 iframe,并统一处理站点配置和加载状态。

<div id="trustturn-box"></div>
<script>
  const apiOrigin = 'https://api.trustturn.cc';
  const siteKey = 'sl_portal_8f1a4c2d';
  const mount = document.querySelector('#trustturn-box');
  window.addEventListener('message', event => {
    if (event.origin !== apiOrigin) return;
    if (event.data?.type === 'trustturn:verified') {
      const ticket = event.data.detail?.verificationTicket;
      // 将 ticket 交给你的业务后端
    }
  });
  const frame = document.createElement('iframe');
  frame.src = apiOrigin + '/widget.html?site_key=' + encodeURIComponent(siteKey)
    + '&action=' + encodeURIComponent('login');
  frame.title = 'TRUSTTURN 人机验证';
  frame.style.cssText = 'width:100%;min-height:88px;border:0';
  mount.appendChild(frame);
</script>

组件只能从该 siteKey 已绑定的业务域名加载。验证完成后,组件会向父页面发送 trustturn:verified 消息,消息来源必须严格校验为 https://api.trustturn.cc。

window.addEventListener('message', event => {
  if (event.origin !== 'https://api.trustturn.cc') return;
  if (event.data?.type === 'trustturn:verified') {
    const ticket = event.data.detail?.verificationTicket;
    // 将 ticket 交给你的业务后端
  }
});

03 / SERVER VERIFY

业务后端检查并消费 ticket

新增站点时,服务端会随机生成可公开的 site_key 和只展示一次的 app_secret。浏览器把 ticket 提交给你的业务后端后,由业务后端动态生成时间戳、nonce 和签名,再调用:

POST/api/tickets/consume
curl https://api.trustturn.cc/api/tickets/consume \
  -H 'Content-Type: application/json' \
  -H 'X-TrustTurn-Timestamp: <13 位 Unix 毫秒>' \
  -H 'X-TrustTurn-Nonce: <随机 nonce>' \
  -H 'X-TrustTurn-Signature: v1=<HMAC-SHA256 签名>' \
  -d '{
    "site_key": "sl_your_site_key",
    "ticket": "<浏览器提交的 ticket>",
    "expected_action": "login"
  }'

// 成功响应
{
  "success": true,
  "site_id": "mailroom",
  "site_key": "sl_your_site_key",
  "action": "login",
  "origin": "https://mail.example.com",
  "hostname": "mail.example.com",
  "challenge_ts": "2026-10-19T03:00:00.000Z",
  "consumed_at": "2026-10-19T03:00:08.000Z",
  "risk": { "decision": "allow", "score": 0 }
}

只有 success === true、site_key、action 和 origin 都与当前业务请求一致时才能继续。ticket 默认 60 秒有效,成功响应时立即消费;重复调用会返回 invalid_ticket。

签名规范串依次为协议域、方法、路径、时间戳、nonce、原始请求体摘要,每个字段用换行连接:

TRUSTTURN-TICKET-CONSUME-V1
POST
/api/tickets/consume
<timestamp>
<nonce>
SHA256(<原始 JSON 请求体字节>)

sign = HMAC-SHA256(app_secret, 上述规范串)

SHA-256 摘要只是绑定原始请求体的公开校验值,不是密码、密钥或加密;真正的请求认证由站点专属 app_secret 计算的 HMAC-SHA256 完成。服务端只接受当前时间前后 2 分钟内的请求,同一站点的 nonce 只能认证一次。签名值不能用于推导 app_secret,修改 ticket、动作或站点都会导致签名失效。ticket 是 64 个以上 Base64URL 字符的随机一次性值,默认 60 秒有效并在成功消费后立即失效。

site_key 可以进入浏览器。app_secret、sign、timestamp 和 nonce 都只能在业务后端生成或保存,禁止放进 HTML、Vue/React 源码、移动端包或返回给浏览器。密钥轮换后旧密钥立即失效。

04 / BUSINESS LOGIN

业务登录接口中的正确顺序

// 浏览器 -> 你的业务后端
POST /auth/login
{ "email": "user@example.com", "password": "...",
  "verification_ticket": "<ticket>", "site_key": "sl_your_site_key" }

// 你的业务后端:先签名并消费 ticket,再检查密码并创建业务会话
import crypto from 'node:crypto';
const consumeBody = JSON.stringify({
  site_key: process.env.TRUSTTURN_SITE_KEY,
  ticket: body.verification_ticket,
  expected_action: 'login'
});
const timestamp = String(Date.now());
const nonce = crypto.randomBytes(18).toString('base64url');
const bodyHash = crypto.createHash('sha256').update(consumeBody).digest('hex');
const canonical = ['TRUSTTURN-TICKET-CONSUME-V1', 'POST',
  '/api/tickets/consume', timestamp, nonce, bodyHash].join('\n');
const sign = crypto.createHmac('sha256', process.env.TRUSTTURN_APP_SECRET)
  .update(canonical).digest('hex');
const result = await fetch('https://api.trustturn.cc/api/tickets/consume', {
  method: 'POST',
  headers: {
    'content-type': 'application/json',
    'x-trustturn-timestamp': timestamp,
    'x-trustturn-nonce': nonce,
    'x-trustturn-signature': `v1=${sign}`
  },
  body: consumeBody
}).then(response => response.json());
if (result.success !== true
  || result.site_key !== process.env.TRUSTTURN_SITE_KEY
  || result.action !== 'login'
  || result.origin !== 'https://mail.example.com') {
  throw new Error('验证码无效');
}

/api/login 是 TrustTurn 自带演示账号的登录接口,不是第三方业务后端的 ticket 校验接口。Mailroom 等独立系统必须使用 /api/tickets/consume。

05 / OPERATIONS

错误码与运行边界

  • invalid_request_signature:签名错误、时间戳超窗、nonce 格式错误或站点尚未配置 app_secret。
  • replayed_request:同一站点的请求 nonce 已使用。
  • invalid_ticket:ticket 不存在、已过期、已消费或属于其他站点。
  • action_mismatch:ticket 的业务动作与 expected_action 不一致。
  • challenge_binding:验证凭证与当前站点不一致。
  • replayed_token:一次性 challenge 凭证已使用。
  • expired_token:验证超时,组件会自动重新初始化。
  • site_domain_not_allowed:当前域名未绑定到该站点。
  • site_authorization_expired:站点业务授权已到期。
  • site_authorization_frozen:站点业务授权已冻结。
  • site_authorization_revoked:站点业务授权已吊销。
  • session_binding:当前会话环境发生变化。
  • environment_risk:当前访问环境未通过风险校验。

管理员可以在后台配置站点域名、授权范围和风险策略,并查看验证事件。正式部署前请配置管理员强认证、访问审计、数据保留周期与适用的告知机制。