Skip to content

Commit 633c98a

Browse files
suryaiyer95claude
andcommitted
feat: validate credentials via /auth_health before saving
Call `GET <url>/auth_health` with `Authorization` and `x-tenant` headers after the user completes the 3-step login wizard. Only persist credentials to `~/.altimate/altimate.json` if the server returns `{"status": "auth_valid"}`. Shows inline error on the URL step if validation fails. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 95a69ac commit 633c98a

1 file changed

Lines changed: 42 additions & 4 deletions

File tree

packages/opencode/src/cli/cmd/tui/component/dialog-altimate-login.tsx

Lines changed: 42 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { createSignal } from "solid-js"
1+
import { createSignal, Show } from "solid-js"
22
import { useDialog } from "@tui/ui/dialog"
33
import { useSDK } from "../context/sdk"
44
import { useSync } from "@tui/context/sync"
@@ -17,8 +17,38 @@ export function DialogAltimateLogin() {
1717
const [step, setStep] = createSignal<"instance" | "key" | "url">("instance")
1818
const [instanceName, setInstanceName] = createSignal("")
1919
const [apiKey, setApiKey] = createSignal("")
20+
const [error, setError] = createSignal("")
21+
const [validating, setValidating] = createSignal(false)
22+
23+
async function validateAndSave(url: string) {
24+
setError("")
25+
setValidating(true)
26+
try {
27+
const res = await fetch(`${url}/auth_health`, {
28+
method: "GET",
29+
headers: {
30+
Authorization: `Bearer ${apiKey()}`,
31+
"x-tenant": instanceName(),
32+
},
33+
})
34+
if (!res.ok) {
35+
setError("Invalid credentials — check your instance name, API key, and URL")
36+
setValidating(false)
37+
return
38+
}
39+
const data = await res.json()
40+
if (data.status !== "auth_valid") {
41+
setError("Unexpected response from server")
42+
setValidating(false)
43+
return
44+
}
45+
} catch (e) {
46+
setError(`Connection failed — could not reach ${url}`)
47+
setValidating(false)
48+
return
49+
}
50+
setValidating(false)
2051

21-
async function saveAndConnect(url: string) {
2252
const creds = {
2353
altimateUrl: url,
2454
altimateInstanceName: instanceName(),
@@ -67,11 +97,19 @@ export function DialogAltimateLogin() {
6797
placeholder="https://api.myaltimate.com"
6898
value="https://api.myaltimate.com"
6999
description={() => (
70-
<text fg={theme.textMuted}>Enter your Altimate server URL</text>
100+
<box gap={1}>
101+
<text fg={theme.textMuted}>Enter your Altimate server URL</text>
102+
<Show when={validating()}>
103+
<text fg={theme.textMuted}>Validating credentials...</text>
104+
</Show>
105+
<Show when={error()}>
106+
<text fg={theme.error}>{error()}</text>
107+
</Show>
108+
</box>
71109
)}
72110
onConfirm={async (value) => {
73111
if (!value) return
74-
await saveAndConnect(value)
112+
await validateAndSave(value)
75113
}}
76114
/>
77115
)}

0 commit comments

Comments
 (0)