纯前端 TOTP 验证码管理:浏览器插件(Chrome/Edge/Firefox,双端均 Manifest V3)+ Tauri 桌面程序。无自建服务端,数据完全留在本机与自备网盘。
- 界面:Material Design 3 设计系统,五页导航(验证码 / 导入 / 同步 / 安全 / 设置),10 种主题色 × 明暗模式(暗色可叠加 AMOLED 纯黑对比度档),主题 CSS 按需懒加载
- 安全:两把口令(库口令 + 备份口令)、vault 落盘加密(envelope v2,三档 Argon2id 强度)、DEK 保管区、Passkey(PRF)/ 原生自动解锁、锁定策略(重启后 / 系统锁屏 / 空闲 N 分钟)、剪贴板自动清空与密钥遮蔽
- 数据:本地备份(多目录源、每源保留策略)、五后端云同步(同类型多目标、每源独立基线与保留策略)、浏览器间同步、17+ 格式导入(四档去重判定树)、otpauth 链接 / 图片二维码 / 剪贴板多入口录入
- 桌面:托盘常驻 + 只读 mini 弹窗(全局快捷键
Alt+Shift+T)、窗口资源释放(隐藏 → 暂停 → 销毁三段式)、内置 MCP 服务器(本机 AI 客户端可读取验证码)
设计文档见 docs/plans/2026-09-13-totp-tool-design.md;实施计划按里程碑分计划存于 docs/plans/;审查与端到端验证报告见 docs/review/。
- 从 GitHub Releases 下载扩展 zip:
totp-extension-chromium-<版本>.zip/totp-extension-firefox-<版本>.zip(Release 附sha256sums.txt校验)- Chrome:解压后进入
chrome://extensions开启「开发者模式」→「加载已解压的扩展程序」选中解压目录 - Edge:已上架 Microsoft Edge 加载项,直接安装即可
- Firefox:要求 Firefox 140+(
strict_min_version: 140.0;Firefox for Android 需 142+,数据收集声明的平台要求);已上架 Firefox Add-ons(AMO),直接安装即可;未签名 zip 也可经about:debugging→「此 Firefox」→「临时载入附加组件」加载
- Chrome:解压后进入
- 自建:Chrome 目标
pnpm --filter @totp/extension build产出.output/chrome-mv3;Firefox 目标另跑pnpm --filter @totp/extension exec wxt build -b firefox产出.output/firefox-mv3(CI 对双目标分别构建并断言 manifest 均为 MV3,见 Assert MV3 artifacts 步)
- 从 GitHub Releases 下载对应平台安装包:Windows(NSIS
.exe)/ macOS(Universal.dmg)/ Linux(.deb、.AppImage)- 安装包未做代码签名/公证:Windows 首次运行触发 SmartScreen 时点「更多信息」→「仍要运行」;macOS 被 Gatekeeper 拦截时右键 App 选「打开」,或在「系统设置 → 隐私与安全性」中放行
- 源码构建与开发命令见下文桌面版(Tauri)与开发与构建
四个入口,均把内容解析后预填进录入表单(批量导入除外),确认后才落库:
- 手动录入:账户名 + base32 密钥(带校验),支持 TOTP/HOTP/Steam(SHA1/256/512,位数 5/6/7/8)
- otpauth 链接:支持
otpauth://totp|hotp|steam|yaotp(Yandex),三个入口:- 粘贴导入(全端):popup 表单区上方「粘贴 otpauth 链接导入」折叠入口,粘贴 URI 后点「导入」
- Firefox 协议注册(
ext+otpauth):安装后首次触发询问处理器,选择「TOTP 验证码工具」后,地址栏输入或点击ext+otpauth:...链接即打开 popup 并预填。平台限制:Firefox 扩展无法注册原生otpauth://scheme(manifest 协议白名单仅接受web+/ext+前缀),故网页中的真实otpauth://链接无法接管 - 右键菜单导入:网页中选中一段
otpauth://文本 → 右键「将选中的 otpauth 链接添加为条目」→ 校验通过后暂存并尝试自动打开扩展弹窗(Chrome 127+ 支持;不可用时手动点扩展图标即见预填表单);选中文本非法时发系统通知提示
- 从图片识别:录入表单选本地图片,解码其中的 otpauth 二维码后预填
- 从剪贴板导入:录入表单「从剪贴板导入」按钮——剪贴板是图片时走二维码识别预填;是文本时单条(otpauth URI / 单条目 JSON)预填、多条批量入库(批量落库在验证码页的表单对话框中,popup 表单仅支持单条预填)。读取剪贴板需扩展
clipboardRead权限(桌面端按系统授权),且需用户手势触发;无可用内容或授权失败均有明确提示 - 更多批量格式(17+)走导入页向导
在「安全」页启用 vault 加密并设置口令后,锁定页即生效;解锁方式(口令 / Passkey / 原生自动解锁)与锁定策略(重启后 / 系统锁屏 / 空闲 N 分钟)见安全。
- 列表:实时验证码 + 倒计时、关键字搜索(可选搜 secret)、按当前站点 URL 过滤(五种匹配策略,条目编辑中配置)、置顶、右键菜单(编辑 / 复制 URI / 置顶)、双击条目显示明文 8 秒后自动打回(默认打码)
- 管理:编辑/删除(二次确认)、标签管理(验证码页)、HOTP 复制后自动递增
- options 页:浏览器扩展详情 → 扩展选项,与主窗口同构五页导航;popup 右上「打开设置」深链直达
options.html#/settings - 主题:设置页「外观」区切换主题模式(自动/浅色/深色)与 10 种主题色,四入口(popup/options/桌面主窗/mini)一致生效
pnpm --filter @totp/desktop tauri dev # 开发(先起 vite,再编译 Rust)
pnpm --filter @totp/desktop tauri build # 构建,产物为 exe + NSIS 安装包- 数据位置:
%APPDATA%/com.totp.desktop/vault.json - 主窗口:完整五页管理(与插件 options 同构)。「失焦自动隐藏」在设置页开启;「隐藏到托盘」按钮在导航栏;主窗口点 X 收起到托盘;托盘中键点击直达主窗口(等价右键菜单「显示主窗口」);彻底退出请用托盘右键菜单 → 退出
- mini 弹窗:只读验证码列表,复制后自动隐藏;以下三种方式均可弹出/隐藏
- 托盘图标左键点击
- 全局快捷键
Alt+Shift+T - mini 窗失焦自动隐藏(mini 窗固定启用)
关闭窗口后不必让 WebView 常驻吃资源,三段式策略(设置页「窗口资源释放」卡配置):
- 关窗隐藏:主窗口点 X / 失焦隐藏后收到托盘
- 暂停:隐藏 N 分钟后挂起 WebView 渲染(Windows 走 WebView2
TrySuspend,要求窗口不可见;失败或非 Windows 静默降级为维持隐藏) - 销毁:再经 M 分钟销毁窗口,仅留托盘进程;点托盘或按全局快捷键自动重建窗口
- 默认 N=5 分钟、M=30 分钟;两档均为 0–1440 整数,0 = 禁用该档
- 「暂停时锁定」默认关;「销毁时锁定」默认开
- 「销毁时锁定」关闭时,销毁重建后自动恢复解锁态(密钥仅暂存本机进程内存,锁库/退出即清除,不落盘)
- 边界:暂停期间空闲锁定暂不生效;Windows 下挂起态无法暂存密钥,故开启「隐藏后暂停」时销毁后重建仍需解锁——将「隐藏后暂停」设为 0 才能保证免解锁恢复
设置页「MCP 服务器」卡开启后,本机 AI 客户端(MCP 协议,Streamable HTTP)可列出账户并读取当前验证码。设计见 docs/plans/2026-09-21-mcp-server-design.md,真机验证见 docs/review/2026-09-21-desktop-mcp-real-machine-test.md。
- 默认关闭;仅绑定
127.0.0.1(不出局域网),默认端口47215(可改 1024–65535) - Bearer 连接 Token 认证:卡片内可显示/复制/重新生成(重新生成使旧 token 立即失效);token 未启用时显示「(启用后自动生成)」占位且复制置灰
- 金库锁定时一律报错不可用;任何工具绝不返回 secret/pin 原文
- 四档客户端授权(连接即校验):仅 Token(任何持令牌客户端可用)/ 白名单通配符(推荐,如
Claude*)/ 白名单精确匹配 / 每次连接人工确认(应用内弹审批:允许 / 拒绝 / 仅本次 / 加入白名单;「仅本次」15 分钟内免重弹且重启即清) - 工具面(勾选暴露,变更即时生效,未暴露的调用被拒绝):
list_accounts、get_code:只读,默认暴露trigger_backup、trigger_sync:触发写操作,可选暴露、逐次确认(不记忆授权),仅「仅 Token」档免确认
客户端接入:设置页 MCP 卡的「客户端连接配置(JSON)」已含真实 token,复制即用;或在项目 .mcp.json(以 ZCode/Claude Code 等为例)按如下格式接入:
{
"mcpServers": {
"totp": {
"type": "http",
"url": "http://127.0.0.1:47215/mcp",
"headers": { "Authorization": "Bearer <在应用「设置 → MCP 服务器」卡复制连接 Token>" }
}
}
}- 默认关闭;在「安全」页启用并设置口令,之后 vault 以密文落盘(桌面与插件各自独立存储、分别启用)
- 算法与备份 envelope 同级(envelope v2):Argon2id 由口令派生 KEK → KEK 以 AES-256-GCM 包裹随机 32 字节 DEK → DEK 以 AES-256-GCM 加密 vault 明文(wrap/data 两段 nonce 各自独立随机)
- 加密强度三档可选(「安全」页「加密强度」):更快(低端机友好)/ 平衡(默认)/ 更慢更耐暴力破解。调整需输入当前口令确认,确认后立即以新档位重封装 DEK,数据本身无需重加密
- 更换口令会轮换 DEK 并以新口令重封装(数据无需重加密,即时完成);已绑定的 Passkey/原生自动解锁旧绑定随轮换失效,需解锁后重新绑定(界面会动态提示)。「安全」页显示「本地主口令已 N 天未更换」便于轮换
- 口令不存储、不上传;口令丢失则已加密数据无法解锁(无后门、无找回手段)
vault 加密支持多种解锁来源(KEK 来源)并存,在「安全」页「解锁方式」区管理;任意一种成功即可解锁:
- 口令(默认):启用加密时的唯一方式,也是其他方式的兜底,不可移除
- Passkey 解锁(PRF 扩展):
- 在「安全」页绑定 Passkey 后,锁定页出现「使用 Passkey 解锁」按钮:本地验证(指纹/PIN 弹窗)通过后由 passkey PRF 扩展输出直接派生密钥解出 DEK,自动完成解锁,全程无需口令
- PRF 输出仅在本机求值,不外传;绑定信息(凭据 id + 盐 + 包裹后的 DEK)存本地
- 浏览器支持面:Chrome/Edge 与桌面版(WebView2)可用;Firefox 扩展页面的 PRF 支持有限,探测不支持时「添加 Passkey 解锁」入口自动隐藏并提示
- 绑定过程会连续弹出两次认证器窗口(注册 + 求值确认),属预期行为
- 可绑定多个 Passkey(列表逐个管理)
- 扩展 popup 弹出框同样提供「使用 Passkey 解锁」:点击后打开独立解锁窗口自动发起验证(popup 窗口失焦即销毁,认证须在真窗口完成),成功后窗口自动关闭,重新打开 popup 即已解锁
- 原生自动解锁(仅桌面版):
- 统一 osAutoUnlock 通道:Windows = DPAPI(当前用户)、macOS = 钥匙串(Keychain)、Linux = 密钥环(Secret Service,GNOME Keyring/KWallet);启用后 DEK 由操作系统安全存储保护,桌面版锁定页打开时静默尝试自动解锁
- 换系统账户或换机器后解不开属预期:静默失败并回落到口令/Passkey 手动解锁,不报错不打断;Linux 无 keyring 服务 / macOS WKWebView 的 PRF 受限时,对应选项不渲染
- Windows DPAPI 路径经 roundtrip 单测验证;macOS/Linux keyring 运行时行为待真机验证
- 插件端不提供此方式
- mini 快速窗与主窗口同权:锁定态打开或聚焦 mini 时同样静默自动解锁(未启用 OS 自动解锁时仍提示需主窗口解锁)
- 应用绑定解锁(ABE 服务,仅 Windows 桌面版):
- 在「安全」页「应用绑定解锁(服务)」区块点「安装服务」,一次 UAC 完成本地系统服务安装与绑定;启用后锁定页静默解锁优先经该服务取 DEK,失败自动回退 Windows 自动解锁(DPAPI)
- 原理:DEK 密文由 LocalSystem 服务以 SYSTEM 上下文 DPAPI 包裹后存
HKLM\SOFTWARE\TotpTools\Elevation;服务对每次连接按调用者 exe 路径 + SHA256 验证后才代理包裹/解包;服务管道 DACL 收窄到安装发起用户(发起用户 SID 存 HKLM CallerSid,值缺失或非法时回退已认证用户兜底;借用他人管理员账户做 UAC 提权的场景仅该管理员可连接,发起用户自动回退 DPAPI);服务副本二进制放在 ACL 锁定的ProgramData\TotpTools\service(拒绝继承 + OWNER_RIGHTS ACE,抑制「预建目录抢占后所有者重开 DACL」) - 威胁模型(防):同用户运行的第三方进程读到 security.json 也无法自行解密(密文不在用户区,也无法冒充调用者向服务取 DEK);用户区对服务二进制的篡改不影响服务运行
- 威胁模型(不防):已验证的应用进程本身被注入/入侵(Chrome 应用绑定加密同类局限);管理员/SYSTEM 直接读 HKLM 与服务内存;目标 exe 被替换且攻击者可重走 UAC 重绑;便携版(用户可写安装目录)形态下,运行中的第三方进程可经文件替换在哈希读取窗口外绕过路径+哈希验证(服务读取已用无写共享打开+双读比较压缩窗口,安装版 ProgramData 副本不受影响)。代码签名校验为后续计划(预留接口已留、当前未启用)
- 应用更新或安装路径变化后绑定失配:区块提示「需要重新绑定」,再走一次 UAC 即可;「移除」删服务侧密文与本地标记(服务本体保留);卸载应用时 NSIS 卸载钩子尽力停删服务并清理 ProgramData 副本与 HKLM 键(currentUser 模式卸载器非提权时以提权的
--elevation-uninstall为准)
- 按端命名:「安全」页与锁定页的解锁方式名称按运行平台注入——Windows 显示「Windows Hello (Passkey)」/「Windows 自动解锁」,macOS 显示「Touch ID (Passkey)」/「钥匙串自动解锁」,Linux 与浏览器扩展显示「Passkey」(桌面另加「密钥环自动解锁」)
- 换设备:绑定数据(密文)随浏览器同步整体走;云备份/云同步(envelope)不含。Passkey 凭据在本机(平台认证器)时他机无法求值 PRF,需在新设备重新绑定;可漫游凭据(如 YubiKey)可直接解锁;原生自动解锁在他机静默失败。新设备通用路径:先用口令解锁,再重新绑定其他解锁方式
- 插件端:解锁后 DEK 写入
chrome.storage.session(浏览器会话级存储,浏览器退出即清),popup 与 options 共享解锁态,任一端解锁另一端免输口令;锁定即清除 - 锁定触发器(「安全」页「锁定策略」区,偏好持久化):
- 「重启后保持锁定」(默认开):桌面端无会话级 DEK 存储,重启后必为锁定态;插件端因 DEK 会话存储在浏览器退出时必然清空,两种取值行为一致
- 「系统锁屏时锁定」(默认开):桌面端 Windows 订阅系统锁屏事件(WTS)执行;mac/Linux 该触发器暂不可用(挂账)。插件端经
chrome.idle的 locked 态触发 - 「空闲 N 分钟后锁定」(默认 0=禁用):插件端在 options 页存活期每 30 秒轮询
chrome.idle(阈值即配置分钟数);桌面端由空闲执行器判定。Firefox 支持 idle API(含locked锁屏态),MV3 迁移(min_version 140)后可用;若运行时仍探测不可用,启动时提示降级
- 复制验证码后 30 秒自动清空剪贴板,可在「安全」页关闭;插件端在 Chrome/Edge 上经 background(alarms + offscreen)执行,popup 提前关闭也能清空;Firefox 无 offscreen API,不支持自动清空,开关保留但无效果
- 录入/编辑表单中 secret 输入框默认以密码形态遮蔽,点右侧按钮可临时明文查看;列表条目双击显示明文 8 秒后自动打回
备份相关功能集中在「同步」页(桌面主窗口 / 插件 options 页):「备份口令」「备份」「云同步」「浏览器同步」四卡;popup 不含备份功能。
- 备份文件为 JSON 文本,扩展名
.totpbackup,结构:v、kdf(alg/profile/m/t/p/salt)、wrapNonce、wrappedDek、aead、dataNonce、ciphertext - 加密流程:Argon2id(按所选档位,默认平衡档 m=65536, t=3, p=1,随机 16 字节 salt)由口令派生 KEK → KEK 以 AES-256-GCM 包裹随机 32 字节 DEK → DEK 以 AES-256-GCM 加密 vault 明文
- 备份加密档位在「备份」卡选择(与库口令加密强度相互独立);口令错误或文件损坏时解密失败并明确报错,不会输出错误数据
- 不兼容说明:2026-09-17 之前生成的 v1 备份文件与当前版本不兼容(v2 起不再读取 v1),如需找回请使用生成该文件的旧版本构建
- 工具区分两把相互独立的口令:库口令(「安全」页设置,加密本机 vault 与解锁)与备份口令(加密本地备份文件与云端同步对象,两者共用)
- 备份口令在「备份口令」卡设置:口令 + 确认输入后点「启用会话」,未记住时仅存会话内存、不落盘,锁定或关闭页面即清
- 「记住(存入保管区)」开关:开启后备份口令以密文存入独立保管区(DEK 加密的
secretBag,需已启用 vault 加密),解锁库即自动装载,备份与云同步(含自动触发)免输口令,原生自动解锁同样生效;关闭 vault 加密时清除保管区 - 口令丢失则备份无法恢复(无后门、无找回手段),请自行妥善保管
- 「备份」卡以源列表管理本地备份:默认目录(
%APPDATA%/com.totp.desktop/backups/)之外可添加任意多个自选目录源,每源独立启用、命名与保留策略 - 每源保留策略(偏好持久化):
- 保留最近 N 份:按
vault-日期-时间.totpbackup命名,每次备份后滚动删除超出 N 的最旧备份 - 覆盖:固定写入
vault-backup.totpbackup
- 保留最近 N 份:按
- 写入为临时文件 + 重命名的原子写;恢复:卡内备份列表(新在前)选择一份,输入口令解密,两步确认后整体替换当前 vault;云同步产生的冲突副本(
conflict-{源id}-{日期}-{时间}.totpbackup)也在列表中可恢复,且不参与滚动删除
- 「备份」相关自动触发持久化在桌面本地:「变更后自动备份」(数据变更提交后防抖 10 秒合并连续操作)与「定时自动备份」(15 分钟 / 1 小时 / 6 小时 / 每天)
- 执行前计算 vault 内容 SHA-256 与上次备份基线比对,无变化则跳过写入;手动备份不受变更检测限制,始终写入
- 自动执行仅在解锁会话且备份口令就绪时运行,结果不打扰,状态行显示「上次自动备份:时间 + 成功/失败」;未设置备份口令/库锁定时显示中文跳过原因
- 桌面:系统对话框选择保存/打开路径(
.totpbackup过滤器) - 插件:浏览器下载保存 / 文件选择器导入
通过自备的网盘/对象存储在多设备间同步数据。入口在「云同步」卡;云端对象为加密备份 envelope。
- 云同步以源列表管理:点「添加源」从五后端菜单添加,同类型后端可添加多份(如两个 WebDAV 账号),每源独立配置:
- 名称(同类型多份时区分)、启用开关
- 凭据与目标文件路径(各后端默认
totp-backup.totpbackup) - 保留策略:覆盖(固定单对象)或保留最近 N 份(keep 源每次同步写时间戳文件
vault-日期-时间.totpbackup,并滚动删除超出 N 的最旧云端备份) - 自动同步偏好(变更后 / 定时,逐源独立)
- 凭据加密存入保管区(DEK 加密的
secretBag,随库解锁装载),不落明文;token 过期需自行重新获取粘贴 - 每源持有独立同步基线,多源收敛由统一编排保证(见「同步语义」)
| 后端 | 凭据(均为手动粘贴,自备) |
|---|---|
| WebDAV | 服务器地址 + 用户名 + 应用密码(坚果云等) |
| S3 兼容 | Region + Bucket + AccessKeyId + SecretAccessKey;Endpoint 可选(如 MinIO http://localhost:9000),填了走 path-style;Key 前缀可选 |
| Google Drive | OAuth Access Token(文件 id 首次推送自动创建并回存凭据) |
| OneDrive | Microsoft Graph Access Token(写入云盘根目录下同名文件) |
| GitHub Gist | GitHub Token + Gist ID(建议使用 secret gist,避免备份内容暴露在公开页) |
- S3 上传为纯 fetch 实现的 AWS Signature V4 签名(无 SDK 依赖),兼容 MinIO 等自托管服务
- 自建 WebDAV/S3(MinIO)服务需允许跨域(CORS),否则插件端请求会被浏览器拦截
- 云端对象与本地备份同一加密形态(envelope v2,档位取「备份」卡的备份加密强度),服务器上永远只有密文
- 云同步口令即「备份口令」,与本地备份共用:在「备份口令」卡输入,或「记住(存入保管区)」后解锁库即免输;所有源共用同一口令;口令丢失则云端备份无法解开(无后门、无找回手段)
- 多源按顺序逐源执行(不并发),每源独立基线:先回读该源云端对象与基线比对,再按口令解密远端;单源失败不阻断其余源(上传流量/请求数随源数线性增长)
- 收敛规则:任一源采纳到较新的云端版本后,终局把该版本回推到其余基线不一致的源,防多源互相打架
- 冲突(云端与本地基线不同):以云端为准覆盖本地(LWW,远端胜),覆盖前先把本地数据保存为冲突副本——桌面写入备份目录
conflict-{源id}-{日期}-{时间}.totpbackup(不参与滚动删除,可从备份列表恢复);插件端保存为下载文件 - 远端内容用当前口令解不开(口令不一致/结构损坏)时直接报错,不做任何写入,本地数据不受影响;「失败:口令不匹配」时可用「用当前口令重置云端」救济(三步确认,以本地为准覆盖云端)
- 采用云端数据前有明确提示与两步确认;确认后云端数据整体替换当前 vault
- 每源提供两种自动触发:「变更后自动同步」(防抖 10 秒合并连续操作)与「定时自动同步」(15 分钟 / 1 小时 / 6 小时 / 每天)
- 桌面版全功能;插件端自动同步仅在 options 页打开期间运行(Service Worker 后台不持有会话口令/DEK,无法加密,故不做后台 alarm),页面关闭即停
- 自动执行仅在解锁会话且备份口令就绪时运行;自动采纳云端版本不弹确认(区别于手动同步的两步确认),结果不打扰,状态行显示「上次自动同步:时间 + 成功/失败/跳过 + 逐源明细」;未设置备份口令/库锁定/无启用源时显示中文跳过原因
- 每个云源可按需加载「云端备份」列表(手动刷新,不自动拉取):按文件名倒序展示远端加密备份
- 恢复:选中一份历史备份,解密校验通过并两步确认后整体替换当前库(恢复历史份不改动同步基线,后续同步按既有规则收敛)
- 导出:把云端密文原件保存到本地(不解密);桌面走另存对话框,插件走浏览器下载
- 删除:两步确认后删除云端份并自动刷新该源列表;GitHub Gist 后端为伪删(其 API 无真删,仅清空内容,列表与滚动清理即刻不可见)
仅插件端支持:借 Chrome 账号经 chrome.storage.sync 在登录同一账号的浏览器之间同步数据(桌面版无此功能)。开关在「浏览器同步」卡,默认关闭,需显式开启;卡内状态条显示上次同步时间/状态。
- 同步内容:vault 全量 + 应用设置。vault 落盘加密已启用时,同步的是 AES-256-GCM 密文(与「安全」页加密同一形态);未启用时同步明文 JSON(与本地一致的信任模型,建议先启用加密)
- 分片机制:vault 按 UTF-8 字节切成每片 5500 字节原始数据的分片(键名
sync:v1:序号/总数,每片独立 base64),编码后约 7.4KB,低于同步区单键 8KB 上限;设置明文整份同步(仅偏好项,不含 secret) - 冲突策略:简单 LWW——每次推送 revision 单调 +1,仅当远端 revision 更新时拉取应用;本地每次数据变更即推送,同步区有变化即拉取;开关位按设备独立,不会被其他设备改写
- 超限行为:同步区总配额约 100KB,占用超 90% 时状态条提示「同步空间已满——建议配置云备份后关闭浏览器同步」
- 加密态换设备:新设备同步到的是密文,需输入同一口令解锁后才能查看/使用;口令不存储在同步通道中,丢失则无法解锁
- 浏览器支持:以 Chrome/Edge 为主验证;Firefox 的 storage.sync 配额与行为不同,未全面验证
导入入口在「导入」页。选择文件后自动嗅探格式,按向导完成解析 → 去重预览 → 确认 → 报告;识别失败或误判时可在格式下拉中手动指定。各格式字段口径对齐 Aegis 官方导入器实现(beemdevelopment/Aegis)。
文本格式(桌面与插件均可导入)
- Aegis(JSON vault):明文与口令加密均支持;加密 vault 需输入 Aegis 导出时设置的口令(scrypt + AES-GCM,算法对齐 Aegis 官方实现)
- WinAuth(XML 配置文件):明文、口令保护(条目级/整包,PBKDF2 + Blowfish,对齐官方算法)均支持;使用 Windows DPAPI 加密(用户/机器层)的文件仅桌面版可导入(依赖系统凭据解密),插件端遇到会逐条提示「请用桌面版导入」;YubiKey 加密暂不支持。另外,WinAuth 无口令整包导出的 .txt(多行 otpauth URI 文本)可直接粘贴导入(走「URI 文本」同一入口);官方导出器只写 URI 行,# 注释行为导入侧容错,正常导出不会产生
- 2FAS(JSON 导出):明文支持(TOTP/HOTP/Steam);加密导出(servicesEncrypted)不支持,会提示改用不加密导出
- Bitwarden(JSON 导出):明文支持,
login.totp接受 otpauth URI /steam:/// 裸 base32 secret 三种形态(裸 base32 secret 也支持,与 AegisBitwardenImporter差异,本工具扩展);密码保护导出(encrypted)不支持,会提示改用明文导出 - Ente Auth:明文导出即 otpauth URI 行文本,与「URI 文本」同一入口;加密导出不支持,请在应用内改用明文导出
- Proton Authenticator(JSON 导出):明文支持(条目 uri 为 otpauth:// 或 steam://);加密导出不支持,会提示改用明文导出
- Stratum / Authenticator Pro(JSON 导出):明文支持(大写键 schema,HOTP/TOTP/Steam);二进制加密导出不支持
- FreeOTP+(JSON 导出)与旧版 FreeOTP(shared_prefs tokens.xml):均支持;secret 按字节数组还原,HOTP counter 沿用存储值(对齐 Aegis 口径)
- TOTP Authenticator:明文 JSON 数组与外部分享文件(Base64 密文)均支持;分享文件默认口令
TotpAuthenticator,改过口令的在口令页输入 - andOTP(JSON 导出):明文支持;加密备份(二进制)暂不支持,请用明文导出
- FoxAuth(JSON 备份):明文与口令加密均支持;加密备份需输入导出口令(口令以 Base64 存于
passwordInfo.encryptPassword,HKDF-SHA-256 + AES-GCM,算法对齐 FoxAuth 官方实现) - Authy(shared_prefs XML):明文与口令加密条目均支持;含加密条目时需输入 Authy 备份口令(PBKDF2 + AES-CBC)
- Battle.net(shared_prefs XML):XOR 掩码还原,单文件单条目(8 位 TOTP)
- Duo(files/duokit/accounts.json):JSON 数组,含 counter 的条目按 HOTP 导入
- Microsoft Authenticator(SQLite db):
accounts表,普通条目 6 位、Microsoft 型 8 位 TOTP - Google Authenticator / otpauth URI 文本:每行一条
otpauth://totp/...|hotp/...|steam/...|yaotp/...URI(多数应用的 URI/迁移文本导出均走此入口,含 Ente Auth 明文导出) - Authenticator Plus:口令加密 ZIP 备份支持导入,需输入备份口令(AES 加密 ZIP,对齐官方加密布局)
- 通用 JSON / JSON array / JSONL:逐字段配置点路径映射(如
otp.params.secret)将行对象映射为条目,secret 字段必填;单个 JSON 对象会自动探测其嵌套的行数组。secret 自动去空白并大写,非法 algorithm/digits/period 回落默认值(SHA1/6/30),Steam 条目固定 5 位。映射方案可命名保存、复用与删除:再次导入同结构文件时按列名匹配自动推荐,也可手动套用
SQLite 数据库(桌面与插件均可导入)
- 来源设备数据库文件(应用私有目录的 db,经系统备份/adb 等方式取出):先按 SQLite 文件头校验,再在 sqlite_master 中按已知表名探测并解析(当前识别 Microsoft Authenticator 的
accounts表) - 解析经 sql.js 完成,其 wasm 二进制随应用打包为本地资产(离线可用);实例化前按固定 SHA-256 校验完整性,不一致即拒绝加载
暂不支持
- Google Authenticator 旧版 SQLite 数据库(≤5000100 版本):需 root 提取应用私有目录数据库,暂不支持
- Steam Android 客户端:官方客户端导出暂不支持;SteamGuard/SDA 明文 JSON(Steamguard-*.json / maFile,shared_secret Base64)已支持——粘贴或导入页直接解析(加密 maFile 需先在原工具解除口令导出明文)
解析后预览阶段逐条标注(互斥完备,secret 维度优先于 issuer+label 维度):
- 完全相同(全部关键字段一致):自动跳过,不重复落库
- 疑似同账户(secret + 算法相同,其余字段不同):逐条三选——跳过(默认)/ 新增 / 覆盖现有条目
- 冲突(issuer + label 相同但 secret 不同,忽略大小写与首尾空白):按冲突策略处理——跳过冲突条目(默认)/ 覆盖现有条目(保留其标签与排序位置)/ 保留两者(并存)
- 新增:其余条目直接落库
文件内完全重复的行自动合并(保留首条),避免预览计数虚高。确认导入后报告逐类计数与逐条失败原因(含行号/条目号);单条解析失败不阻断整体导入,整体解密类失败(如 Aegis 口令错误)明确报错且不写入部分数据。
条目可配置品牌图标,列表头像与录入表单展示;四种来源,均在录入/编辑表单的「图标」区设置。
- Simple Icons 精选 218 项(CC0、单色 SVG),覆盖开发/云/运维/身份认证/社交/媒体/购物/金融/加密/游戏/自托管/密码工具等常见 2FA 发行方;由 scripts/gen-builtin-icons.mjs 从 simple-icons 包提取生成(packages/core/src/icons/builtin.json,另含 58 条 issuer 别名)
- 商标下架说明:上游 Simple Icons 会应商标方要求不定期移除部分品牌图标,内置集只能收录当期包内仍在收录的图标;已下架或未收录的品牌,请用图标包导入/上传/URL 引用补足
- issuer 关键词推荐:录入时输入发行方名称自动匹配(归一化忽略大小写与空白、
.、-、_,含中文别名如「微信」「B站」「战网」);命中且未手动选图标时显示推荐气泡,点击即用
- 支持 aegis-icons 社区包及同构 zip 包:任意目录层级下的
.png均会导入,文件名(去扩展名)即服务名,与条目 issuer 归一化后匹配 - 限制:单个文件超过 200KB 跳过;aegis-icons 全量包(2000+ 图标)可整体导入(仅防病态 zip 的 65536 条目上限);同名(归一化后)图标后者覆盖前者
- 入口:录入/编辑表单的「图标」区点「导入图标包(zip)」选择 zip 文件;导入后需在条目编辑中手动选择对应图标(包导入只入库,不自动绑定到条目)
- 任意图片自动等比缩放至长边 ≤128px(不放大),转 PNG 存储
- 填入图片 URL 后立即拉取并缓存为本地副本(缓存键
urlcache:<id>) - URL 拉取上限 200KB,超限视为失败;拉取失败(网络不通、非 2xx、站点不允许跨域 CORS)会明确报错,修正 URL 后可重试;缓存丢失(如清空存储、换设备)时列表回退首字母占位,可在编辑表单重新拉取
- 图标统一以 dataUrl 存于
'icons'键(id→dataUrl 映射),与 vault 各自独立 - 浏览器插件:
chrome.storage.local,已声明unlimitedStorage权限,不受 10MB 默认配额限制;popup 与 options 共享同一份数据 - 桌面:与 vault 同目录
%APPDATA%/com.totp.desktop/
pnpm install
pnpm test # 全部前端单测(core / ui / extension / desktop 四包 vitest,各包实跑数见输出)
cargo test # Rust 侧单测(apps/desktop/src-tauri,CI 门禁运行;cargo clippy -- -D warnings 同)
# 注意:extension 的类型检查依赖 WXT 生成的 .wxt/ 目录(已被 git ignore),
# 需先 pnpm --filter @totp/extension exec wxt prepare 生成类型,再执行 pnpm typecheck。
pnpm --filter @totp/extension build # Chrome 目标产物 .output/chrome-mv3
pnpm --filter @totp/extension exec wxt build -b firefox # Firefox 目标产物 .output/firefox-mv3
pnpm typecheck # 类型检查:core 为纯 tsc;ui/extension/desktop 为 vue-tsc(含 .vue 单文件组件)- 扩展双目标均为 Manifest V3:Firefox 端
strict_min_version: 140.0、gecko idtotp@bhxch.github.io(AMO 一经发布不可改),manifest 配置见 apps/extension/wxt.config.ts;CI 在「Assert MV3 artifacts」步断言双目标产物(.github/workflows/build.yml) - 端到端验证基建(测试专用,不影响运行时):scripts/inject-test-shim.mjs 向构建产物注入 chrome shim 与 fetch/clipboard mock,配合
?seedVault=/?seedKeys=/?tabUrl=驱动测试态;历轮验证报告见 docs/review/
- packages/core — 纯 TS 核心:OTP 引擎(HOTP/TOTP/Steam/URI)、匹配策略、加密与 envelope、备份源模型、云同步编排(多目标/调度器/滚动删除)、导入器(17+ 格式与去重判定树)、存储抽象
- packages/ui — Vue 3 共享层:MD3 组件库(packages/ui/src/components/md/)、主题系统(packages/ui/src/theme/:10 种子色 × 明暗 35 角色 tokens + AMOLED 对比度覆盖层,按需懒加载)、五页导航与页面、store(DEK 保管区/口令轮换/解锁状态机)、双端共享 runner(云同步/备份)
- apps/extension — WXT 浏览器插件(Chrome/Edge/Firefox,均 MV3):popup、options(同构五页)、background、DEK 会话持久化、云凭据迁移
- apps/desktop — Tauri 2 桌面程序(复用 packages/ui):主窗口五页、mini 弹窗、自动备份/自动云同步 runner、窗口资源释放、内置 MCP 服务器、Rust 端(备份目录命令 / DPAPI / osAutoUnlock / Windows 锁屏事件 / 释放策略 / MCP server)
文档索引:
- 总体设计与前端重设计:docs/plans/2026-09-13-totp-tool-design.md、docs/plans/2026-09-15-frontend-redesign-design.md
- 备份源与加密设计:docs/plans/2026-09-17-backup-sources-and-crypto-design.md
- MCP 服务器设计:docs/plans/2026-09-21-mcp-server-design.md
- 实施计划(按里程碑):docs/plans/
- 审查与端到端验证报告(含 MCP 真机测试):docs/review/
- 更新日志(版本变更记录,release 说明自动取自此处):CHANGELOG.md
- 隐私政策(中英双语):docs/PRIVACY.md
扩展申请全部 http/https 站点权限(host_permissions)的原因:云备份目标由您自行配置(WebDAV / S3 / GitHub Gist / Google Drive / OneDrive,地址可以是任意服务),而浏览器会对页面直连的跨源请求施加 CORS 限制;因此云备份请求统一由扩展 background 发出,host_permissions 是其出网前提。这些权限仅用于与您配置的备份目标及 OAuth 令牌端点之间的备份数据往返,不用于读取任何网页内容。
详见 plan13-16 全量代码审查与批⑧六规格审查与验证。
- 手动云同步不设内容门:manual 轮始终完整推拉(内容无变化也会重写云端);自动轮经持久内容门降级 pull-only,内容无变化时零上传,两端均闲置不再互踢
- Google Drive 源仅保留 1 份远端对象(时间戳文件名对 gdrive 不生效,后端按名覆盖;keep 配置对该后端已隐藏)
- 「重启后保持锁定」开关当前在桌面/扩展两端均置灰禁用(两端均无会话级凭据存储:桌面重启后必为锁定态;扩展端凭据仅存会话,浏览器退出即清);mac/Linux 的「系统锁屏时锁定」触发器不可用(挂账)
- Firefox(MV3):剪贴板自动清空不可用(无 offscreen API,清空降级为仅前台不调度);空闲/锁屏自动锁定按 MDN 兼容性 idle API 已支持(含
locked态,min_version 140),真机如有异常以运行时降级提示为准;Passkey(PRF)解锁支持有限,探测不支持时入口自动隐藏 - 云端列表无分页:单目录/前缀下对象数超过云接口单页上限时(如 S3 1000 条),滚动删除可能漏删最旧份;达到分页上限时状态行会提示手动清理(gdrive/onedrive/s3 已实现截断感知)
- 替换/重装 exe 后 ABE 自动解锁失效属预期,重新绑定一次即恢复:提权服务按客户端可执行文件的 SHA256 校验调用方(绑定存 HKLM);文件被替换(手动覆盖、覆盖安装、nightly 升级)后哈希失配、调用被拒——「安全」页 ABE 状态会转异常提示,重新执行一次绑定(走一次 UAC)即恢复;不使用 ABE 自动解锁则完全无影响
- mini 窗与主窗的设置写入为整文件级联(多窗口):极端并发下后写覆盖先写(last-writer-wins,单用户低频场景取舍)