DSH HUB
HomePlugin StorePlugin PacksCommunityRankingsResourcesPublish Guide
Plugin source
Back to catalog

v1xingyue /

dsh-auth-plugin

Verified

DSH 用户授权插件:密码 + 通用 OAuth 2.0(内置 GitHub/Google/Discord 模板)+ Solana/EVM 钱包登录。零依赖核心,纯配置接入

★ 1 Stars0 Forks0 IssuesN/A Community rating0 Confirmed installs
View on GitHub
READMESource: main@dec45bf6

DSH Auth Plugin — DeepSeek Harness 登录认证插件

给 DeepSeek Harness Web GUI 添加登录保护的 Cordis 插件。 零第三方依赖、单文件、极简配置,同时支持:

  • 🔑 用户名 + 密码登录(scrypt 哈希存储)
  • 🦖 Solana 钱包登录(ed25519 签名验证 + 公钥白名单)
  • 🛡️ 统一会话管理(HMAC-SHA256 令牌 + HttpOnly Cookie + 登出吊销)

目录

  1. 特性总览
  2. 架构与工作原理
  3. 安装
  4. 快速开始
  5. 配置参考
  6. API 参考
  7. 前端集成
  8. 安全设计
  9. 用户与密钥管理
  10. 测试
  11. 已知问题与故障排除
  12. 升级与回滚
  13. 生产部署建议
  14. 许可证

特性总览

特性 说明 状态
用户名密码登录 scrypt 哈希存储,支持明文(开发环境)与哈希 ✅
通用 OAuth 2.0 登录 任意授权码 provider(GitHub/Google/Discord…),纯配置接入 ✅
Solana 钱包登录 Phantom/Solflare,ed25519 challenge-response ✅
EVM 钱包登录 MetaMask 等,EIP-191 personal_sign + ecrecover ✅ 新增
白名单强制 钱包登录 allowlist/allowlist 必填,禁止任意钱包 ✅
会话令牌 HMAC-SHA256 自包含 token(JWT 风格) ✅
防重放 一次性 nonce/state,消费即删 ✅
登出吊销 服务端内存黑名单,防被窃 cookie 复用 ✅
统一门卫 包装 webserver fallback,保护全部 SPA 页面与静态资源 ✅
核心零依赖 node:crypto(scrypt/ed25519/HMAC)+ Node 全局 fetch + 手写 base58 ✅
可选增强 EVM 需要纯 JS 库 @noble/curves+@noble/hashes(缺失时自动禁用) ⚙️
极简安装 复制 1 个文件 + 2 行配置,无需 pnpm/npm ✅
npm 发布就绪 exports/files/engines/prepack 校验,npm pack 验证通过 ✅
bundle 化 dsh.bundle 声明,dsh plugin add 一行安装即生效 ✅

版本:1.8.0(package.json)· 测试:60 项(34 单元 + 26 端到端)


架构与工作原理

模块解析基础

DSH 的 Loader 以 profile 目录(~/.dsh/profiles/web/)为 baseUrl, 插件行的 name 直接传给 Node import(),因此:

  • name: "./dsh-auth-plugin.js" → 相对 profile 目录加载文件
  • name: "@scope/pkg" → 从 profile 的 node_modules 解析
  • name: "file:///abs/path" → 绝对路径加载

