fischer-agentkit/docs/solutions/integration-issues/jwt-secret-dev-mode-user-id...

7.5 KiB
Raw Blame History

title date category module problem_type component symptoms root_cause resolution_type severity tags
JWT secret 未设置导致 dev mode user_id 丢失 + reload 登录失效 2026-06-28 docs/solutions/integration-issues server/auth, server/app, tools/calendar_tool, frontend/stores/auth integration_issue authentication
agent 创建日历事件后回复'已完成',但日历 UI 中看不到事件
浏览器 reload 后跳回登录页refresh token 未过期却验证失败
calendar.db 中事件 user_id 为 default/zhangsanLLM hallucinateUI 查询用 user_id=None
AuthMiddleware 处于 dev mode所有请求 current_user.user_id=None
config_error config_change critical
jwt
auth
dev-mode
calendar
user-id
ephemeral-secret
login-state

JWT secret 未设置导致 dev mode user_id 丢失 + reload 登录失效

Problem

用户报告4个问题(1) 启动后出现一堆测试对话且无法删除;(2) 要求创建下周一日历事件agent 回复已完成但日历里看不到;(3) 日历默认周日开始;(4) reload 后跳回登录页。深入调查发现问题2和4同根同源AGENTKIT_JWT_SECRET 从未设置,触发 dev mode 一系列连锁反应。

Symptoms

  • 日历事件数据不一致calendar.db 中事件 user_id="default"/"zhangsan"LLM hallucinate/calendar/events 路由用 user_id=None 查询 → 返回空列表
  • reload 登录失效:服务器重启后,之前签发的 refresh token 全部失效,前端 startupCheckwhoami(refresh) → 401 → startupState='invalid' → 跳 /login
  • AuthMiddleware dev mode:所有请求 current_user={"user_id": None, "username": "dev", "role": "admin"}
  • CalendarTool schema 要求 LLM 提供 user_idLLM 不知道真实值hallucinate 假值写入 DB

What Didn't Work

  • 前次"修复"日历 401:在 AuthMiddleware dev mode 分支添加 synthetic dev useruser_id=None),解决了 401 但引入了 user_id=None 数据不一致问题——治标不治本
  • 前次"修复"登录状态:在 auth.ts 注册4个 token provider但未解决 refresh token 跨重启失效的根本问题
  • 假设 API 删除失败curl 直接测试 DELETE 端点返回 200DB 行数减少——API 正常,问题是预存测试数据 + 前端视觉复活

Solution

核心修复:设置持久化 JWT secret

.env 中添加(.env 已被 gitignore不会泄露

# 生成持久化 secret
python3 -c "import secrets; print(secrets.token_urlsafe(48))"

# 写入 .env
AGENTKIT_JWT_SECRET=<generated_secret>

这一步同时解决问题2AuthMiddleware 退出 dev modecurrent_user.user_id 从 JWT payload 获取真实值和问题4refresh token 跨重启持久化)。

CalendarTool 注入真实 user_id

calendar_tool.py 修改:

# 之前schema 要求 LLM 提供 user_idLLM 会 hallucinate
"required": ["action", "user_id"],

# 之后:移除 user_id from required改用注入的 default_user_id
def __init__(self, calendar_service, default_user_id: str | None = None):
    ...
    self._default_user_id = default_user_id

def _resolve_user_id(self, kwargs) -> str | None:
    provided = kwargs.get("user_id")
    if provided and isinstance(provided, str) and provided.strip():
        return provided
    return self._default_user_id

app.py 在 lifespan 中查询 admin 用户并注入:

# 查询第一个 active admin 用户作为 default_user_id
async with aiosqlite.connect(str(DEFAULT_AUTH_DB_PATH)) as db:
    db.row_factory = aiosqlite.Row
    cur = await db.execute(
        "SELECT id FROM users WHERE is_active = 1 "
        "ORDER BY CASE role WHEN 'admin' THEN 0 ELSE 1 END, created_at LIMIT 1"
    )
    row = await cur.fetchone()
    if row is not None:
        default_cal_user_id = str(row["id"])

calendar_tool = CalendarTool(
    calendar_service=cal_service,
    default_user_id=default_cal_user_id,
)

CalendarGrid firstDay 配置

CalendarGrid.vue 添加 firstDay: 1

数据清理

# 清理测试对话
sqlite3 ~/.agentkit/conversations.db "DELETE FROM messages; DELETE FROM conversations;"

# 修复 calendar.db 中已存在的 hallucinate user_id
sqlite3 data/calendar.db "UPDATE calendar_events SET user_id = '<admin_user_id>' WHERE user_id IN ('default', 'zhangsan')"

Why This Works

根因因果链

  1. AGENTKIT_JWT_SECRET 未设置 → get_jwt_secret() 返回 None
  2. app.py:812-837explicit_jwt_secret or "" 传给 AuthMiddleware → jwt_secret=""_is_dev_mode()=True
  3. dev mode 下 get_or_create_jwt_secret() 每次进程启动生成新的 ephemeral secret(不持久化)
  4. 问题4:服务器重启 → 新 ephemeral secret → 旧 refresh token 签名不匹配 → whoami 401 → 跳登录页
  5. 问题2dev mode 下 current_user.user_id=NoneCalendarTool 由 LLM hallucinate user_id="default"UI 路径用 None 查询不匹配 → 日历看不到事件

设置 AGENTKIT_JWT_SECRET 后:

  • AuthMiddleware 收到真实 secret → _is_dev_mode()=False → JWT 验证启用 → current_user.user_id 从 payload 获取
  • get_jwt_secret() 返回持久化 secret → 跨重启一致 → refresh token 持久有效

Prevention

  • 环境变量检查:在 app.py 启动时检查 AGENTKIT_JWT_SECRET 是否设置,未设置时打印明显警告(当前 dev mode 日志不够醒目)
  • e2e 测试覆盖:添加 e2e 测试覆盖以下场景:
    1. 服务器重启后 reload 页面,验证登录状态保持
    2. agent 创建日历事件后UI 能看到该事件(数据一致性 + calendar_event_created WS 消息到达前端,详见 calendar-agent-create-no-refresh.md
    3. 无效 token 返回 401非 dev mode 验证)
  • CalendarTool user_id 来源:多用户场景需通过 agent 框架 contextvar 传递 per-request user_id当前 default_user_id 是 dev 模式单用户简化(代码中已标注 ponytail: 注释)
  • 配置审计.env.example 应列出 AGENTKIT_JWT_SECRET 并说明必须设置

同一症状("agent 创建日历事件后 UI 看不到")现已记录三个不同的根因,调查时需同时排查: