Skip to content

Commit e759115

Browse files
authored
Avoid auth prompt for forbidden settings responses (#563)
## Summary - Treat only `401 Unauthorized` from settings load as a token-auth challenge. - Surface `403 Forbidden` as a settings error instead of showing a misleading auth-token prompt. - Add settings-store regression coverage for 401 vs 403 handling. Related to #562 ## Test Plan - [x] `npm test` - [x] `npm run check`
1 parent 458c7f9 commit e759115

2 files changed

Lines changed: 63 additions & 2 deletions

File tree

frontend/src/lib/stores/settings.svelte.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ class SettingsStore {
2020
loading: boolean = $state(false);
2121
saving: boolean = $state(false);
2222
error: string | null = $state(null);
23-
/** True when the API returned 401/403, indicating the user needs
23+
/** True when the API returned 401, indicating the user needs
2424
* to provide an auth token before the app can load. */
2525
needsAuth: boolean = $state(false);
2626

@@ -44,7 +44,7 @@ class SettingsStore {
4444
setAuthToken(data.auth_token);
4545
}
4646
} catch (e) {
47-
if (e instanceof ApiError && (e.status === 401 || e.status === 403)) {
47+
if (e instanceof ApiError && e.status === 401) {
4848
this.needsAuth = true;
4949
} else {
5050
this.error =
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import {
2+
beforeEach,
3+
describe,
4+
expect,
5+
it,
6+
vi,
7+
} from "vitest";
8+
import { settings } from "./settings.svelte.js";
9+
import * as api from "../api/client.js";
10+
import { ApiError } from "../api/client.js";
11+
12+
vi.mock("../api/client.js", async (importOriginal) => {
13+
const orig =
14+
await importOriginal<typeof import("../api/client.js")>();
15+
return {
16+
...orig,
17+
getSettings: vi.fn(),
18+
updateSettings: vi.fn(),
19+
setAuthToken: vi.fn(),
20+
isRemoteConnection: vi.fn(),
21+
};
22+
});
23+
24+
beforeEach(() => {
25+
vi.clearAllMocks();
26+
settings.agentDirs = {};
27+
settings.githubConfigured = false;
28+
settings.terminal = { mode: "auto" };
29+
settings.host = "";
30+
settings.port = 0;
31+
settings.authToken = "";
32+
settings.requireAuth = false;
33+
settings.loading = false;
34+
settings.saving = false;
35+
settings.error = null;
36+
settings.needsAuth = false;
37+
});
38+
39+
describe("SettingsStore.load auth handling", () => {
40+
it("prompts for a token on 401 responses", async () => {
41+
vi.mocked(api.getSettings).mockRejectedValue(
42+
new ApiError(401, "Unauthorized"),
43+
);
44+
45+
await settings.load();
46+
47+
expect(settings.needsAuth).toBe(true);
48+
expect(settings.error).toBeNull();
49+
});
50+
51+
it("does not prompt for a token on non-auth 403 responses", async () => {
52+
vi.mocked(api.getSettings).mockRejectedValue(
53+
new ApiError(403, "Forbidden"),
54+
);
55+
56+
await settings.load();
57+
58+
expect(settings.needsAuth).toBe(false);
59+
expect(settings.error).toBe("Forbidden");
60+
});
61+
});

0 commit comments

Comments
 (0)