Foreword¶
DSH (DeepSeek Harness) is open to anonymous access by default: you can enter a session by opening the homepage, and owner permissions within the same process are not split by individual. When deployed on an internal network or team environment, the problem arises—who has logged in and who is still using can only be checked via process logs; leaked access addresses can be used by anyone who can open the page. Existing community solutions usually involve writing a separate login middleware or placing a reverse proxy layer in front for authentication, but the result is either a disconnect from DSH’s native session or the need to maintain an additional component.
Introducing lloydzhou/dsh-feishu-oauth below. It is maintained by lloydzhou, categorized under admin-security, and integrates Feishu OAuth into DSH’s native authentication flow: upon successful login, it directly reuses DSH’s own session Cookie; the plugin neither modifies the core nor issues cookies on its own. This means that logout, session validity, and token recovery are still handled by DSH’s native mechanisms, and the plugin merely replaces the entry step.
A prerequisite to note: this plugin is not a multi-user permission system. Everyone who can log in using the Feishu application receives the full permissions of the same DSH owner; there are no independent accounts, users, or tenant whitelists, or per-user auditing or revocation mechanisms. The README explicitly states that it is “for trusted personnel only,” and the scope of personnel needs to be limited by the available scope of the Feishu application.
1. What problems it solves, and its boundaries¶
The plugin does only one thing: replace DSH’s entry from “anonymous direct entry” to “entry after Feishu authorization”.
Specific capabilities are as follows:
- Anonymous access to
/displays a “DSH × Feishu Login” card, providing an authorization login button and a login hint for the native token URL. The card supports Chinese and English, narrow screens, and the system’s dark mode, and does not load any external fonts, images, or scripts. - After successful login, the user remains on
/displaying the DSH page; the address bar does not jump elsewhere. The success callback uses a zero-latency meta refresh to return to the same-origin/, eliminating the need for the user to click confirm again, while ending the cross-site redirect chain to be compatible with nativeSameSite=StrictCookies. /?token=...serves as the recovery entry, always handled by native authentication.- Authentication for APIs and WebSockets is unaffected and continues to use native authentication.
- The login page style is authorized via a fixed CSP hash and does not depend on runtime-injected external resources.
The boundaries must also be clearly stated:
- Revoking authorization on Feishu, user logout, or closing the plugin will not invalidate already issued DSH Cookies; their validity is managed by the native session. The README suggests keeping the native token recovery method and explicitly warns against treating this plugin as a multi-user permission system.
- When OAuth is disabled or application credentials are missing, the plugin does not register a login route, and behavior falls back to the native entry.
- Successful configuration saving does not mean Feishu credentials or callback registration are valid; the README makes no promise that it will work immediately after saving.
2. Installation and Enabling¶
Environment requirements: Node.js >=24.9.0, and a DSH that provides webServer.readResource(), connection.browserPeer, and native authentication interfaces. The DSH version minimum declared in the manifest is >=0.2.1-rc.3, but the README also notes that older versions are not guaranteed to possess these APIs. The author has performed compatibility testing using extracted native modules from 0.2.2-rc.1, which does not equate to all versions or real-world Feishu end-to-end verification.
The installation command comes from the original README and needs to be executed inside the plugin source code directory:
git clone https://github.com/lloydzhou/dsh-feishu-oauth.git
cd dsh-feishu-oauth
npm ci
npm run build:client
dsh plugin --profile web add .
Step-by-step explanation:
npm ciinstalls dependencies based on the lockfile; esbuild is fixed version and locked by the lockfile.npm run build:clientcallsscripts/build-client.mjs, packingsrc/client/index.jsinto the browser artifactdist/client.js. The npm package only contains server-side source code, browser artifacts, language JSON, DSH patch, and documentation; it does not contain client source code, build scripts, or tests, so building is required before publishing.dsh plugin --profile web add .installs the plugin bundle and simultaneously applies thecordis.patch.ymllisted in the manifest.
After installation, three things still need to be done:
- Enable this plugin bundle on the Plugins page. Note that “Enable Feishu Login” in the configuration page is disabled by default; this is separate from the bundle’s master switch.
- Log in using the native token URL printed when starting DSH, then open Plugins → Installed Plugins → the configuration page for this plugin.
- In the Feishu Open Platform application, limit the available scope of trusted personnel, and register the exact callback address displayed on the card: the browser’s current origin plus
/auth/feishu/callback.
Key declarations in package.json:
{
"name": "dsh-feishu-oauth",
"version": "0.2.2-rc.2",
"description": "Feishu OAuth login for DeepSeek Harness using native session cookies",
"license": "MIT",
"engines": { "node": ">=24.9.0", "dsh": ">=0.2.1-rc.3" }
}
3. Configuration Options and Credential Handling¶
The current version provides only three configuration fields:
| Field | Default Value | Description |
|---|---|---|
enabled |
false |
Enable OAuth routes |
appId |
Empty | Feishu App ID |
appSecret |
Empty | Raw App Secret; masked when read, leaving the field blank means preserving the current value |
A few usage details worth knowing before getting started:
- Input is the original value of App ID and App Secret, not the environment variable name.
- First-time configuration must fill in the Secret; after that, leaving the password field blank means preserving the current value.
- The password field is always loaded blank and cleared after successful saving; the interface will not tell you if the Secret has been configured correctly.
- After successful saving or a configuration conflict, the form needs to be reloaded before editing.
There are two explicit security reminders in the README, emphasized with bold text:
- Masking and password masking do not equal encryption. The App Secret is stored in plaintext in the local profile configuration, so the configuration file and its backups need to be protected.
- Allowing HTTP access to DSH means that when the browser submits settings, the App Secret, native login credentials, and session traffic are not encrypted in transit. The Feishu provider requests always use HTTPS, which does not protect the HTTP link from the browser to DSH. The README suggests using native HTTPS and clarifies that deployments where “HTTPS is terminated at the reverse proxy and DSH still listens on HTTP” are not supported. The access address must also meet the native Connection trust rules, keeping the Host unchanged and consistent with the callback address registered with Feishu.
4. Login and Recovery Flow¶
Walk through the complete path:
- Open
/anonymously, see the login card, and click the authorization login button. - Redirect to the Feishu authorization endpoint, return to
/auth/feishu/callbackafter authorization. - The success callback uses a zero-latency meta refresh to return to the same-origin
/. At this point, the native Cookie has been written; the user sees the DSH interface, and the address bar remains/. - Subsequent requests are taken over by native authentication, and the plugin no longer intervenes.
Failure or recovery paths:
- Accessing
/index.htmlanonymously may result in a native 401, and should return to/to login. - Access with
?token=...is always handled by native authentication; this is the recovery entry. - When OAuth is disabled or credentials are missing, no login route is registered, and the login page will not appear.
When relevant configuration changes (enabling, disabling, changing credentials) occur, the plugin will unmount the OAuth route, cancel pending login requests, clean up, and remount, without affecting the native entry. Disabling or uninstalling the plugin also returns to the native entry.
A fixed masked warning is logged when settings integration captures an activation or cleanup exception; the README also reminds that not all missing or incorrect configurations will generate a warning, so do not rely solely on the presence or absence of a warning to judge the configuration status.
5. Security Measures and Known Limitations¶
The plugin imposes several limits in memory, but they must be interpreted correctly:
stateis single-use, bound to the browser, valid for 300 seconds, and retains a maximum of 256 pending states; a maximum of 16 provider exchange processes can be performed simultaneously.- These are single-process capacity limits, not rate limiting by IP, nor DoS protection. When deployed in a multi-process environment, these limits do not take effect globally.
stateis only saved in process memory; exiting the process or updating configuration will invalidate pending logins; a new login from the same browser will overwrite the old temporary binding.- Custom listeners bypassing Connection are not within the scope of protection.
Measures regarding protocol and transport:
- Uses S256 PKCE, provider address is fixed HTTPS, following provider redirects is disabled, and request timeouts and response size limits are set.
- Temporary Cookies are
HttpOnly/SameSite=Laxand includeSecureunder HTTPS. - Native startup tokens do not enter the page, do not enter browser redirects, are not sent to external HTTP requests, and are not written to logs; the plugin also does not read the signing key.
- Errors only contain fixed stages
TOKEN,USERINFO, orNATIVE_COOKIE, along with the received HTTP status codes and Feishu numeric error codes, without echoing the provider response body or credentials.
Meaning of two common error codes:
TOKEN/HTTP_400/CODE_20002indicates that Feishu rejected the application credentials.- Only the
TOKENstage appears with no HTTP status, indicating that the token interface response header was not received.
The troubleshooting method suggested in the README: after handling the issue, re-login from the homepage; do not refresh the callback page that has already consumed the authorization code.
6. Feishu Protocol Implementation Details¶
The plugin uses Authorization Code + PKCE and preserves the S256 challenge and verifier; it does not automatically downgrade and does not retry already used authorization codes.
- Authorization endpoint:
https://accounts.feishu.cn/open-apis/authen/v1/authorize - Token exchange:
https://open.feishu.cn/open-apis/authen/v2/oauth/token, exchanging via JSON request body according to the PKCE pairing instructions in the Feishu documentation. - Feishu error code
20049indicates PKCE verification failure.
One thing to note: The Feishu v2 token endpoint is officially marked as a legacy version, and the documentation provides migration instructions for v3. The README states that before migrating to v3, you need to verify the PKCE compatibility of the authorization endpoint, and you must confirm that the new endpoint still supports PKCE before upgrading Feishu-side capabilities.
7. Development and Verification¶
The plugin is published as an npm package. To modify and verify locally, you can run the following in order:
npm ci
npm run build:client
npm run check
npm test
npm pack --dry-run --ignore-scripts
npm run checkusesnode --checkto check server-side ESM source code,dist/client.js, client source code, and build scripts one by one.npm testexecutesnode --test test/*.test.js, running unit tests, as well as optional native module compatibility tests.npm pack --dry-run --ignore-scriptsis used to verify which files are actually included in the npm package.
Directory structure:
src/server/ Plugin entry, OAuth, login page, and settings lifecycle
src/client/ Configuration card and form logic source code
dist/client.js Browser bundle (generated and submitted)
scripts/ Client build scripts
test/ Unit tests and optional native module compatibility tests
locale/ Externally exported Chinese and English JSON
The files field in package.json lists the published content: src/server, dist/client.js, locale, cordis.patch.yml, README, LICENSE, SECURITY.md, SETTINGS-INTEGRATION.md, ASSET-ATTRIBUTION.md. Public exports remain as . , ./client, ./locale/*.json, and ./package.json. CI checks for differences after rebuilding browser artifacts.
Two points to note on the client side: The client depends on three packages, @deepseek-ai/dsh-client-locale, @deepseek-ai/dsh-client-ui-settings, and @deepseek-ai/dsh-client-ui-plugin-manager, injected via the manifest’s client.inject; the plugin’s changes to DSH are written in cordis.patch.yml, and the patch needs to be checked for applicability after upgrading DSH. The README explicitly states that mock tests and extracted module tests do not equal real browser or Feishu authorization round-trip acceptance, and verification should be redone after upgrading DSH.
Report security vulnerabilities privately via SECURITY.md; do not submit credentials in public issues.
8. Who it is for, and what to check before getting started¶
Suitable for deployments where DSH runs on an internal network or team environment, the users are a known and trusted group of members all within the same Feishu organization, and you want them to log in using their existing Feishu accounts instead of receiving separate account credentials for each person. At this point, using the available scope of the Feishu application for personnel filtering is the prerequisite for this solution and the only permission boundary.
Not suitable for scenarios:
- Require per-user permissions, auditing, or revocation capabilities. This solution gives all logins the same owner permissions; revocation can only be done by terminating personnel access on the Feishu side, and already issued DSH Cookies will not automatically expire.
- Require member isolation across multiple tenants or organizations. The plugin has no tenant concept.
- Have compliance requirements for transport encryption but can only provide an HTTP link from browser to DSH. The README suggests using native HTTPS and does not support deployments where “HTTPS is terminated at the reverse proxy and DSH still listens on HTTP.”
Three things recommended to do before getting started:
- Read the source code, especially
src/server/oauth.js,src/server/settings.js, andcordis.patch.yml, to confirm what the plugin changes and whether it exceeds the scope of “replacing the login entry”. The license is MIT; the license text is in the repository’s LICENSE file. - Confirm the DSH version meets
>=0.2.1-rc.3and verify that the required native APIs actually exist. The minimum declared by the manifest does not guarantee these capabilities for older versions. - Confirm that the callback address matches the Feishu application registration, and that the network exit allows the browser to access this precise address.
Conclusion¶
The value of dsh-feishu-oauth lies not in creating a complex account system, but in connecting DSH’s login entry to Feishu OAuth with the minimum of changes, leaving session state management to the native mechanism, requiring less to maintain yourself. Its cost is also clear: it does not provide per-user differentiated permissions, auditing, and revocation. Only by understanding this can you judge whether it is suitable for your deployment.
Related links:
- Community directory page: https://www.skillhub.cn/plugins/lloydzhou/dsh-feishu-oauth
- Source code and issue feedback: https://github.com/lloydzhou/dsh-feishu-oauth
- Security vulnerability report: SECURITY.md inside the repository