summaryrefslogtreecommitdiffhomepage
path: root/packages/app/src/utils
diff options
context:
space:
mode:
Diffstat (limited to 'packages/app/src/utils')
-rw-r--r--packages/app/src/utils/server.test.ts23
-rw-r--r--packages/app/src/utils/server.ts18
-rw-r--r--packages/app/src/utils/terminal-websocket-url.test.ts18
-rw-r--r--packages/app/src/utils/terminal-websocket-url.ts10
4 files changed, 65 insertions, 4 deletions
diff --git a/packages/app/src/utils/server.test.ts b/packages/app/src/utils/server.test.ts
new file mode 100644
index 000000000..4666b7d6d
--- /dev/null
+++ b/packages/app/src/utils/server.test.ts
@@ -0,0 +1,23 @@
+import { describe, expect, test } from "bun:test"
+import { authFromToken, authTokenFromCredentials } from "./server"
+
+describe("authFromToken", () => {
+ test("decodes basic auth credentials from auth_token", () => {
+ expect(authFromToken(btoa("kit:secret"))).toEqual({ username: "kit", password: "secret" })
+ })
+
+ test("defaults blank username to opencode", () => {
+ expect(authFromToken(btoa(":secret"))).toEqual({ username: "opencode", password: "secret" })
+ })
+
+ test("ignores malformed tokens", () => {
+ expect(authFromToken("not base64")).toBeUndefined()
+ expect(authFromToken(btoa("missing-separator"))).toBeUndefined()
+ })
+})
+
+describe("authTokenFromCredentials", () => {
+ test("encodes credentials with the default username", () => {
+ expect(authTokenFromCredentials({ password: "secret" })).toBe(btoa("opencode:secret"))
+ })
+})
diff --git a/packages/app/src/utils/server.ts b/packages/app/src/utils/server.ts
index ae849b71e..603784e4d 100644
--- a/packages/app/src/utils/server.ts
+++ b/packages/app/src/utils/server.ts
@@ -1,5 +1,21 @@
import { createOpencodeClient } from "@opencode-ai/sdk/v2/client"
import type { ServerConnection } from "@/context/server"
+import { decode64 } from "@/utils/base64"
+
+export function authTokenFromCredentials(input: { username?: string; password: string }) {
+ return btoa(`${input.username ?? "opencode"}:${input.password}`)
+}
+
+export function authFromToken(token: string | null) {
+ const decoded = decode64(token ?? undefined)
+ if (!decoded) return
+ const separator = decoded.indexOf(":")
+ if (separator === -1) return
+ return {
+ username: decoded.slice(0, separator) || "opencode",
+ password: decoded.slice(separator + 1),
+ }
+}
export function createSdkForServer({
server,
@@ -10,7 +26,7 @@ export function createSdkForServer({
const auth = (() => {
if (!server.password) return
return {
- Authorization: `Basic ${btoa(`${server.username ?? "opencode"}:${server.password}`)}`,
+ Authorization: `Basic ${authTokenFromCredentials({ username: server.username, password: server.password })}`,
}
})()
diff --git a/packages/app/src/utils/terminal-websocket-url.test.ts b/packages/app/src/utils/terminal-websocket-url.test.ts
index c85863abd..5fa1506b1 100644
--- a/packages/app/src/utils/terminal-websocket-url.test.ts
+++ b/packages/app/src/utils/terminal-websocket-url.test.ts
@@ -19,7 +19,7 @@ describe("terminalWebSocketURL", () => {
expect(url.searchParams.get("auth_token")).toBe(btoa("opencode:secret"))
})
- test("omits query auth for same-origin websocket URL", () => {
+ test("omits query auth for same-origin saved credentials", () => {
const url = terminalWebSocketURL({
url: "https://app.example.test",
id: "pty_test",
@@ -33,4 +33,20 @@ describe("terminalWebSocketURL", () => {
expect(url.protocol).toBe("wss:")
expect(url.searchParams.has("auth_token")).toBe(false)
})
+
+ test("uses query auth for same-origin credentials from auth_token", () => {
+ const url = terminalWebSocketURL({
+ url: "https://app.example.test",
+ id: "pty_test",
+ directory: "/tmp/project",
+ cursor: 10,
+ sameOrigin: true,
+ username: "opencode",
+ password: "secret",
+ authToken: true,
+ })
+
+ expect(url.protocol).toBe("wss:")
+ expect(url.searchParams.get("auth_token")).toBe(btoa("opencode:secret"))
+ })
})
diff --git a/packages/app/src/utils/terminal-websocket-url.ts b/packages/app/src/utils/terminal-websocket-url.ts
index d364762d7..c1c7abad4 100644
--- a/packages/app/src/utils/terminal-websocket-url.ts
+++ b/packages/app/src/utils/terminal-websocket-url.ts
@@ -1,3 +1,5 @@
+import { authTokenFromCredentials } from "@/utils/server"
+
export function terminalWebSocketURL(input: {
url: string
id: string
@@ -6,12 +8,16 @@ export function terminalWebSocketURL(input: {
sameOrigin: boolean
username: string
password?: string
+ authToken?: boolean
}) {
const next = new URL(`${input.url}/pty/${input.id}/connect`)
next.searchParams.set("directory", input.directory)
next.searchParams.set("cursor", String(input.cursor))
next.protocol = next.protocol === "https:" ? "wss:" : "ws:"
- if (!input.sameOrigin && input.password)
- next.searchParams.set("auth_token", btoa(`${input.username}:${input.password}`))
+ if (input.password && (!input.sameOrigin || input.authToken))
+ next.searchParams.set(
+ "auth_token",
+ authTokenFromCredentials({ username: input.username, password: input.password }),
+ )
return next
}