Skip to content

fix(opencode): sync auth client hotfixes#487

Merged
Astro-Han merged 7 commits into
devfrom
codex/i477-auth-client-hotfix
May 7, 2026
Merged

fix(opencode): sync auth client hotfixes#487
Astro-Han merged 7 commits into
devfrom
codex/i477-auth-client-hotfix

Conversation

@Astro-Han

@Astro-Han Astro-Han commented May 7, 2026

Copy link
Copy Markdown
Owner

Summary

  • Port the [Task] Track upstream sync after opencode 17701628bd #477 PR Slice 2 auth/client credential hotfixes from upstream opencode.
  • Centralize server auth header construction and reuse it from CLI, ACP, plugin clients, and attach flows.
  • Surface readable SDK/CLI errors for empty error bodies and preserve app startup auth_token credentials.

Why

#477 tracks PawWork's upstream opencode sync after 17701628bd. PR #482 already landed the provider/model slice. This PR implements the next bounded auth/client slice while excluding provider/model, runtime/session/tool safety, desktop packaging, HttpApi listener, generated SDK, and dependency changes.

Related Issue

Refs #477

Human Review Status

Pending. A human should make the final merge decision after reviewing the final diff and verification evidence.

Review Focus

  • ServerAuth helper behavior for passwordless mode, default username, configured username, and explicit attach credentials.
  • Empty SDK/server error wrapping without changing parsed JSON error bodies.
  • App startup auth_token override behavior when persisted server state already has the same URL.
  • URL cleanup after auth_token capture, preserving unrelated query params and hash.
  • Terminal websocket query auth behavior for same-origin credentials that came from auth_token.

