父 Story:#7(M1 入口级交付 · webui)。WP4b-SDK。⚠ 架构关键 D1/D2/D6,骨架不可返工。红线:示例数据脱敏占位。
目标
会话承载租户声明,X-Tenant-Id 由会话派生(去硬编码);结构按多租户(M1 先 1 租户跑通,结构不返工)。
背景(别被「OIDC 管线已就绪」掩盖)
鉴权路径已完整落地:AppAuthProvider 运行期开关(isOidcEnabled()→真实 OIDC;DEV→mock;生产未配→fail-closed),token→Bearer、角色提取、登录守卫齐全。启用真实 OIDC = 纯部署期 config.js 注 oidc.authority(主仓/I3),零本仓代码。未做的是租户上下文这一半。
范围(文件清单)
packages/sdk/src/auth/session.ts:SessionUser/Session 加 tenant(及可选 tenants)字段。
packages/sdk/src/auth/extractRoles.ts(或新增 extractTenant.ts):读 JWT tenant 声明(org=租户)。
packages/sdk/src/auth/OidcSessionProvider.tsx + MockSessionProvider.tsx:注入 tenant 到统一 Session。
packages/sdk/src/api/http.ts:X-Tenant-Id 由会话派生,替换硬编码 tenant-demo;无租户时的兜底。
起点(精确路径 · 真/stub/缺失)
- 鉴权 provider/守卫/角色提取 = 真实已落地。
- tenant 链 未建:
SessionUser = {name,email,roles}(无 tenant);extractRoles 只读 realm_access/resource_access,不读 tenant;http.ts 第 11 行 DEFAULT_TENANT='tenant-demo' 硬编码 + TODO。
预估 LOC
~140
测试 / DoD
- 单测:从 JWT 提取 tenant 声明;http 拦截器附带会话派生的
X-Tenant-Id;无租户兜底路径。
- lint / vitest / build 绿。
依赖
验收
目标
会话承载租户声明,
X-Tenant-Id由会话派生(去硬编码);结构按多租户(M1 先 1 租户跑通,结构不返工)。背景(别被「OIDC 管线已就绪」掩盖)
鉴权路径已完整落地:
AppAuthProvider运行期开关(isOidcEnabled()→真实 OIDC;DEV→mock;生产未配→fail-closed),token→Bearer、角色提取、登录守卫齐全。启用真实 OIDC = 纯部署期config.js注oidc.authority(主仓/I3),零本仓代码。未做的是租户上下文这一半。范围(文件清单)
packages/sdk/src/auth/session.ts:SessionUser/Session加tenant(及可选tenants)字段。packages/sdk/src/auth/extractRoles.ts(或新增extractTenant.ts):读 JWTtenant声明(org=租户)。packages/sdk/src/auth/OidcSessionProvider.tsx+MockSessionProvider.tsx:注入 tenant 到统一 Session。packages/sdk/src/api/http.ts:X-Tenant-Id由会话派生,替换硬编码tenant-demo;无租户时的兜底。起点(精确路径 · 真/stub/缺失)
SessionUser = {name,email,roles}(无 tenant);extractRoles只读realm_access/resource_access,不读 tenant;http.ts第 11 行DEFAULT_TENANT='tenant-demo'硬编码 + TODO。预估 LOC
~140
测试 / DoD
X-Tenant-Id;无租户兜底路径。依赖
packages/sdk,与 WP2 · 九模块 L1/L2 导航骨架 + i18n + 路由占位(使能刀) #9 并行)。blocks:WP4b-UI · 顶栏租户切换占位 + GET /me/tenants #15(租户切换 UI)。验收
X-Tenant-Id,取自会话(非硬编码)。X-Tenant-Id唯一(M1 先 1 租户,结构按多租户)。