Appearance
功能规格说明:SSO 认证
创建日期:2026-05-12
用户场景与测试 (必填)
用户故事:通过浏览器进行 SSO 登录(优先级:P1)
作为在本地机器上工作的开发者,我希望在 Wave 中输入 /login 通过公司 SSO 进行认证,这样就不必手动配置 API 密钥。
为什么是这个优先级:这是主要的认证流程——大多数用户在本地机器上可以使用浏览器。
独立测试:设置 WAVE_SERVER_URL,运行 Wave,输入 /login,浏览器打开,完成 SSO 登录,验证 token 已保存且 API 请求成功。
验收场景:
- 假设
WAVE_SERVER_URL已设置且没有现有的 SSO token,当用户输入/login时,则 Wave 打开浏览器到 SSO 登录页面并在终端中显示认证 URL。 - 假设用户在浏览器中完成 SSO 登录,当浏览器重定向到带有
?code={code}的 localhost 回调 URL 时,则 Wave 通过POST /api/auth/token使用{ grant_type: "authorization_code", code }交换 code 获取 JWT,将 token 和 refresh token 保存到~/.wave/auth.json,并显示"Login successful"。 - 假设用户已通过 SSO 认证,当用户发送消息时,则所有 LLM API 请求发送到
WAVE_SERVER_URL/api/v1,使用 SSO token 作为 Bearer 认证。 - 假设用户已通过 SSO 认证,当用户输入
/login时,则 Wave 显示当前认证状态(截断的 token、AI URL)并提供按 Enter 退出的选项。 - 假设用户已认证并在登录 UI 中按 Enter,当他们确认退出时,则 SSO token 从
~/.wave/auth.json中移除,Wave 显示"Logged out successfully"。
用户故事:远程服务器的手动 Token 输入(优先级:P1)
作为通过 SSH 在远程服务器上工作的开发者,我希望在本地浏览器完成登录后手动粘贴 SSO token,以便在没有 localhost 回调转发的情况下进行认证。
为什么是这个优先级:大量开发者使用远程服务器(SSH、容器、devbox),localhost 回调无法到达 CLI。没有这个功能,/login 对他们来说无法使用。
独立测试:SSH 到远程服务器,设置 WAVE_SERVER_URL,运行 Wave,输入 /login,在本地浏览器中打开 URL,完成 SSO,从浏览器 URL 栏复制 token,粘贴到终端。
验收场景:
- 假设 Wave 在远程服务器上运行,当用户输入
/login时,则 Wave 显示 SSO 认证 URL 并提示"Paste the authorization code from your browser URL bar:"。 - 假设用户粘贴有效的授权码并按 Enter,则 Wave 通过
POST /api/auth/token使用{ grant_type: "authorization_code", code }交换 code 获取 JWT,将 token 和 refresh token 保存到~/.wave/auth.json,并显示"Login successful"。 - 假设用户粘贴空行,则 Wave 清除输入并继续等待 token 输入。
- 假设用户在 token 输入期间按 Escape,则登录流程被取消,Wave 返回空闲状态。
用户故事:自动 SSO API 路由(优先级:P1)
作为已通过 SSO 认证的开发者,我希望所有 LLM API 请求自动使用 Wave AI 代理,这样就不必配置 WAVE_API_KEY 或 WAVE_BASE_URL。
为什么是这个优先级:这是核心价值主张——SSO 认证应透明地通过 Wave AI 路由 API 流量而无需额外配置。
独立测试:通过 SSO 登录,发送消息,验证 API 请求发送到 WAVE_SERVER_URL/api/v1/chat/completions,使用 Bearer SSO token。
验收场景:
- 假设
~/.wave/auth.json包含有效的SSO_TOKEN,当 Agent 解析网关配置时,则它返回{ apiKey: SSO_TOKEN, baseURL: "${WAVE_SERVER_URL}/api/v1" },无论WAVE_API_KEY或WAVE_BASE_URL设置。 - 假设不存在
SSO_TOKEN,当 Agent 解析网关配置时,则它回退到现有行为(读取WAVE_API_KEY/WAVE_BASE_URL)。 - 假设
SSO_TOKEN存在但WAVE_SERVER_URL未设置,当解析网关配置时,则抛出配置错误并附带清晰消息。
用户故事:IDE 插件更多菜单的登录/退出登录入口(优先级:P2)
作为在 VS Code 或 JetBrains 插件中使用 Wave 的开发者,我希望在聊天头部"更多"菜单中按当前认证状态切换显示"登录"或"退出登录"入口,并在已有对话时退出登录不跳转到欢迎页,这样在 GUI 环境下也能便捷地完成认证状态切换且不丢失当前对话上下文。
为什么是这个优先级:IDE 插件(VS Code 扩展 + JetBrains 插件,共用同一 webview)没有 CLI 的 /login、/logout 斜杠命令入口的便利性,需要在 GUI 菜单中提供等价入口;已有对话时退出登录跳欢迎页会丢失上下文,违背用户预期。
独立测试:在 VS Code 扩展或 JetBrains 插件中打开已有对话,点击头部"更多"按钮,验证认证态显示"退出登录"、未认证态显示"登录";点击"退出登录"后对话消息保留且不跳欢迎页、菜单切换为"登录"。
验收场景:
- 假设用户已通过 SSO 认证且当前会话已有消息,当用户点击头部"更多"按钮打开菜单时,则菜单显示"退出登录"项(不显示"登录"项)。
- 假设用户已认证且当前会话已有消息,当用户点击"退出登录"时,则系统清除 SSO token、菜单关闭、认证状态切换为未认证,且当前对话消息保留、不跳转到欢迎页。
- 假设用户未认证(已退出或从未登录),当用户打开"更多"菜单时,则菜单显示"登录"项(不显示"退出登录"项)。
- 假设用户未认证,当用户点击"登录"时,则系统发起 SSO 登录流程(浏览器/手动 token 输入),登录成功后认证状态切换为已认证、菜单切换回"退出登录"项。
- 假设用户未认证且欢迎页(空对话引导页)显示"登录"按钮,当用户点击该按钮时,则它与"更多"菜单中的"登录"项触发同一登录流程,二者行为一致。
用户故事:IDE 插件更多菜单与欢迎页(优先级:P2)
作为 IDE 插件用户,我希望在"更多"菜单中直达设置、企业控制台和登录入口,并在没有对话时看到引导性的欢迎页,以便快速完成初始配置和日常使用。
为什么是这个优先级:菜单与欢迎页是 GUI 环境的入口框架,缺失时用户找不到设置与控制台入口;但核心对话能力不受影响,属于体验增强。
独立测试:打开插件,验证空对话欢迎页在未认证时显示"登录"按钮与「登录后即可开始使用~」引导文案、已认证时仅显示品牌引导;打开"更多"菜单验证包含设置、企业控制台和登录入口;聊天头部不显示独立的"登录"按钮(登录入口只存在于欢迎页与"更多"菜单);验证插件不再提供 API Key / Base URL 直连配置入口(未登录即不可用)。
验证分层(2026-09-10 追加拍板):本故事场景 5/7 的「未认证即显示登录入口、遗留直连配置不再关掉它」→ 单测 packages/webview/tests/webview/welcomeViewFlash.test.tsx + e2e(真浏览器 + 真 webview bundle)packages/webview/e2e/ide-welcome-login.e2e.ts;场景 8 的「未认证时发送入口禁用 + 禁用原因写在占位文案 + 登录后恢复」→ 单测 packages/webview/tests/webview/unauthenticatedInputDisabled.test.tsx(IDE 未登录)与 packages/webview/tests/webview/desktopApp.test.tsx(桌面未选目录「should disable the input area when no workdir is selected」两条原因独立 +「shows the login reason, not the workdir reason, when both reasons apply」登录优先)+ e2e packages/webview/e2e/ide-welcome-login.e2e.ts 与 packages/webview/e2e/desktop-unauthenticated-input-disabled.e2e.ts(真 bundle 验证两条原因互不覆盖、登录原因优先);「设置页不得存在凭据输入控件」→ e2e packages/webview/e2e/ide-settings-credentials-removed.e2e.ts(7 个视图全扫,且带遗留 apiKey/baseURL 的 configurationResponse 不得长出控件);「宿主→CLI 报文不得携带三个键、未登录时宿主如实回带未登录且不发模型请求」→ 真 host packages/desktop/tests/integration/realHostCredentialWire.integration.test.ts(tee 透明代理真 wave --stdio,断言 initialize / updateConfig / sendMessage 双向真报文)。逐条对照见 agent-config.md 边界说明「验证分层(单测 / e2e / 真 host)」。
验收场景:
- 假设用户打开"更多"菜单,当菜单渲染时,则包含"设置"、"企业控制台"、以及按认证状态切换的"登录/退出登录"入口(行为见下文对应条目)。
- 假设用户点击"更多"菜单中的"设置",当操作发生时,则打开设置页「全局设置」选项卡(见 agent-config.md「IDE 插件配置入口」用户故事)。
- 假设用户点击"更多"菜单中的"企业控制台",当操作发生时,则在系统浏览器中打开当前服务端地址对应的控制台页面。
- 假设当前会话没有任何可见消息(隐藏的 meta 用户消息不计入,如 SessionStart 钩子注入的
additionalContext)且初始化已完成,当界面渲染时,则显示欢迎页(而非空白对话区);初始化未完成时显示扫光加载动画,且加载动画显示期间不展示输入区域(输入框与目录选择器等一并隐藏),动画结束、初始化完成后输入区域随欢迎页一同出现。 - 假设欢迎页已显示(当前无任何可见消息)且初始化已完成、用户未认证,当欢迎页渲染时,则欢迎页在品牌引导下方显示「登录后即可开始使用~」引导文案与"登录"按钮(仅 IDE 宿主显示;desktop 的登录入口在侧边栏账户卡片,欢迎页不显示登录按钮);用户已认证时欢迎页仅显示品牌引导、不显示登录按钮与引导文案。IDE 宿主不再提供 API Key / Base URL 直连配置入口(2026-09-10 拍板:宿主端凭据链路整体下线)——设置页各视图内不得存在任何凭据输入控件(API Key / Base URL / headers 语义的输入框、下拉或文案),宿主向会话进程发送的 stdio 报文(
initialize/updateConfig等)也不得携带apiKey/baseURL/defaultHeaders(宿主→CLI→回包的整条链路都不得出现这三个键;保留的是 SDK / CLI 层的构造参数、WAVE_API_KEY/WAVE_BASE_URL环境变量与企业侧下发,见边界情况)。因此未认证时不存在"配好直连即可免登录使用"的旁路——IDE 未登录即不可用(未认证时发送入口的禁用形态见场景 8)。 - 假设用户在欢迎页输入并发送第一条消息,当消息发出后,则欢迎页切换为正常对话视图。
- 假设欢迎页显示"登录"按钮且用户点击它,当登录流程完成、认证状态切换为已认证时,则欢迎页的"登录"按钮与「登录后即可开始使用~」引导文案消失,欢迎页仅剩品牌引导;登录流程与"更多"菜单的"登录"项一致(见「IDE 插件更多菜单的登录/退出登录入口」)。
- 假设用户未认证,当对话界面渲染时,则发送入口整体禁用(输入区不可编辑,发送 / 附件 / 快捷指令 / 权限模式按钮均置灰),输入框占位文案显示禁用原因「请先登录后再发送消息」;输入区内不出现登录按钮、也不出现指路文案。假设用户随后完成登录,当认证状态切换为已认证时,则同一输入框立即恢复可编辑、占位文案回到默认提示(
/快捷指令,@添加上下文,粘贴图片,Enter发送...)。该禁用在桌面端与 IDE 插件端同一形态。桌面端另有「未选工作目录」这一独立禁用原因,其占位文案为「请先选择项目目录」;两条原因互斥且登录优先——未登录且同时未选目录时显示登录原因,登录后仍未选目录时占位文案切换为目录原因,选定目录后解除禁用并回到默认提示。
用户故事:CLI 欢迎页的登录引导(优先级:P2)
作为使用 CLI 的开发者,我希望在未通过 SSO 认证、也未配置直连 API 时,欢迎页提示我输入 /login 进行登录,这样即使不记得斜杠命令也能发现登录入口。
为什么是这个优先级:登录是可选的(直连 API 配置也可正常使用),缺少提示只是降低入口的发现性,不影响核心对话能力。
独立测试:在没有 SSO token 也没有 WAVE_API_KEY 的环境启动 CLI,验证欢迎页显示 /login 登录引导;配置 WAVE_API_KEY 后(或完成 SSO 登录后)重启 CLI,验证引导不再显示。
验收场景:
- 假设 CLI 已启动且用户未通过 SSO 认证、也未配置直连 API,当欢迎页渲染时,则在欢迎页显示登录引导文案(提示输入
/login)。 - 假设用户已通过 SSO 认证,当欢迎页渲染时,则不显示登录引导。
- 假设用户未通过 SSO 认证但已配置直连 API(
WAVE_API_KEY/WAVE_BASE_URL环境变量,或 settings.jsonenv中的同名变量),当欢迎页渲染时,则不显示登录引导(本条款为 CLI 语义——IDE 宿主无直连配置入口,未登录即不可用,见边界情况「IDE 宿主不再有直连免登录旁路」)。 - 假设欢迎页显示登录引导且用户输入
/login完成登录,当认证状态变化为已认证时,则登录引导实时消失。
边界情况
- IDE 宿主不再有「直连免登录」旁路(2026-09-10 拍板):宿主端(VS Code 扩展 / JetBrains 插件 / 桌面端)的 API Key / Base URL / headers 用户配置链路整体下线——宿主设置页与插件状态里不再有这三项,webview→宿主→stdio 的透传与回包字段一并删除,IDE 因此未登录即不可用(此前残留在插件 globalState / 插件状态里的旧
baseURL+apiKey仍可免登录聊天,该后门随之下线)。SDK / CLI 层不受影响:WAVE_API_KEY/WAVE_BASE_URL环境变量、构造参数(apiKey/baseURL/defaultHeaders)与企业侧下发仍保留(stdio 协议里的同名字段也因此保留,宿主只是不再填充),故 CLI 欢迎页的「已配置直连 API 则不提示登录」语义不变(见「CLI 欢迎页的登录引导」)。 - 未认证时发送入口禁用,禁用原因写在输入框里(2026-09-10 拍板,取代此前「不新增硬门禁」的口径):未认证时发送入口整体禁用——复用
MessageInput既有的disabled语义(输入区contentEditable=false,发送 / 附件("+")/ 快捷指令("/")/ 权限模式等按钮一并置灰),桌面端与 IDE 插件端同一形态。禁用原因只写在输入框的占位文案处(data-placeholder):未认证显示「请先登录后再发送消息」,桌面端「未选工作目录」显示「请先选择项目目录」(此前该禁用态沿用了通用的/快捷指令,@添加上下文…提示,属"禁用但不说原因",2026-09-10 一并改掉);两条原因互斥(同一时刻只有一条生效)且登录优先(未登录必然发不出消息,目录未选只是还没定位到项目,故两者同时成立时先说更根本的那条)。不新增提示行、不在输入区放登录按钮、不写「请去某处登录」的指路文案——登录入口本身用户能看见(IDE 在欢迎页与"更多"菜单,桌面在左侧账户卡片),已认证用户完全不受影响;文案由调用方按当前原因派生后传入MessageInput的可选占位覆盖参数,组件本身不硬编码任何禁用原因,未禁用时不传(回落默认/快捷指令…提示)。这样做的依据是实测事实:未登录必然发不出消息(无凭据时不产生任何模型请求,且不报错、只是静默挂住),所以把入口直接置灰并说明原因是唯一有意义的用户可见反馈;仍不新增「未登录即拒绝新建对话/拒绝恢复历史」这类宿主或 CLI 层硬门禁(三端均无,若产品需要须另立规格)。 - 如果 SSO 回调服务器端口已被占用会怎样? 服务器使用
localhost:0(系统分配的随机端口),避免端口冲突。 - 如果 Wave AI 上没有配置 SSO 提供程序会怎样? 登录失败并显示清晰的错误消息("No SSO providers available")。
- 如果登录时
WAVE_SERVER_URL未设置会怎样? 登录失败并显示清晰的错误,指示用户设置环境变量。 - 如果浏览器无法打开(无头服务器)会怎样? 认证服务器保持活动,用户可以手动打开 URL 并粘贴授权码。
- 如果用户将来在
auth.json中保存额外字段会怎样?saveAuth方法与现有配置合并,保留非 SSO_TOKEN 字段。 - 如果 token 过期(JWT 默认 8 小时)会怎样? 系统在过期前 5 分钟使用存储的 refresh token 主动刷新 token。如果 refresh token 被撤销(400/401),认证被清除,用户必须重新运行
/login。在刷新期间的瞬时网络错误中,保留现有 token,重试在下次请求时进行。 - 如果 SSO 登录超时(5 分钟)会怎样? 认证服务器关闭并显示错误。用户可以重试
/login。