本地 TOTP 查看器

使用小巧且无依赖的 Python 脚本,在 Linux PC 上查看和管理基于时间的一次性双重验证代码。

maebahesioru
2026年10月16日
0 收藏
51 下载

安装与下载

1 ClawHub CLI

从源直接安装技能的最快方式。

npx clawhub@latest install local-totp-viewer

2 手动安装

将技能文件夹复制到以下位置之一

全局模式 ~/.openclaw/skills/
工作区 {'/skills/'}

优先级:工作区 › 全局 › 内置

3 提示词安装

将此提示词复制到 Openclaw 即可自动安装。

请帮我使用 Clawhub 安装 skill-local-totp-viewer。如果尚未安装 Clawhub,请先安装(npm i -g clawhub)。

更倾向于下载?

获取包含原生技能文件的ZIP压缩包。

下载技能 (.zip)

基本信息

名称 本地 TOTP 查看器
Slug local-totp-viewer
创建者 maebahesioru
创建时间 2026-10-16 05:30:30
更新时间 2026-10-16 05:30:30
收藏数 0
使用量 51
浏览量 3

安装指南

  1. ClawHub CLI 从源直接安装技能的最快方式。
    npx clawhub@latest install local-totp-viewer
  2. 手动安装 将技能文件夹复制到以下位置之一
    全局模式
    ~/.openclaw/skills/
    工作区
    /skills/
    优先级:工作区 > 本地 > 内置
  3. 提示词安装 将此提示词复制到 OpenClaw 即可自动安装。
    请帮我使用 Clawhub 安装 local-totp-viewer。如果尚未安装 Clawhub,请先安装(npm i -g clawhub)。
    更倾向于下载?
    获取包含原生技能文件的ZIP压缩包。
    下载技能 (.zip)

什么是

本地 TOTP 查看器可生成符合 RFC 6238 标准的基于时间的一次性密码,并在实时终端视图中显示,因此无需手机或外部身份验证器应用,也能在 PC 上访问双重验证代码。此 Openclaw Skills 工作流还介绍了如何通过 Base32 值或 otpauth:// URI 注册密钥,以及移除已存储的账户。
该脚本使用 Python 标准库,并将账户名称和密钥存储在 ~/.2fa_secrets.json 中,保存时会将文件权限设为 600。它是简单的本地工具,并非完整的身份验证器应用:请保护密钥文件,避免在聊天或日志中分享密钥,并在每个代码的 30 秒有效期结束前输入代码。

应用场景

手机无法使用或使用不便时,在 Linux 桌面上显示双重验证代码。
将账户从 Google Authenticator 迁移到本地桌面工作流。
在无头或 CLI 流程中获取一次性代码,由人员确认并输入每个代码。
使用 Base32 密钥或
otpauth://
URI 添加 TOTP 账户。
列出本地注册的账户,或移除不再需要的账户。

工作原理

不带参数运行单文件 Python 脚本,以加载已保存的密钥并持续刷新显示的代码。
添加新账户时,提供 Base32 密钥和账户名称,或传入 otpauth:// URI;脚本会将密钥添加到本地 JSON 存储中。
脚本根据密钥和当前时间计数器计算代码,然后显示代码以及 30 秒有效期内剩余的秒数。
使用列表选项查看已注册的账户名称,或使用移除选项从存储中删除账户。
迁移 Google Authenticator 时,导出账户、解码 QR 载荷、提取并对每个密钥进行 Base32 编码,然后使用查看器逐个注册账户。

配置指南

确认 Linux PC 上已安装 Python 3。
将技能中包含的 Python 脚本保存为 ~/2fa_view.py。查看器本身仅使用 Python 标准库;QR 解码是迁移时可选的独立步骤。
使用如下命令注册账户并启动实时查看器:
python3 ~/2fa_view.py BASE32_SECRET account-name
python3 ~/2fa_view.py
你也可以注册受支持的 otpauth:// URI、列出账户或移除账户:
python3 ~/2fa_view.py 'otpauth://totp/Example?secret=BASE32_SECRET&issuer=Example'
python3 ~/2fa_view.py -l
python3 ~/2fa_view.py -r account-name
迁移 Google Authenticator 时,从应用中导出账户并解码生成的迁移 QR 码。该技能介绍了使用 pyzbar 等 QR 库的方法;它不是查看器的运行时依赖。解析迁移 protobuf,对每个密钥进行 Base32 编码,然后逐个注册账户。
使用脚本前,请通过针对该测试向量的时间戳和八位数输出配置的测试框架,将 TOTP 计算结果与 RFC 6238 测试向量进行核对。查看器常规显示使用六位数和 30 秒周期。

数据架构

查看器将数据存储在 ~/.2fa_secrets.json 中,格式为 JSON 对象,将账户标签映射到 Base32 密钥字符串。例如:
{
"account-name": "BASE32_SECRET"
}
文件位置: 当前用户主目录中的 ~/.2fa_secrets.json。
文件权限: 脚本保存文件时设为 600;请对密钥保密,并在文件由脚本外部创建或更改时检查权限。
账户标签: 直接注册时使用提供的账户名称。URI 注册会根据发行者和密钥的前六个字符创建标签;脚本不会保留完整的 URI 元数据。
生成的数据: TOTP 代码在运行时计算并显示在终端中;它们不会作为账户记录存储。
迁移字段: 所述 Google Authenticator 导出工作流会从迁移 protobuf 中提取每个密钥、账户名称、发行者、算法、位数和 OTP 类型。查看器的 URI 注册路径只存储密钥,并使用内置的六位数、30 秒计算设置,因此请确认非默认参数账户的兼容性。
列表注意事项: -l 命令会显示每个密钥的前八个字符以及账户名称。请避免分享其输出。

