70 lines
3.0 KiB
Markdown
70 lines
3.0 KiB
Markdown
|
|
# Auth Token Compatibility + Refresh Singleflight Implementation Plan
|
||
|
|
|
||
|
|
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
|
||
|
|
|
||
|
|
**Goal:** 兼容云 Supabase 实际 access token claims(缺失 `iss` 仍可通过),并修复前端 401 导致 refresh 风暴问题,消除日志中的批量 401/429 警告。
|
||
|
|
|
||
|
|
**Architecture:** 后端保持 HS256 签名校验、`exp/sub` 必检,将 `iss` 从“强制存在”改为“存在时校验”;前端在拦截器中加入 refresh 单飞与防重入,避免并发 401 触发多次 refresh 或 refresh 自递归。同步清理无效分支与冗余状态。
|
||
|
|
|
||
|
|
**Tech Stack:** FastAPI, PyJWT, Flutter, Dio, flutter_test
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### Task 1: 后端 JWT claim 兼容化(无 `iss` 可通过)
|
||
|
|
|
||
|
|
**Files:**
|
||
|
|
- Modify: `backend/src/core/auth/jwt_verifier.py`
|
||
|
|
- Test: `backend/tests/unit/core/auth/test_jwt_verifier.py`
|
||
|
|
|
||
|
|
**Step 1: Write failing test**
|
||
|
|
- 新增用例:token 不含 `iss`、但 `sub/exp` 与 HS256 签名合法时应验证成功。
|
||
|
|
|
||
|
|
**Step 2: Run test to verify it fails**
|
||
|
|
- Run: `cd backend && uv run pytest tests/unit/core/auth/test_jwt_verifier.py -q`
|
||
|
|
|
||
|
|
**Step 3: Write minimal implementation**
|
||
|
|
- `jwt.decode` 的 `require` 去掉 `iss`,仅保留 `sub/exp`。
|
||
|
|
- 若 payload 中存在 `iss` 且配置了 issuer,则手动比对 issuer;不一致时报错。
|
||
|
|
|
||
|
|
**Step 4: Run test to verify it passes**
|
||
|
|
- Run: `cd backend && uv run pytest tests/unit/core/auth/test_jwt_verifier.py -q`
|
||
|
|
|
||
|
|
### Task 2: 前端 refresh 单飞 + 防递归
|
||
|
|
|
||
|
|
**Files:**
|
||
|
|
- Modify: `apps/lib/core/api/api_interceptor.dart`
|
||
|
|
- Test: `apps/test/core/api/api_interceptor_test.dart`
|
||
|
|
|
||
|
|
**Step 1: Write failing tests**
|
||
|
|
- 并发 401 时只调用一次 `onTokenRefresh`。
|
||
|
|
- `/api/v1/auth/sessions/refresh` 自身 401 不触发 refresh 重试。
|
||
|
|
|
||
|
|
**Step 2: Run tests to verify failures**
|
||
|
|
- Run: `cd apps && flutter test test/core/api/api_interceptor_test.dart`
|
||
|
|
|
||
|
|
**Step 3: Write minimal implementation**
|
||
|
|
- 增加 `_refreshFuture` 单飞字段。
|
||
|
|
- 非 refresh 请求命中 401 时 await 同一个 refresh future。
|
||
|
|
- 对 refresh/logout 认证端点和已重试请求加短路,避免无限重入。
|
||
|
|
|
||
|
|
**Step 4: Run tests to verify pass**
|
||
|
|
- Run: `cd apps && flutter test test/core/api/api_interceptor_test.dart`
|
||
|
|
|
||
|
|
### Task 3: 清理无效/旧分支并做回归验证
|
||
|
|
|
||
|
|
**Files:**
|
||
|
|
- Modify: `apps/lib/core/api/api_interceptor.dart`(移除无效重试分支)
|
||
|
|
- Modify: `backend/src/core/auth/jwt_verifier.py`(删除不再使用的路径)
|
||
|
|
|
||
|
|
**Step 1: Refactor cleanup**
|
||
|
|
- 删除不再可达的分支与重复逻辑,保持行为不变。
|
||
|
|
|
||
|
|
**Step 2: Full targeted verification**
|
||
|
|
- Run: `cd backend && uv run ruff check src tests`
|
||
|
|
- Run: `cd backend && uv run basedpyright`
|
||
|
|
- Run: `cd backend && uv run pytest tests/unit/core/auth/test_jwt_verifier.py tests/unit/v1/users -q`
|
||
|
|
- Run: `cd apps && flutter test test/core/api/api_interceptor_test.dart test/features/auth`
|
||
|
|
|
||
|
|
**Step 3: Runtime spot-check**
|
||
|
|
- Run: 登录拿 token 后请求 `/api/v1/agent/history`,确认不再因缺失 `iss` 返回 401。
|