--- title: "JWT secret 未设置导致 dev mode user_id 丢失 + reload 登录失效" date: 2026-06-28 category: docs/solutions/integration-issues module: server/auth, server/app, tools/calendar_tool, frontend/stores/auth problem_type: integration_issue component: authentication symptoms: - "agent 创建日历事件后回复'已完成',但日历 UI 中看不到事件" - "浏览器 reload 后跳回登录页,refresh token 未过期却验证失败" - "calendar.db 中事件 user_id 为 default/zhangsan(LLM hallucinate),UI 查询用 user_id=None" - "AuthMiddleware 处于 dev mode,所有请求 current_user.user_id=None" root_cause: config_error resolution_type: config_change severity: critical tags: [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 全部失效,前端 `startupCheck` → `whoami(refresh)` → 401 → `startupState='invalid'` → 跳 `/login` - **AuthMiddleware dev mode**:所有请求 `current_user={"user_id": None, "username": "dev", "role": "admin"}` - **CalendarTool schema 要求 LLM 提供 user_id**:LLM 不知道真实值,hallucinate 假值写入 DB ## What Didn't Work - **前次"修复"日历 401**:在 AuthMiddleware dev mode 分支添加 synthetic dev user(`user_id=None`),解决了 401 但引入了 `user_id=None` 数据不一致问题——治标不治本 - **前次"修复"登录状态**:在 auth.ts 注册4个 token provider,但未解决 refresh token 跨重启失效的根本问题 - **假设 API 删除失败**:curl 直接测试 DELETE 端点返回 200,DB 行数减少——API 正常,问题是预存测试数据 + 前端视觉复活 ## Solution ### 核心修复:设置持久化 JWT secret 在 `.env` 中添加(`.env` 已被 gitignore,不会泄露): ```bash # 生成持久化 secret python3 -c "import secrets; print(secrets.token_urlsafe(48))" # 写入 .env AGENTKIT_JWT_SECRET= ``` 这一步同时解决问题2(AuthMiddleware 退出 dev mode,`current_user.user_id` 从 JWT payload 获取真实值)和问题4(refresh token 跨重启持久化)。 ### CalendarTool 注入真实 user_id [calendar_tool.py](file:///Users/Chiguyong/Code/Fischer/fischer-agentkit/src/agentkit/tools/calendar_tool.py) 修改: ```python # 之前:schema 要求 LLM 提供 user_id,LLM 会 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](file:///Users/Chiguyong/Code/Fischer/fischer-agentkit/src/agentkit/server/app.py) 在 lifespan 中查询 admin 用户并注入: ```python # 查询第一个 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](file:///Users/Chiguyong/Code/Fischer/fischer-agentkit/src/agentkit/server/frontend/src/components/calendar/CalendarGrid.vue) 添加 `firstDay: 1`。 ### 数据清理 ```bash # 清理测试对话 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 = '' WHERE user_id IN ('default', 'zhangsan')" ``` ## Why This Works **根因因果链**: 1. `AGENTKIT_JWT_SECRET` 未设置 → `get_jwt_secret()` 返回 None 2. [app.py:812-837](file:///Users/Chiguyong/Code/Fischer/fischer-agentkit/src/agentkit/server/app.py#L812-L837) 中 `explicit_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. **问题2**:dev mode 下 `current_user.user_id=None`,CalendarTool 由 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](file:///Users/Chiguyong/Code/Fischer/fischer-agentkit/docs/solutions/ui-bugs/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` 并说明必须设置 ## Related Issues 同一症状("agent 创建日历事件后 UI 看不到")现已记录**三个不同的根因**,调查时需同时排查: - [日历能力缺失修复 + UI 布局优化](file:///Users/Chiguyong/Code/Fischer/fischer-agentkit/docs/solutions/logic-errors/calendar-capability-and-ui-fixes.md) — 前次日历问题(CalendarTool 接入 ReAct),本次是认证配置导致的 user_id 不匹配,同一领域不同根因 - [Calendar events created via agent chat do not refresh the calendar UI](file:///Users/Chiguyong/Code/Fischer/fischer-agentkit/docs/solutions/ui-bugs/calendar-agent-create-no-refresh.md) — `CalendarService.create_event` 创建成功后未广播 `calendar_event_created` WS 消息(第三个根因,2026-06-29 记录) - [Portal 平台安全可靠性修复](file:///Users/Chiguyong/Code/Fischer/fischer-agentkit/docs/solutions/security-issues/portal-platform-security-reliability-fixes.md) — 认证相关修复