高级特性

在终端中持续刷新所有已注册的代码,并显示当前 30 秒有效期内剩余的秒数。
支持直接通过 Base32 值或
otpauth://
URI 添加密钥。
提供简单的 CLI 操作,用于列出账户和移除指定账户。
说明从 Google Authenticator 的
otpauth-migration://
导出数据进行迁移的方法,包括 QR 解码和 protobuf 字段提取;查看器不内置 QR 解码功能。
使用本地 JSON 存储以及标准库中的 HMAC、散列、Base32 和时间函数,无需在运行时依赖第三方软件包。
提供安全建议:保护密钥文件,避免在对话记录中暴露密钥,并等待新的时间窗口,而不要重试已过期的代码。


name: local-totp-viewer description: Set up and use a zero-dependency local TOTP code viewer on a Linux PC. Use when a user wants 2FA/TOTP codes shown on the desktop, wants to migrate secrets out of Google Authenticator, or needs one-time codes without a phone. license: MIT

Local TOTP Viewer

Show and manage TOTP (RFC 6238) codes directly on a PC with a single-file Python script — no phone, no external app, no dependencies.

When to use

The user wants 2FA codes displayed on their desktop instead of a phone.
Migrating secrets from Google Authenticator to a desktop workflow.
A headless/CLI workflow needs one-time codes (e.g. automated login steps where a human confirms each code).

The script

Save this as ~/2fa_view.py (pure stdlib + optional system qrcode not required):

#!/usr/bin/env python3 """Zero-dependency TOTP viewer. Usage:

python3 2fa_view.py # live view of all codes
python3 2fa_view.py # register a secret
python3 2fa_view.py "otpauth://..." # register from URI
python3 2fa_view.py -l # list
python3 2fa_view.py -r # remove

"""
import base64, hashlib, hmac, json, os, struct, sys, time, urllib.parse

STORE = os.path.expanduser("~/.2fa_secrets.json")

def load():

if os.path.exists(STORE):

with open(STORE) as f:
return json.load(f)
return {}

def save(d):
with open(STORE, "w") as f:
json.dump(d, f)
os.chmod(STORE, 0o600)

def totp(secret_b32, digits=6, period=30, algo="sha1"):
key = base64.b32decode(secret_b32.upper() + "=" * (-len(secret_b32) % 8))
counter = int(time.time()) // period
msg = struct.pack(">Q", counter)
h = hmac.new(key, msg, hashlib.sha1 if algo == "sha1" else hashlib.sha256).digest()
o = h[-1] & 0x0F
code = (struct.unpack(">I", h[o:o+4])[0] & 0x7FFFFFFF) % (10 ** digits)
return str(code).zfill(digits)

def main():
d = load()
args = sys.argv[1:]

if not args:

try:
while True:
now = int(time.time())
left = 30 - (now % 30)
line = []
for name, s in sorted(d.items()):
line.append(f"{name}: {totp(s)} ({left:2d}s)")
print(" | ".join(line) + " ", end="\r")
time.sleep(0.25)
except KeyboardInterrupt:
print()
return

if args[0] == "-l":

for name, s in sorted(d.items()):
print(f"{name} {s[:8]}...")
return

if args[0] == "-r":

d.pop(args[1], None); save(d); print("removed"); return

if args[0].startswith("otpauth://"):

q = urllib.parse.parse_qs(urllib.parse.urlparse(args[0]).query)
d[q.get("issuer", ["?"])[0] + ":" + q["secret"][0][:6]] = q["secret"][0]
save(d); print("registered from URI"); return
d[args[1]] = args[0]
save(d)
print(f"registered {args[1]}")

if __name__ == "__main__":

main()

Verify against the RFC 6238 test vector (secret GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ, T=59s, SHA1 → 94287082) before trusting it.

Migrating secrets from Google Authenticator

Google Authenticator can export all accounts as a single QR code:

In the app: menu → Transfer accounts → Export accounts → select accounts.
Screenshot the QR code and pass the image to the agent.
Decode it with a QR library (e.g. pyzbar); OpenCV's QR detector often fails on app screenshots.
The payload is an otpauth-migration://offline?data= URI. URL-decode, then parse the protobuf:
field 1 (otp_parameters): secret = field 1 (bytes), name = field 2, issuer = field 3, algo = field 4 (1=SHA1), digits = field 5 (1=6 digits), type = field 6 (2=TOTP).
Encode each secret as base32: base64.b32encode(secret).rstrip(b"=").
Register each secret with the viewer script.

Pitfalls

Generate a code immediately before entering it. TOTP windows are 30 seconds; a pre-generated code can expire mid-entry.
Keep the secrets file at mode 600 and never print full secrets into chat logs or transcripts.
When automating a login form, type the code within the window and handle rejection by waiting for the next window rather than retrying the same code.
定时发布

SeaGames CLI