DeepSeek account browser login
DeepSeek 账号浏览器登录
Desktop login must preserve local cancellation across the browser, UI, and Host and report success only after credentials are saved.
English
Problem
Desktop login must preserve local cancellation across the browser, UI, and Host and report success only after credentials are saved.
Decision
The account Service Definition separates UI and model consumers from the platform protocol implementation. The platform provider registers an AuthorizationFlow and persists an owner-scoped GrantRecord. Account presence is derived from storage. Login attempts stay in memory and expose safe snapshots through the account Remote controller.
Local cancellation is authoritative. The provider stops callbacks and ignores late exchange results; auth_cancel invalidates the remote application and unexchanged code using authorize_id and the original PKCE verifier. Its background request does not delay local cancellation, and failure never restores login. session.commit admits persistence synchronously before its first await and rejects an already-cancelled flow. Cancellation after admission waits for the commit result. The callback redirects to auth_exchange.biz_data.authorized_url only after authorization settlement confirms storage.
Authorization registers a temporary /oauth/callback route on the Host webServer. The authenticated initiating client supplies its browser-accessible loopback HTTP origin, including any local SSH forwarding port. Non-loopback reverse proxies are unsupported. Cleanup removes only the route and preserves shared connections. Exchange failure returns Web to its login UI or focuses Desktop through account state; retry is explicit. PKCE secrets never cross UI transports. Model and file request tokens resolve only for the configured inferenceOrigin, defaulting to https://api.deepseek.com; redirects are refused. An explicit alternate origin requires grants from the configured Platform issuer, so changing the endpoint alone never forwards account credentials. Development loopback grants cannot authenticate production requests. API-key references remain separately stored.
Platform navigation and authorization share one validated platformOrigin from Cordis configuration. Private profile patches or environment expressions supply deployment-specific origins without committing development addresses.
Platform client identity belongs to the calling UI rather than to the Host. Every account operation carries the caller's client version, active language, and current UTC offset, and the provider derives the five Platform client headers for that call from them plus the composed native platform. Client identity headers override same-named deployment headers, so one Host shared by several callers reports the UI that made each request instead of the last one it saw, and the update-policy client keeps the same header meanings for the installed shell. PlatformSession deliberately omits caller identity; the embedding client composes its own five headers and limits them to the configured origin.
Alternatives considered
Putting the protocol in Electron duplicates the implementation for Web consumers and gives the shell credential ownership. Putting tokens in DEEPSEEK_API_KEY loses the distinction between account grants and user API keys. Treating a browser callback as success before persistence admits false success. Waiting for a remote cancel acknowledgment leaves local cancellation dependent on network availability. Remembering one client identity in the Host would mislabel every other caller that shares it or reaches it through a forward. These alternatives are rejected.
Consequences
Sign out removes the local grant before starting Platform POST /auth-api/v0/users/logout with the captured token. Remote failure never restores login. The provider retries at most five times after the initial request, with configurable exponential backoff starting at one second by default. Retry jobs retain only their old token, cannot change later login state, and end at provider shutdown without durable storage. Local credential deletion must succeed before publishing signed-out state. Sign out belongs to the sidebar account menu; Account settings owns profile, balance and sign-in. Both read one plugin-owned Host stream through framework hooks.
API keys and account grants remain separately stored. Account tokens have no proactive refresh flow; exhausted logout retries can leave the removed token valid remotely. Provider initialization discards grants from a different configured Platform issuer locally, without remote revocation, so switching environments starts signed out instead of aborting Desktop startup. API keys and device identity survive this cleanup. Profile and balance HTTP 401 responses clear the rejected credential; other query failures preserve login state; authorization-attempt deadlines apply only before token issuance. The same grant authenticates Platform current and get_user_summary queries on its configured issuer origin. Host preserves Platform-masked contact details and drops response tokens; account changes invalidate pending results. UI shows normal_wallets recharge balances and positive bonus_wallets in separate rows, so promotional credit is never presented as recharge funds.
The account plugin supplies choice, waiting, failure and timeout dialogs through settings.models.sign-in. The model package owns credential readiness and the existing API-key editor; the settings shell coordinates explicit reopening so login and API-key onboarding do not mount competing dialogs.
Platform embedding transfers a grant from Host to Electron over private Node IPC and then once to the trusted Platform main frame through a sandboxed preload. Preload performs one synchronous IPC before page scripts execute; the main process only validates the sender and returns prepared memory. Platform reads the token synchronously thereafter without a readiness API. A failed initialization retains embedded mode and a throwing getter. Account UI projections remain credential-free. The Platform document can read this credential, so its script security is part of account protection; context isolation protects native capabilities, not a token intentionally returned to the document. Replacing or removing the grant destroys the document and clears browser authentication; account-scoped page storage survives.
Support questionnaires receive only available environment metadata through explicit prefill fields. Account UIDs, tokens and masked contact details stay out of questionnaire URLs.
Private proxy development can explicitly map authorization and completion URLs to platformOrigin with rewriteBrowserOrigin. Both pages retain their fixed paths and full query strings; allowing a remote origin without mapping it would send the browser outside the configured environment. Shipped configuration requires same-origin URLs.
DSH grants authenticate Platform, inference and Files requests with x-dsh-auth-token, without a Bearer prefix. API-key authentication remains protocol-specific.
Profile and recharge-wallet queries use independent getProfile and getBalance operations. The client publishes each result as it arrives, so a slow or failed balance request cannot delay the sidebar username. Account changes invalidate both pending results.
Deployment authentication uses explicit Host-only requestHeaders on the configured Platform origin. The provider rejects redirects and reserved-header overrides so deployment cookies cannot replace account authorization or follow a browser destination. Environment-specific authentication protocols remain outside the account provider.
The bundled account UI mounts the account launcher, settings and sign-in onboarding in both Desktop and plain Web. The Host sends login_source (desktop or web) in auth_init so Platform can choose the completion experience; the UI sends desktop when the Desktop preload bridge is present and web otherwise. Web failures close the authorization tab while the original tab receives Host state; no Web UI return URL is used. The backend accepts localhost callbacks. DSH preserves the browser-supplied localhost hostname and port without DNS resolution or conversion to an IP literal.
The macOS development launcher registers an isolated, ad-hoc-signed application bundle for dsh://open. The bundle retains the workspace entry and development paths for Launch Services cold starts without copying credentials or changing the package-manager-owned Electron application. Protocol registration targets the latest launched development or packaged application.
Exchange user data supplies the first profile read after credential commit, avoiding a second request before displaying the username. The Host retains only projected UI fields in the active attempt, matched to its token and consumed once; later reads use current. Missing or malformed user data does not discard successful authorization.
The embedded Platform document stays hidden during loading because native child views cover renderer overlays. Only the current document may become visible after loading; returning or signing out invalidates pending visibility changes. Native ownership also ends when the application document reloads or is replaced, its renderer terminates, or its window closes. React effect cleanup alone is insufficient because document teardown may never execute it. Same-document and subframe navigation preserve the view.
Separate accountRequestHeaders route account data and embedded Platform traffic independently of authorization and logout. Cookie overrides merge by name, retaining deployment authentication. Host passes the resolved headers over private process IPC; Electron injects them only at the configured origin and omits them from bootstrap.
Bonus notices use Platform-provided text and order identities. Reads occur when an account becomes active — signed-in startup and successful sign-in — and once per Settings entry or return from the top-up view, with no periodic polling. The card is painted as soon as the server offers it, including underneath the open Settings panel, so display is never deferred. The client acknowledges an order after a presented frame or the user closes it; nothing is written to browser storage, so a later sign-in re-displays an order the server still offers. Failed acknowledgments retry with capped exponential backoff for the rest of the signed-in session. Platform acknowledgments suppress delivery on other devices, but concurrent displays before acknowledgment are possible because the API does not reserve delivery. Fetch success alone never records a display.
The Host binds private Platform sessions to the account provider lifetime. Removal or watch termination clears Electron’s session; replacement subscribes to the new provider, and disposed reads cannot publish old credentials.
The client distinguishes plugin disposal from terminal account-stream failure. RemoteStream aborts its signal after either outcome, so the plugin owns a separate disposal flag to preserve failure feedback while suppressing reports after unload.
Verification
Provider tests exercise real loopback callbacks, invalid state, delayed exchange cancellation, credential persistence, sign-out, and official-origin restrictions. Desktop tests cover the native action bridge and localized entry. Manual development integration uses the platform dev middleware Mock and the real Electron Host, including cancellation before browser approval. Production backend credentials and installer scheme registration require release-environment validation.
Related
Credential records and flows remains the generic credential authority. Desktop wrapper owns the transport composition.
中文
问题
桌面登录必须在浏览器、UI 和 Host 之间保留本地取消意图,并只在凭证保存后宣告成功。
决策
账号服务定义将 UI 和模型使用方与平台协议实现分开。平台提供者注册 AuthorizationFlow,并保存按所有者寻址的 GrantRecord。账号是否存在从存储派生。登录尝试保留在内存中,通过账号 Remote 控制器提供安全快照。
本地取消具有决定权。提供者停止回调并忽略迟到的兑换结果;auth_cancel 使用 authorize_id 和原始 PKCE verifier 作废远程申请及未兑换的 code。后台请求不延迟本地取消,失败也不会恢复登录。session.commit 在第一次 await 前同步准入持久化,并拒绝已取消的流程。准入后的取消等待提交结果。回调只有在授权结束确认存储后才跳转 auth_exchange.biz_data.authorized_url。
授权在 Host webServer 上注册临时 /oauth/callback 路由。已鉴权的发起客户端提供浏览器可访问的本机 HTTP 来源,包含 SSH 本地转发端口。不支持非本机域名反向代理。清理仅移除路由,保留共享连接。兑换失败返回 Web 登录界面,或通过账号状态聚焦 Desktop;重试由用户明确发起。PKCE 私密数据不经过 UI 传输。模型和文件请求的 token 仅为配置的 inferenceOrigin 解析,默认为 https://api.deepseek.com;请求拒绝重定向。显式配置其他来源时,要求授权由配置的 Platform 来源签发,因此仅修改请求地址不会转发账号凭证。本机开发授权记录不能认证生产请求。API Key 引用独立保存。
平台导航与授权共享 Cordis 配置中同一个经过校验的 platformOrigin。私有 profile patch 或环境表达式提供部署来源,开发地址不进入仓库。
Platform 客户端身份属于发起调用的界面,而不属于 Host。每个账号操作都携带调用方的客户端版本、当前语言和当前 UTC 偏移,provider 据此与组合出的原生平台推导该次调用的五个 Platform 客户端请求头。客户端身份请求头覆盖部署配置中的同名请求头,因此一个被多方共享的 Host 报告的是每次请求的发起界面,而不是它上一次见到的调用方;更新策略客户端对已安装的壳沿用同一组请求头语义。PlatformSession 有意不携带调用方身份;内嵌客户端自行组装同样的五个请求头,并仅在配置来源发送。
替代方案
把协议放在 Electron 会为 Web 使用方重复实现,并让外壳拥有凭证。把 token 放入 DEEPSEEK_API_KEY 会丢失账号授权与用户 API Key 的区别。在持久化前将浏览器回调视为成功会产生虚假成功。等待远程取消确认会使本地取消依赖网络。在 Host 中记住一份客户端身份,会让共享该 Host 或经转发接入的其他调用方被标错。这些方案均被拒绝。
影响
退出登录先删除本地授权,再使用捕获的 token 调用 Platform POST /auth-api/v0/users/logout。远程失败不会恢复登录态。首次请求失败后,提供者最多重试五次,使用可配置的指数退避,默认从一秒开始。重试任务只保留旧 token,不改变后续登录态,并在提供者关闭时结束,不持久化。本地凭证删除成功后才发布已退出状态。退出登录位于侧边栏账号菜单;账号设置负责资料、余额和登录。两者通过框架 hook 读取插件持有的同一条 Host 状态流。
API Key 与账号记录独立保存。账号 token 没有主动刷新流程;退登重试耗尽后,已从本地删除的 token 在远端仍可能有效。提供者初始化时在本地丢弃与配置的 Platform 来源不同的授权,不调用远端撤销,使环境切换以未登录态启动,而不导致 Desktop 启动失败。API Key 和设备标识保留。资料与余额接口的 HTTP 401 响应清除被拒绝的凭据,其他查询错误保留登录态;授权尝试的有效期仅适用于 token 签发前。同一授权 token 可认证已配置签发来源上的 Platform current 和 get_user_summary 查询。Host 原样保留 Platform 脱敏后的联系方式并丢弃响应 token;账号变化使未完成结果失效。UI 分行显示 normal_wallets 充值余额与正额 bonus_wallets 赠金余额,避免将赠金额度显示为充值资金。
账号插件通过 settings.models.sign-in 提供选择、等待、失败和超时对话框。模型包保留凭证就绪检查和现有 API Key 编辑器;设置外壳协调显式重开,避免登录与 API Key 引导同时挂载冲突的对话框。
Platform 内嵌通过私有 Node IPC 将授权从 Host 传给 Electron,再经沙箱 preload 一次性传给受信任的 Platform 主 frame。preload 在页面脚本执行前执行一次同步 IPC;主进程仅校验调用来源并返回已准备的内存数据。Platform 此后同步读取 token,无需就绪接口。初始化失败时保留内嵌模式,由 getter 抛错。账号 UI 投影仍不包含凭证。Platform 文档能够读取此凭证,因此其脚本安全也是账号保护的一部分;上下文隔离保护原生能力,不能隐藏有意返回给文档的 token。替换或移除授权会销毁文档并清理浏览器认证;账号范围内的页面存储会保留。
支持问卷仅通过显式预填字段接收已有的环境信息,URL 不包含账号 UID、token 或脱敏联系方式。
私有代理开发可通过 rewriteBrowserOrigin 显式将授权页和完成页映射到 platformOrigin。两者保留固定路径和完整查询字符串;仅放宽来源而不做映射会让浏览器离开配置的环境。发布配置要求浏览器地址同源。
DSH 授权通过 x-dsh-auth-token 请求头鉴权 Platform、推理和 Files 请求,不加 Bearer 前缀。API Key 继续使用各协议的认证方式。
资料和充值钱包余额分别通过 getProfile 与 getBalance 查询。客户端在各自返回时立即更新,余额请求慢或失败不会延迟侧边栏用户名显示。账号变化使两种进行中的查询结果失效。
开发环境认证通过配置的平台来源上的显式 Host 请求头 requestHeaders 完成。提供者拒绝重定向和保留请求头覆盖,防止开发环境 Cookie 替换账号授权或跟随浏览器跳转地址。特定环境的认证协议不属于账号提供者。
内置账号界面在 Desktop 和普通 Web 中都挂载账号入口、设置和登录引导。Host 在 auth_init 中发送 login_source(desktop 或 web),以便 Platform 选择完成页体验;存在 Desktop preload 桥接时界面发送 desktop,否则发送 web。Web 登录失败时关闭授权标签页,原标签页通过 Host 状态获知结果;不使用 Web UI 返回 URL。后端接受 localhost 回调。DSH 保留浏览器提供的 localhost 主机名和端口,不做 DNS 解析或 IP 字面量转换。
macOS 开发启动器为 dsh://open 注册独立、经临时签名的应用包。该应用包保留工作区入口和开发路径,使 Launch Services 能够冷启动;凭证不会被复制,也不修改包管理器安装的 Electron 应用。协议注册指向最近启动的开发版或打包版应用。
exchange 的 user 数据在凭证提交后供首次资料读取使用,展示用户名无需再等待一次请求。Host 仅在当前尝试中保留筛选后的 UI 字段,与 token 匹配并读取一次后移除;后续读取使用 current。缺失或格式无效的 user 不会使已成功的授权失效。
内嵌 Platform 文档在加载期间保持隐藏,因为原生子视图会覆盖渲染层浮层。加载完成后仅当前文档可显示;返回或退登会使待执行的显示操作失效。应用文档刷新或替换、其渲染进程终止或窗口关闭时,原生视图的所有权也随之结束。仅依赖 React effect 清理不足以保证释放,因为文档销毁时可能不执行它。同文档导航和子框架导航保留视图。
独立的 accountRequestHeaders 将账号资料及内嵌 Platform 流量与授权、退登分开路由。Cookie 覆盖按名称合并,保留部署认证。Host 通过私有进程 IPC 传递合并后的请求头;Electron 仅在配置来源注入,并从 bootstrap 排除。
赠金通知使用 Platform 提供的文案和订单标识。账号进入登录态(已登录启动、登录成功)时读取一次,每次进入设置页或从充值视图返回时再读取一次,不进行定时轮询。服务端下发的通知立即绘制,包括在打开的设置面板之下,因此展示从不延后。客户端在通知卡完成一帧绘制后或用户关闭卡片时确认订单。不写入任何浏览器存储,因此之后再次登录会重新展示服务端仍在下发的订单。确认失败会在本次登录会话内按带上限的指数退避重试。Platform 的确认记录阻止其他设备继续获取通知,但接口不预留展示资格,因此确认前仍可能并发展示。仅获取成功不会记录为已展示。
Host 将私有 Platform 会话绑定到账号提供者的生命周期。提供者移除或订阅结束时清空 Electron 会话;替换后订阅新的提供者,已释放的读取不得发布旧凭据。
客户端区分插件卸载和账户状态流的终止错误。RemoteStream 在两种情况下都会中止其信号,因此插件独立记录卸载状态,保留错误反馈并抑制卸载后的报告。
验证
提供者测试覆盖真实本机回调、错误 state、延迟兑换取消、凭证持久化、退出和官方来源限制。桌面测试覆盖原生操作桥接与本地化入口。手动开发联调使用平台 dev middleware Mock 和真实 Electron Host,包括在浏览器批准前取消。生产后端凭证及安装器 scheme 注册仍需发布环境验证。