Risk Notes

  • Credential handling touched intentionally: auth_token is captured into startup server credentials, then removed from the visible URL/history.
  • Upstream files under packages/opencode/src/cli/cmd/tui/* and HttpApi listener paths were behavior references only because PawWork does not have matching local paths.
  • No provider/model, runtime/session/tool safety, desktop packaging, HttpApi listener migration, generated SDK, dependency, or lockfile files were changed.

How To Verify

bun test test/server/auth.test.ts test/util/error.test.ts: 9 passed
bun test --preload ./happydom.ts src/utils/server.test.ts src/context/server.test.ts src/utils/terminal-websocket-url.test.ts: 9 passed
bun run typecheck in packages/opencode: passed
bun run typecheck in packages/app: passed
bun run typecheck in packages/sdk/js: passed
git diff --check: no whitespace errors
Diff boundary check: no provider/model, desktop, HttpApi, generated SDK, dependency, lockfile, or workflow files changed
Manual/code-review verification: auth_token is removed from browser history after capture while unrelated query params and hash remain
Manual/code-review verification: run --attach --username passes explicit username into the Basic auth header helper
Manual/code-review verification: SDK empty-error wrapping changes only empty generated-client error payloads and passes parsed JSON errors through unchanged

Screenshots or Recordings

Not applicable. This is credential handling and client auth behavior, with no visible UI change.

Checklist

  • Human review status is stated above as pending, approved, or not required
  • I linked the related issue, or stated why there is no issue
  • This PR has type, primary area, and priority labels, or I requested maintainer labeling
  • I described the review focus and any meaningful risks
  • I listed the relevant verification steps and the key result for each
  • I did not introduce unrelated refactors, dependencies, generated files, or file changes beyond the stated scope
  • I manually checked visible UI or copy changes when needed, with screenshots or recordings
  • I considered macOS and Windows impact for platform, packaging, updater, signing, paths, shell, or permissions changes
  • I called out docs, release notes, dependencies, permissions, credentials, deletion behavior, generated content, or local file changes when relevant
  • I reviewed the final diff for unrelated changes and suspicious dependency changes
  • I am targeting dev, and my PR title and commit messages use Conventional Commits in English

Summary by CodeRabbit

  • New Features

    • Added support for URL-based authentication tokens with automatic cleanup after use.
    • Enhanced server credential management with persistent connection support across sessions.
    • Improved WebSocket connection authentication for cross-origin scenarios.
  • Improvements

    • Centralized authentication header generation for consistency across the platform.
    • Enhanced error formatting and messaging for better debugging.
  • Tests

    • Added comprehensive authentication and server credential validation tests.

@coderabbitai

coderabbitai Bot commented May 7, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Rate limit exceeded

@Astro-Han has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 27 minutes and 18 seconds before requesting another review.

To continue reviewing without waiting, purchase usage credits in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 23cb1158-e84b-42a4-af36-0391b186774b

📥 Commits

Reviewing files that changed from the base of the PR and between b3aedf7 and 660f89d.

📒 Files selected for processing (4)
  • packages/app/src/context/server.test.ts
  • packages/app/src/context/server.tsx
  • packages/opencode/src/server/middleware.ts
  • packages/opencode/test/server/auth.test.ts
📝 Walkthrough

Walkthrough

This PR consolidates server authentication across the app, CLI, and SDK layers. It introduces centralized authentication utilities for token encoding/decoding, exports a new server connection resolution function, implements a custom server middleware for Basic Auth validation, and integrates auth headers throughout CLI commands and the plugin system.

Changes

Centralized Authentication System

Layer / File(s) Summary
Type Contracts
packages/app/src/context/server.tsx, packages/opencode/src/server/auth.ts
StoredServer type exported; ServerConnection.Http extended with optional authToken field; new auth types Credentials, ConfigInfo, DecodedCredentials defined.
Token Codec Utilities
packages/app/src/utils/server.ts
New authTokenFromCredentials(...) encodes username/password to base64; new authFromToken(...) decodes and parses tokens; createSdkForServer uses the encoder for Authorization headers.
Server Auth Module
packages/opencode/src/server/auth.ts
New required(config) checks password presence; authorized(credentials, config) validates username and password; header(...) and headers(...) build Basic Auth header strings with flag fallbacks.
Server Middleware
packages/opencode/src/server/middleware.ts
AuthMiddleware rewritten from Hono basicAuth to async manual validation: handles CORS preflight, forwards auth_token query param to header, decodes and validates credentials via ServerAuth.authorized(), returns 401 on mismatch.
Server List Resolution
packages/app/src/context/server.tsx
New resolveServerList(...) merges persisted and startup server configs, deduplicates by connection key, prefers authToken entries; context init uses it instead of inline logic; stored HTTP connections clear authToken: undefined before persistence.
Entry Point Token Handling
packages/app/src/entry.tsx
Reads auth_token from page URL via authFromToken, constructs ServerConnection.Http with authToken flag and merged credentials, clears token from URL via history.replaceState.
Terminal WebSocket URL
packages/app/src/utils/terminal-websocket-url.ts, packages/app/src/components/terminal.tsx
New terminalWebSocketURL(...) builds PTY connection URLs with protocol conversion (HTTPS↔WSS), query params (directory, cursor), and conditional auth_token based on sameOrigin and authToken flags; Terminal component uses it instead of manual URL construction.
CLI & Plugin Integration
packages/opencode/src/cli/cmd/acp.ts, packages/opencode/src/cli/cmd/run.ts, packages/opencode/src/plugin/index.ts
ACP and plugin use ServerAuth.headers(); run command adds --username option and uses ServerAuth.headers(...) instead of inline header builder; providers command routes stderr to inherit for auth spawned processes.
Error Handling
packages/opencode/src/util/error.ts, packages/sdk/js/src/v2/client.ts
errorFormat enhanced to detect empty objects and use constructor name/property fallback; errorMessage and errorData consolidated to use errorFormat; SDK adds error interceptor for structured error translation from request failures.
Test Coverage
packages/app/src/context/server.test.ts, packages/app/src/utils/server.test.ts, packages/app/src/utils/terminal-websocket-url.test.ts, packages/opencode/test/server/auth.test.ts, packages/opencode/test/util/error.test.ts
Tests for resolveServerList deduplication and authToken override; token codec encode/decode and default username handling; terminalWebSocketURL query parameter logic per sameOrigin/authToken conditions; ServerAuth header generation with flag and explicit credential fallbacks; opaque error object formatting.

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested labels

authentication, refactor, centralization, middleware, app, cli

🐰 From hops through the code to tokens in flight,
Auth flows now dance with centralized might—
No headers scattered, no secrets unclear,
Just helpers and middleware, clean and sincere!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The PR title 'fix(opencode): sync auth client hotfixes' directly reflects the main objective of porting auth/client credential hotfixes from upstream opencode, using Conventional Commits format.
Description check ✅ Passed The PR description comprehensively covers all required template sections: Summary, Why, Related Issue, Human Review Status, Review Focus, Risk Notes, How To Verify, Screenshots/Recordings (marked not applicable), and a completed Checklist with all boxes checked.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/i477-auth-client-hotfix

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@Astro-Han Astro-Han added bug Something isn't working upstream Tracked upstream or vendor behavior app Application behavior and product flows P1 High priority labels May 7, 2026

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request centralizes server authentication logic, introducing utilities for encoding/decoding auth tokens and managing basic auth headers across the CLI, plugins, and web app. Key changes include refactoring terminal WebSocket URL generation, handling auth_token query parameters in the server middleware, and improving error formatting for opaque objects. Review feedback identifies a critical issue where mutating request headers in the middleware will cause a TypeError in Fetch-based runtimes like Bun. A more robust implementation using regex for credential extraction and avoiding direct header mutation was suggested.

Comment thread packages/opencode/src/server/middleware.ts Outdated
Comment thread packages/opencode/src/server/middleware.ts Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
packages/opencode/src/util/error.ts (1)

41-43: 💤 Low value

return "unknown error" is essentially unreachable.

errorFormat only returns an empty string for errorFormat("") (a bare empty string as the thrown value). For every object and non-object input reachable here, formatted is always truthy, making the "unknown error" fallback dead code in all practical paths. Not a functional bug — the guard is strictly safer than the previous unconditional return errorFormat(error) — but the comment that it guards against a real case is misleading if anyone reads this later.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/opencode/src/util/error.ts` around lines 41 - 43, The fallback
return "unknown error" after calling errorFormat is effectively unreachable and
misleading; replace the ternary-style guard with a direct return of the
formatted value (i.e., return formatted) or, if you intended to treat a bare
empty-string throw specially, add an explicit check for that case (e.g., if
(formatted === "") handle empty-string case) so the logic in errorFormat and the
variable formatted are consistent and the dead-code fallback is removed.
packages/app/src/utils/server.test.ts (1)

19-23: 💤 Low value

Consider adding test for explicit username and password with colons.

The test suite covers the default username case but misses testing explicit username encoding. Also, passwords containing colons are valid and should be handled correctly by authFromToken (the colon separator uses indexOf which finds the first colon, so this should work).

📝 Suggested additional tests
 describe("authTokenFromCredentials", () => {
   test("encodes credentials with the default username", () => {
     expect(authTokenFromCredentials({ password: "secret" })).toBe(btoa("opencode:secret"))
   })
+
+  test("encodes credentials with explicit username", () => {
+    expect(authTokenFromCredentials({ username: "kit", password: "secret" })).toBe(btoa("kit:secret"))
+  })
 })
+
+describe("authFromToken edge cases", () => {
+  test("handles password containing colons", () => {
+    expect(authFromToken(btoa("user:pass:with:colons"))).toEqual({
+      username: "user",
+      password: "pass:with:colons",
+    })
+  })
+})
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/app/src/utils/server.test.ts` around lines 19 - 23, Add tests that
pass an explicit username to authTokenFromCredentials and verify it encodes
using btoa("username:password"), and add a test where the password contains one
or more colons to ensure authFromToken correctly decodes using the first colon
(it uses indexOf) — specifically add assertions calling
authTokenFromCredentials({ username: "alice", password: "p:ass:word" }) and then
decode with authFromToken(token) (or compare to btoa("alice:p:ass:word")) to
confirm the round-trip; reference authTokenFromCredentials and authFromToken to
locate the functions to test.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/opencode/src/server/middleware.ts`:
- Line 50: The auth-scheme check in middleware.ts uses header?.startsWith("Basic
") which rejects case-insensitive variants; update the conditional that checks
the header (the variable header in the middleware) to perform a case-insensitive
match (e.g., compare header.toLowerCase().startsWith("basic ") or use a
case-insensitive regex like /^basic /i) so valid "basic" tokens (e.g., "basic",
"BASIC") are accepted and still return c.text("Unauthorized", 401) when missing
or not a Basic auth header.

---

Nitpick comments:
In `@packages/app/src/utils/server.test.ts`:
- Around line 19-23: Add tests that pass an explicit username to
authTokenFromCredentials and verify it encodes using btoa("username:password"),
and add a test where the password contains one or more colons to ensure
authFromToken correctly decodes using the first colon (it uses indexOf) —
specifically add assertions calling authTokenFromCredentials({ username:
"alice", password: "p:ass:word" }) and then decode with authFromToken(token) (or
compare to btoa("alice:p:ass:word")) to confirm the round-trip; reference
authTokenFromCredentials and authFromToken to locate the functions to test.

In `@packages/opencode/src/util/error.ts`:
- Around line 41-43: The fallback return "unknown error" after calling
errorFormat is effectively unreachable and misleading; replace the ternary-style
guard with a direct return of the formatted value (i.e., return formatted) or,
if you intended to treat a bare empty-string throw specially, add an explicit
check for that case (e.g., if (formatted === "") handle empty-string case) so
the logic in errorFormat and the variable formatted are consistent and the
dead-code fallback is removed.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: e7bdfcb9-65e9-4381-bdd6-6d7e3c6636a5

📥 Commits

Reviewing files that changed from the base of the PR and between d216ec0 and b3aedf7.

📒 Files selected for processing (18)
  • packages/app/src/components/terminal.tsx
  • packages/app/src/context/server.test.ts
  • packages/app/src/context/server.tsx
  • packages/app/src/entry.tsx
  • packages/app/src/utils/server.test.ts
  • packages/app/src/utils/server.ts
  • packages/app/src/utils/terminal-websocket-url.test.ts
  • packages/app/src/utils/terminal-websocket-url.ts
  • packages/opencode/src/cli/cmd/acp.ts
  • packages/opencode/src/cli/cmd/providers.ts
  • packages/opencode/src/cli/cmd/run.ts
  • packages/opencode/src/plugin/index.ts
  • packages/opencode/src/server/auth.ts
  • packages/opencode/src/server/middleware.ts
  • packages/opencode/src/util/error.ts
  • packages/opencode/test/server/auth.test.ts
  • packages/opencode/test/util/error.test.ts
  • packages/sdk/js/src/v2/client.ts

Comment thread packages/opencode/src/server/middleware.ts Outdated
@Astro-Han Astro-Han merged commit 57e96b5 into dev May 7, 2026
20 checks passed
@Astro-Han Astro-Han deleted the codex/i477-auth-client-hotfix branch May 7, 2026 09:26
Astro-Han added a commit that referenced this pull request May 7, 2026
Root cause:
- SessionProcessor correctly attached harness-owned diagnostics.loop to tool parts when tool-call events arrived.
- Bash then streamed metadata updates through Tool.Context.metadata while the part was still running.
- SessionPrompt replaced state.metadata with the tool-provided metadata object, so those Bash output/description updates erased diagnostics.loop before completion.
- Because completed successful Bash parts no longer carried loop diagnostics, the existing successful exact-input loop strategy had no records to count, so Kimi K2.6 could repeat the same successful Bash command many times without reminder, block, or stop.

User impact:
- In issue #489, the review session repeated the same git diff command 39 times.
- The expected gate sequence was reminder at the 3rd repeat, synthetic block at the 4th, and synthetic stop with a visible summary at the 5th.

Fix:
- Preserve harness-owned metadata in SessionPrompt's Tool.Context.metadata update path.
- Reuse SessionDiagnostics.mergeMetadata so tool-owned fields such as Bash output, exit, description, truncated, and outputPath can still update while diagnostics.loop and diagnostics.failure survive.
- Keep the fix at the shared session/tool metadata boundary instead of hard-coding loop diagnostics into Bash.

Scope intentionally not changed:
- No Bash permission, shell parsing, timeout, truncation, or process execution changes.
- No loop threshold changes: reminder remains 3, block remains 4, stop remains 5.
- No PR #487 auth/client code changes.
- No metadata API migration or broader diagnostics refactor.

Regression coverage:
- Added a prompt-effect regression covering repeated successful Bash calls where Bash updates metadata during execution.
- The test asserts the 3/4/5 reminder-block-stop behavior and verifies completed Bash parts retain both diagnostics.loop hashes and Bash metadata.

Verification:
- RED before fix: the new regression failed with 5 completed Bash calls instead of the expected 3, proving the old gate did not stop the loop.
- GREEN after fix: the focused regression passed with 1 pass and 27 assertions.
- Focused suite passed: bun test test/session/prompt-effect.test.ts test/session/diagnostics.test.ts test/session/loop-gate.test.ts test/tool/bash.test.ts, 129 pass, 0 fail, 388 assertions.
- Typecheck passed: bun run typecheck from packages/opencode.
- Diff whitespace check passed: git diff --check.
- PR CI passed on GitHub before merge, including typecheck, unit-app, unit-opencode, unit-desktop, desktop-smoke, e2e-artifacts, CodeQL, dependency-review, commit-lint, and PR title checks.

Closes #489.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

app Application behavior and product flows bug Something isn't working P1 High priority upstream Tracked upstream or vendor behavior

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant