@ikenxuan/amagi

v6.6.0

2026-09-01 发布的实验性抖音 Passport 扫码登录功能

v6.6.0 变更日志

抖音 Passport 扫码登录是 实验性功能,不保证稳定性。它依赖抖音 Web 端的内部登录接口、签名参数和风控策略,服务端行为变化可能随时导致功能失效。后续版本可能调整、重命名或移除相关方法、参数、返回值和底层实现,请不要在对稳定性有要求的生产登录链路中依赖当前 API。

新增:抖音 Passport 扫码登录

本版本新增一套基于 login.douyin.com Passport 体系的抖音扫码登录流程,支持:

  • 获取 Passport 登录二维码;
  • 轮询二维码状态;
  • 处理扫码确认后的 SSO 跳转;
  • 处理部分账号触发的短信二次验证;
  • 提交短信验证码并获取登录 Cookie;
  • 在调用之间传递会话 Cookie 和 bd-ticket-guard 设备票据状态。

这套流程与原有的 requestLoginQrcode 不同。后者是旧的登录二维码接口;Passport 登录提供的是一套从取码到获取登录态的完整流程。

重要限制:目前独立于 Amagi Client 架构

Passport 登录当前是独立接入的实验性模块,尚未完整纳入 Amagi Client 的实例级架构。它与现有通过 client.douyin.fetcher 使用的稳定数据接口不是同一套内部实现:

  • 当前推荐使用导出的 Passport 方法或 douyinFetcher;
  • 不支持通过 client.douyin.fetcher 的实例 Cookie 状态自动维护登录会话;
  • 不支持通过 createBoundDouyinFetcher 绑定并自动维护这套登录流程;
  • 登录过程中的会话状态必须由调用方保存每次返回的 cookie,并在下一次调用时传回;
  • 轮询循环也需要由调用方自行维护。

后续可能会将这套能力重构并接入统一的客户端、Fetcher 或会话管理架构。因此当前方法的调用方式和类型不能视为长期兼容的稳定 API。

当前 API

可以从包根路径导入以下方法:

import {
  checkPassportQrcode,
  requestPassportQrcode,
  sendPassportVerifyCode,
  validatePassportVerifyCode
} from '@ikenxuan/amagi'

也可以通过 douyinFetcher 使用这些方法:

import { douyinFetcher } from '@ikenxuan/amagi'

const qrcode = await douyinFetcher.requestPassportQrcode()

获取二维码

const result = await requestPassportQrcode()

if (!result.success) {
  throw new Error(result.message)
}

console.log(result.data.content)
console.log(result.data.expires_in)

// 必须保存,用于后续轮询;其中可能包含 Passport 会话状态和实验性模块的内部状态
const cookie = result.data.cookie
const token = result.data.token

返回数据包括:

字段说明
token轮询二维码状态时使用的令牌
content二维码内容,可交给二维码生成器渲染
expire_time服务端返回的绝对 Unix 时间戳,单位为秒
expires_in获取二维码时计算出的剩余秒数,是一个时间快照
cookie后续调用必须继续传递的会话 Cookie

轮询二维码状态

const status = await checkPassportQrcode({ token }, cookie)

if (!status.success) {
  throw new Error(status.message)
}

switch (status.data.status) {
  case 'new':
  case 'scanned':
  case 'busy':
    // 根据 status.data.interval 等待后继续轮询
    cookie = status.data.cookie
    break
  case 'verify':
    // 使用 status.data.verify 进入短信二次验证流程
    cookie = status.data.cookie
    break
  case 'confirmed':
    cookie = status.data.cookie
    console.log('登录完成:', status.data.logged_in)
    break
  case 'expired':
  case 'risk':
  case 'unknown':
    console.log(status.data.message)
    break
}

每次调用后都应使用响应中最新的 cookie。二维码状态可能包括:

  • new:尚未扫码;
  • scanned:已扫码,等待手机端确认;
  • confirmed:已确认并尝试完成登录;
  • verify:需要进行二次验证;
  • expired:二维码已过期;
  • busy:轮询过于频繁,按 interval 退避后重试;
  • risk:触发风控;
  • unknown:服务端返回了暂未适配的状态。

短信二次验证

当轮询结果为 verify 时,可以把返回的验证上下文传给发码接口:

const sendCode = await sendPassportVerifyCode(
  {
    verify: status.data.verify
  },
  status.data.cookie
)

if (!sendCode.success || !sendCode.data.ok) {
  throw new Error(sendCode.success ? sendCode.data.message : sendCode.message)
}

const validateCode = await validatePassportVerifyCode(
  {
    verify: status.data.verify,
    code: '123456',
    biz_trace_id: sendCode.data.biz_trace_id,
    verify_way: sendCode.data.verify_way
  },
  sendCode.data.cookie
)

if (!validateCode.success || !validateCode.data.ok) {
  throw new Error(validateCode.success ? validateCode.data.message : validateCode.message)
}

cookie = validateCode.data.cookie

verify_way 应优先使用服务端返回的实际值。不同账号可能返回不同的验证方式,例如 mobile_sms_verify 或 assist_mobile_sms_verify。biz_trace_id 也应在发送验证码和提交验证码时保持一致。

兼容性与稳定性说明

当前实现包含 Passport 专用的签名、a_bogus、环境指纹、Cookie 管理和 bd-ticket-guard 票据处理逻辑。这些内容不属于 Amagi 面向稳定数据接口的公共抽象,抖音也没有为这些 Web 内部接口提供稳定的第三方 API 契约。

使用时请注意:

  • 抖音可能修改接口地址、请求字段、签名算法、Cookie 语义或风控规则;
  • 同一账号、设备、网络环境下的可用验证方式可能不同;
  • 获取二维码成功不代表后续轮询或登录一定成功;
  • confirmed、ok 和 logged_in 等字段应结合实际返回结果判断,不应只根据 HTTP 或外层 code 判断;
  • 请妥善保管返回的登录 Cookie,不要写入日志、提交到代码仓库或暴露给不可信的第三方;
  • 发生异常时可以启用 debug 日志辅助排查,但仍应避免记录完整 Cookie 或验证码。

如果需要稳定的抖音数据访问能力,请继续使用现有的抖音 Fetcher API。Passport 登录仅建议用于实验、开发验证和能够接受接口变化的场景。

最后更新于 2026-09-17

On this page