$DSH_HOME/profiles/node_modules 是 dsh 全依赖闭包的扁平回退目录 (含全部 195+ 个 @deepseek-ai/* 包),所以插件里 import "@deepseek-ai/schemastery" 等依赖无需安装即可解析—— 这是"复制即用"机制的根基。

请求流(门卫架构)

浏览器请求
   │
   ▼
webserver.match(pathname)
   │
   ├─ 精确路由表命中(exact)        → 直接处理
   │    /login                          → 登录页
   │    /api/auth/login                 → 密码登录 API
   │    /api/auth/logout                → 登出 API
   │    /api/auth/solana/challenge      → Solana 钱包 challenge
   │    /api/auth/solana/verify         → Solana 钱包 verify
   │    /api/auth/evm/challenge         → EVM 钱包 challenge
   │    /api/auth/evm/verify            → EVM 钱包 verify
   │
   ├─ 最长前缀路由命中(prefix)
   │    /api/auth/oauth/<id>/start      → OAuth 授权跳转(本插件)
   │    /api/auth/oauth/<id>/callback   → OAuth 回调(本插件)
   │    /api/*(client-connection 拥有)→ 保持 DSH 自带 loopback/
   │                                      trustedHosts 篱笆(刻意不拦截)
   │
   └─ 未命中 → fallback 座位(本插件包装)
        ├─ 公开路径?                → 放行
        ├─ 有有效会话 Cookie?       → 放行(用户信息挂 req.authUser)
        ├─ 浏览器请求(Accept: html)→ 302 → /login
        └─ API 调用                  → 401 JSON { code: "auth_required" }
              │
              ▼
        frontend-static(原始 dist 服务,SPA 页面与静态资源)

关键设计:插件注册为唯一 fallback 座位持有者(替换 frontend-static 的占座,认证通过后转交原始 handler)。这比注册 prefix "/" 路由可靠—— webserver 的 prefix 匹配 pathname.startsWith("/" + "/") 对非根路径恒为 false,prefix "/" 实际拦不住任何路径(早期版本的 bug,已修复)。

会话令牌

  • 结构:<base64url(payload)>.<HMAC-SHA256(base64url(payload))>
  • Payload:{ u: 用户名, r: 角色, exp: 过期时间戳 }
  • 传输:HttpOnly + SameSite=Strict + Max-Age 的 Cookie
  • 吊销:登出时 token 进内存黑名单(重启清空)

Solana 登录流程

登录页点击「使用 Solana 钱包登录」
   │
   ├─ 1. POST /api/auth/solana/challenge { publicKey }
   │      校验 base58(32B) → 白名单检查(403) → 签发一次性 nonce
   │      返回 { nonce, message: "DSH Login <nonce>", expiresAt }
   │
   ├─ 2. 钱包 signMessage(message)(用户在钱包中确认)
   │
   └─ 3. POST /api/auth/solana/verify { publicKey, signature, nonce }
          白名单检查(403) → 消费 nonce(401 防重放) → ed25519 验签(401)
          → 通过则签发会话 Cookie

消息编码兼容两种:Phantom signMessage 直接 UTF-8 字节,以及 SIWS v0 官方格式(\xff + 小写 "solana offchain message" + preamble,见 Agave 规范)。

OAuth 授权码流程

登录页点击「使用 GitHub 登录」
   │
   ├─ 1. GET /api/auth/oauth/<id>/start
   │      签发一次性 state(绑定 provider,防 CSRF)
   │      302 → provider 授权页 ?client_id&redirect_uri&state[&scope]
   │
   ├─ 2. 用户在 provider 页面授权
   │
   ├─ 3. provider 302 → /api/auth/oauth/<id>/callback?code=&state=
   │      消费 state(一次性+过期+绑定)→ 失败 302 /login?error=oauth_bad_state
   │      服务端 code 换 token(client_secret 不下发浏览器)
   │      带 Bearer 取 userinfo → 映射 id/name/email
   │      签发会话 Cookie → 302 /

零依赖:token 交换与 userinfo 用 Node 全局 fetch(Node 18+)。

EVM 钱包流程(MetaMask 等)

登录页点击「⬡ 使用 EVM 钱包登录」
   │
   ├─ 1. POST /api/auth/evm/challenge { address }
   │      校验 0x 地址 → 白名单检查(403) → 签发一次性 nonce
   │      返回 { nonce, message: "DSH Login <nonce>", expiresAt }
   │
   ├─ 2. 钱包 personal_sign(message, address)(用户在钱包中确认)
   │      返回 65 字节 r||s||v 十六进制签名
   │
   └─ 3. POST /api/auth/evm/verify { address, signature, nonce }
          白名单检查(403) → 消费 nonce(401 防重放)
          → EIP-191 消息哈希 → ecrecover 恢复地址(secp256k1)
          → 恢复地址 === 声明地址 → 签发会话 Cookie

依赖可选的 @noble/curves + @noble/hashes(纯 JS、无 native); 缺失时 EVM 登录自动禁用并打印警告,其他登录方式不受影响。 安装(仅启用 EVM 时需要):

dsh plugin --profile web add @noble/curves @noble/hashes

安装

方式 A:直接复制(推荐,已验证)

# 1. 复制单文件到 web profile 目录
cp lib/index.js ~/.dsh/profiles/web/dsh-auth-plugin.js
# 2. ~/.dsh/profiles/web/cordis.patch.yml 追加
- insert:
    - id: auth
      name: "./dsh-auth-plugin.js"
      config:
        users:
          admin: admin123

重启 dsh web 生效。不需要改 package.json、跑 pnpm 或安装依赖。

方式 B:npm 包安装(发布到 registry 后)

# 从 npm registry 安装到 web profile
dsh plugin --profile web add dsh-auth-plugin

然后同样在 cordis.patch.yml insert 一行(name 用包名,依赖由 dsh plugin/pnpm 解析):

- insert:
    - id: auth
      name: "dsh-auth-plugin"
      config:
        users:
          admin: admin123

dsh plugin 是 pnpm 转发器;本插件为普通插件(无 dsh.bundle), 安装后需 insert 一行声明(bundle 形态见方式 C)。

发布到 npm(维护者)

插件已做好发布就绪(package.json 含 exports/files/engines/ prepack 校验):

npm pack                       # 本地验证产物(prepack 会先跑语法检查)
npm login
npm publish --access public
# 本地测试 tarball 安装
npm pack
dsh plugin --profile web add file:/path/to/your-org-dsh-auth-plugin-1.6.0.tgz

方式 C:bundle 化(推荐给发布后的 npm 包)✅ 已实现

本插件已声明 "dsh": { "bundle": { "patch": "./cordis.patch.yml" } }, 安装即自动成为配置层,连 cordis.patch.yml 的 insert 都省了:

dsh plugin --profile web add dsh-auth-plugin
# → pnpm 安装 → 自动加入 bundles 层 → auth 插件行自动生效
#    (默认 admin/admin123,启动警告,请立即覆盖配置)

安装后在 profile 的 cordis.patch.yml 按 id 覆盖 config 即可:

- id: auth
  config:
    users:
      admin: { password: "scrypt$...", role: admin }
    oauth:
      providers:
        github: { builtin: github, clientId: "...", clientSecret: "..." }

已在隔离 DSH_HOME 完整验证:dsh plugin add <tarball> → bundles 列表 自动追加 → --dump-config 出现 # == dsh-auth-plugin 段。

安装后文件布局

~/.dsh/profiles/web/
├── cordis.patch.yml         # 认证配置(insert auth 行)
├── dsh-auth-plugin.js       # 插件本体(单文件)
├── package.json             # profile manifest(可选加 "type": "module")
└── pnpm-workspace.yaml

快速开始

# cordis.patch.yml —— 最简配置:用户名密码登录
- insert:
    - id: auth
      name: "./dsh-auth-plugin.js"
      config:
        users:
          admin: admin123
# 用户名密码 + Solana 钱包登录(allowlist 必填)
- insert:
    - id: auth
      name: "./dsh-auth-plugin.js"
      config:
        users:
          admin:
            password: "scrypt$<salt>$<hash>"
            role: "admin"
        solana:
          enabled: true
          allowlist:
            - "Dy6mBH4YeqJCRZohd39iSFaf4jyLaxPeBakbZwt1jToL"

重启 dsh web 后访问 http://127.0.0.1:3080 即重定向到登录页。


配置参考

顶层配置

字段 类型 默认值 说明
enabled boolean true 总开关
secret string 每次启动随机 会话签名密钥;可用环境变量 DSH_AUTH_SECRET 固定
ttlHours number 24 会话有效期(小时)
title string DSH 登录 登录页标题
cookie string dsh_session 会话 Cookie 名
users dict 空→admin/admin123 用户表(见下)
solana false | object false Solana 钱包登录配置(见下)
evm false | object false EVM 钱包登录配置(见下)
oauth object {providers:{}} 通用 OAuth 配置(见下)
publicPaths string[] 见下 免认证路径(精确或前缀匹配)

默认 publicPaths:/login、/api/auth/login、/api/auth/logout、 /favicon.ico

⚠️ secret 为空时每次启动随机——重启后所有会话失效需重新登录。 这是保守设计(无需持久化);生产环境建议固定:secret: !!js process.env.DSH_AUTH_SECRET。

users 三种写法

# 1. 极简:用户名: 明文密码(开发环境)
users:
  admin: admin123

# 2. 带角色
users:
  admin: { password: admin123, role: admin }

# 3. 推荐:scrypt 哈希
users:
  admin: { password: "scrypt$<salt>$<hash>", role: admin }

明文密码启动时会打印警告(仅限开发环境)。

solana 配置

字段 类型 默认值 说明
enabled boolean true(对象形式) 开关;solana: false 或省略 = 禁用
challengeTtlMs number 300000 nonce 有效期(5 分钟)
allowlist string[] 必填 公钥白名单(base58),至少 1 个
role string user 钱包登录默认角色

⚠️ allowlist 必填:启用时缺字段或空数组都会在加载时报配置错误 (schema 层拦截)——不允许"任何钱包可登录"。

evm 配置(MetaMask 等 EVM 钱包)

evm:
  enabled: true
  allowlist:                # 必填:只允许这些 0x 地址(小写或混合大小写均可)
    - "0x4e984616e2dd9dffe7f2413efc7da35ef64c4117"
  role: user
字段 类型 默认值 说明
enabled boolean true(对象形式) 开关;evm: false 或省略 = 禁用
challengeTtlMs number 300000 nonce 有效期(5 分钟)
allowlist string[] 必填 允许的 0x 地址,至少 1 个
role string user 登录成功角色

⚠️ 同 Solana:allowlist 必填;且需要可选依赖 @noble/curves + @noble/hashes(dsh plugin --profile web add @noble/curves @noble/hashes), 缺失时该功能自动禁用(其余登录不受影响)。

oauth 通用 OAuth 2.0 配置

每个 provider 一条,纯配置接入任意标准授权码 OAuth 服务。 支持内置模板:填 builtin + 凭据即可,端点/字段映射/scope 自动填充。

最简方式:内置模板(推荐)

oauth:
  providers:
    github:                       # provider id
      builtin: "github"           # ← 内置模板:端点/字段/scope 自动填充
      clientId: "Ov23li..."
      clientSecret: "ghp_..."     # 仅服务端使用,绝不下发浏览器

内置模板一览(BUILTIN_OAUTH,显式字段可覆盖模板):

builtin 授权端点 token 端点 userinfo 端点 默认 scope idField
github github.com/login/oauth/authorize …/access_token api.github.com/user read:user id
google accounts.google.com/o/oauth2/v2/auth oauth2.googleapis.com/token …/oauth2/v3/userinfo openid email profile sub
discord discord.com/oauth2/authorize discord.com/api/oauth2/token discord.com/api/users/@me identify email id
gitlab gitlab.com/oauth/authorize gitlab.com/oauth/token gitlab.com/api/v4/user read_user id
microsoft login.microsoftonline.com/common/oauth2/v2.0/authorize …/token graph.microsoft.com/v1.0/me User.Read id
bitbucket bitbucket.org/site/oauth2/authorize …/access_token api.bitbucket.org/2.0/user account uuid

完整方式:显式配置(任意标准 OAuth 服务)

oauth:
  providers:
    custom:
      label: "我的服务"
      clientId: "..."
      clientSecret: "..."
      authorizeUrl: "https://.../authorize"
      tokenUrl: "https://.../token"
      userInfoUrl: "https://.../userinfo"
      scope: "read"                # 可选
      idField: "id"                # 可选,默认 id
      nameField: "name"            # 可选
      emailField: "email"          # 可选
      role: "user"                 # 可选
      # redirectUri: "https://..." # 可选:显式回调地址
字段 类型 默认值 说明
builtin string "" 内置模板名(见上表);填了则端点/字段/scope 用模板
label string provider id / 模板 登录页按钮文案
clientId / clientSecret string 必填 OAuth 应用凭据
authorizeUrl / tokenUrl / userInfoUrl string 模板值 三个端点
responseType string code 授权响应类型(授权码模式)
scope string 模板值/空 请求的 scope(空格分隔)
redirectUri string 自动 显式回调地址;留空 = http://<Host>/api/auth/oauth/<id>/callback
idField / nameField / emailField string id / 模板 userinfo 字段映射
emailDomains string[] [] 邮箱域名白名单;非空时邮箱域名必须命中,否则拒绝(oauth_email_not_allowed)
role string user 登录成功角色
userInfoHeaders dict {} 取 userinfo 附加请求头
tokenParams dict {} token 请求附加参数
stateTtlMs number 600000 state 有效期(10 分钟)

回调地址(OAuth 应用后台填写):http(s)://<你的地址>/api/auth/oauth/<id>/callback 例如:http://127.0.0.1:3080/api/auth/oauth/github/callback 未知 builtin 名或合并后缺必需字段会在启动时报配置错误。


API 参考

GET /login

返回内置登录页(含密码表单 + 可选 Solana 钱包区块)。状态码:200。

POST /api/auth/login

用户名密码登录。支持 JSON 与 application/x-www-form-urlencoded。

curl -X POST http://127.0.0.1:3080/api/auth/login \
  -H 'Content-Type: application/json' \
  -d '{"username":"admin","password":"secret"}'
结果 状态码 说明
成功(JSON 调用) 200 { ok, username, role } + Set-Cookie
成功(浏览器表单) 302 重定向 / + Set-Cookie
凭据错误(JSON) 401 { error: "invalid credentials" }
凭据错误(表单) 401 登录页 + 错误提示
缺参 400 { error: "username and password required" }
方法错误 405 非 GET/POST

POST /api/auth/logout

登出。服务端将当前 token 加入吊销黑名单并清除 Cookie。 成功:200 { ok: true }(JSON)或 302 → /login(浏览器)。

POST /api/auth/solana/challenge

签发一次性 nonce(绑定公钥)。

curl -X POST http://127.0.0.1:3080/api/auth/solana/challenge \
  -H 'Content-Type: application/json' \
  -d '{"publicKey":"<32字节base58>"}'
结果 状态码 说明
成功 200 { nonce, message, expiresAt }
公钥非法 400 { error: "invalid public key" }
不在白名单 403 { error: "public key not allowed" }

POST /api/auth/solana/verify

提交钱包签名换取会话。

curl -X POST http://127.0.0.1:3080/api/auth/solana/verify \
  -H 'Content-Type: application/json' \
  -d '{"publicKey":"<base58>","signature":"<base58 64B>","nonce":"<challenge 返回>"}'
结果 状态码 说明
成功 200 { ok, username: "solana:<pubkey>", role, publicKey } + Set-Cookie
不在白名单 403 拒绝
nonce 无效/过期/重放 401 { error: "invalid or expired challenge" }
签名验证失败 401 { error: "signature verification failed" }
缺参 400 { error: "publicKey, signature and nonce required" }

POST /api/auth/evm/challenge

EVM 钱包登录第一步:签发一次性 nonce(绑定 0x 地址)。

curl -X POST http://127.0.0.1:3080/api/auth/evm/challenge \
  -H 'Content-Type: application/json' \
  -d '{"address":"0x4e984616e2dd9dffe7f2413efc7da35ef64c4117"}'
结果 状态码 说明
成功 200 { nonce, message: "DSH Login <nonce>", expiresAt }
地址非法 400 { error: "invalid address" }
不在白名单 403 { error: "address not allowed" }

POST /api/auth/evm/verify

提交 personal_sign 签名换取会话(需要可选依赖 @noble)。

curl -X POST http://127.0.0.1:3080/api/auth/evm/verify \
  -H 'Content-Type: application/json' \
  -d '{"address":"0x...","signature":"0x<65字节r||s||v>","nonce":"<challenge 返回>"}'
结果 状态码 说明
成功 200 { ok, username: "evm:<address>", role, address } + Set-Cookie
不在白名单 403 拒绝
nonce 无效/过期/重放 401 { error: "invalid or expired challenge" }
ecrecover 不匹配 401 { error: "signature verification failed" }
缺参/地址非法 400 { error: "address, signature and nonce required" }

GET /api/auth/oauth/<id>/start

发起 OAuth 登录。签发一次性 state 后 302 到 provider 授权页 (authorizeUrl?client_id&redirect_uri&state[&scope])。 未知 provider → 404。

GET /api/auth/oauth/<id>/callback

OAuth 回调(用户从 provider 授权页跳回)。浏览器直接访问即可,无需手动调用。

# 模拟(真实流程由浏览器跳转完成)
curl -i "http://127.0.0.1:3080/api/auth/oauth/github/callback?code=xxx&state=<start 返回的 state>"
结果 状态码 行为
成功 302 重定向 / + Set-Cookie(会话已建立)
state 无效/过期/重放 302 重定向 /login?error=oauth_bad_state(防 CSRF)
token 交换失败 302 /login?error=oauth_token_failed
userinfo 获取失败 302 /login?error=oauth_userinfo_failed
userinfo 缺 id 字段 302 /login?error=oauth_no_id
未知 provider 302 /login?error=oauth_unknown_provider

会话身份:oauth:<providerId>:<id>(如 oauth:github:user-42)。


前端集成

密码表单

内置登录页为自包含 HTML(无外部资源),POST 到 /api/auth/login, 成功即设置 Cookie 并跳转 /。

Solana 钱包登录

登录页内联脚本(零外部依赖):

  1. 检测钱包:window.solana.isPhantom → window.phantom.solana
  2. 点击按钮 → provider.connect() 取公钥
  3. 请求 challenge → provider.signMessage(TextEncoder.encode(message), "utf8")
  4. 签名 base58 编码后 POST verify → 成功 location.href = "/"

支持 Phantom、Solflare 等注入 window.solana 的钱包;未安装钱包时 按钮下方提示"未检测到钱包"。

OAuth 第三方登录

登录页按配置为每个 provider 渲染一个按钮(<a href="/api/auth/oauth/<id>/start">), 点击即进入标准 OAuth 授权码流程,无需前端脚本。

EVM 钱包登录

登录页「⬡ 使用 EVM 钱包登录」按钮,内联脚本检测 window.ethereum: eth_requestAccounts 取地址 → challenge → personal_sign → 提交 verify。


安全设计

项 实现
密码存储 node:crypto scrypt(随机盐),支持明文仅限开发
令牌完整性 HMAC-SHA256 签名,timingSafeEqual 比较
Cookie HttpOnly; SameSite=Strict; Path=/; Max-Age
登出吊销 服务端内存黑名单(revoked Set)
nonce 防重放 一次性消费,consume 即删;绑定公钥/地址;TTL 5 分钟 + 惰性 sweep
OAuth state 防 CSRF 一次性 + 绑定 provider + 10 分钟 TTL;伪造/重用 state → oauth_bad_state
client_secret 保密 仅服务端持有,token 交换在服务端完成,绝不下发浏览器
userinfo 服务端获取 Bearer token 不出服务端
EVM 验签 EIP-191 personal_sign 消息哈希 + secp256k1 ecrecover 恢复地址,与声明地址比对
登录审计 每次登录成功 info / 失败 warn 打点([dsh-auth] login ok/failed: <method> <身份> [原因])
白名单强制 钱包登录 allowlist/allowlist 必填,challenge 与 verify 双重 403
消息重建 服务端 DSH Login <nonce> 重建,不信任客户端回传 message
定时器依赖 inject: ["webServer", "timer"] 显式声明(Cordis 强制)
/api 边界 保持 DSH 自带 loopback/trustedHosts 篱笆,插件不越权

用户与密钥管理

生成 scrypt 哈希

# 方式一:内置脚本
node scripts/manage-users.js hash 你的密码

# 方式二:一行命令
node --input-type=module -e "
import { scrypt, randomBytes } from 'node:crypto';
const salt = randomBytes(16).toString('hex');
scrypt(process.argv[1], salt, 32, (e,k)=>{ if(e) throw e; console.log('scrypt\$'+salt+'\$'+k.toString('hex')); });
" 你的密码

添加/修改/删除用户

直接编辑 cordis.patch.yml 的 users 表后重启即可。支持多用户、多角色 (admin / user,角色目前仅作标识,可由其他插件消费 req.authUser)。

Solana 公钥获取

钱包连接后从 publicKey.toString() 得到 base58 地址,填入 allowlist。 验证合法性:node -e "import('./dsh-auth-plugin.js').then(m => console.log(m.base58Decode('...').length === 32))"


测试

# 单元测试(核心零依赖,直接可跑;EVM 组需要 @noble,缺失时自动跳过)
node --test test/auth.test.js
# → 34 项:scrypt、HMAC 令牌、base58、ed25519 验签(含 SIWS v0)、
#   nonce/state 管理、EVM ecrecover、OAuth 模板合并

# 端到端测试(需要 DSH 的 node_modules 环境解析 schematery)
node --test test/e2e.test.js
# → 26 项:密码登录、Cookie、登出吊销、Solana/EVM 钱包、白名单 403、
#   fake OAuth provider 授权码流程(含内置模板)、邮箱域名白名单、防重放

已知问题与故障排除

已修复:cannot get property "timer" without inject(v1.1.0 → v1.2.0)

启用 Solana 后启动失败:

Error: dsh: plugin tree failed to load:
failed to apply loader entry auth (./dsh-auth-plugin.js):
cannot get property "timer" without inject

根因:ctx.setInterval() 由 Cordis 的 timer 服务提供,新版 Cordis 要求显式声明所有经 ctx 使用的服务;原 inject = ["webServer"] 遗漏了 timer。

修复:const inject = ["webServer", "timer"]。

完整排查记录见 docs/repair-timer-inject.md。

常见问题

现象 处理
启动报 failed to import loader entry auth 确认 ./dsh-auth-plugin.js 相对 profile 目录路径正确、文件存在
报找不到 @deepseek-ai/schemastery 确认 ~/.dsh/profiles/node_modules 存在(dsh 首次启动自动生成)
登录后又被弹回登录页 secret 未固定 → 重启后旧会话失效,属正常;或系统时间异常
cannot get property "timer" 确认 inject 含 "timer"(v1.2.0 起已内置)
Solana 按钮不显示 确认 solana.enabled: true 且 allowlist 非空
钱包登录报 403 钱包公钥不在 allowlist
钱包签名后 401 nonce 过期(5 分钟)或已被使用,重新点击登录
OAuth 按钮不显示 确认 oauth.providers 至少配置了一个 provider
OAuth 回调 oauth_bad_state 刷新/重放旧回调,或 state 过期(10 分钟);重新从登录页发起
OAuth 回调 oauth_token_failed 检查 clientId/clientSecret 与回调地址是否与 provider 后台一致
OAuth 回调 oauth_no_id userinfo 响应里没有 idField 指定字段,按 provider 响应调整映射
EVM 按钮不显示 确认 evm.enabled: true 且 allowlist 非空,且已安装 @noble(缺失时启动日志有警告)
EVM 登录报 403 钱包地址不在 allowlist
EVM 签名后 401 nonce 过期(5 分钟)或已被使用,重新点击登录;或签名格式非 0x + 65 字节
启动时的 MODULE_TYPELESS 警告 无碍;在 profile 的 package.json 加 "type": "module" 消除

升级与回滚

升级

cp <新版本>/lib/index.js ~/.dsh/profiles/web/dsh-auth-plugin.js
# 重启 dsh web

回滚

cp ~/.dsh/profiles/web/cordis.patch.yml.bak ~/.dsh/profiles/web/cordis.patch.yml
rm -f ~/.dsh/profiles/web/dsh-auth-plugin.js

(安装时自动备份了 cordis.patch.yml.bak;再次改动前有 .bak2。)


生产部署建议

  1. 固定 secret:secret: !!js process.env.DSH_AUTH_SECRET(环境变量注入)
  2. HTTPS 反向代理:Nginx/Caddy 终结 TLS,转发 127.0.0.1:3080 (注意 WebSocket upgrade 头:Upgrade / Connection: upgrade)
  3. 强密码:全部用户使用 scrypt 哈希,避免明文
  4. 钱包白名单:Solana 登录务必维护 allowlist,公钥即身份
  5. OS 边界:防火墙限制端口暴露;/api 仍由 DSH 自带篱笆保护
  6. 日志审计:如需登录审计日志,可 fork 插件在 loginHandler / verifyHandler 成功后打点

许可证

MIT

DSH HUB

A community index for DSH plugins. Not an official GitHub or DeepSeek AI product.

CommunityResourcesAPIAbout
—/ 5

No ratings yet

Verified DSH bundle

Commit dec45bf6132e

Community comments

No comments yet. Be the first to write one.