fix(login): persona click signs in directly + friendly error copy (#3)
build-and-publish / test (push) Has been cancelled
build-and-publish / image (push) Has been cancelled

This commit was merged in pull request #3.
This commit is contained in:
2026-06-14 21:26:01 +00:00
parent d68cb5b34e
commit 300024964f
3 changed files with 27 additions and 8 deletions
+1 -1
View File
@@ -56,7 +56,7 @@ describe("api client", () => {
]);
const r = await api.ping();
expect(r.ok).toBe(false);
expect(r.reason).toMatch(/401/);
expect(r.reason).toMatch(/rejected|401|sign-in/i);
});
it("workItems() returns the items array", async () => {
+11 -5
View File
@@ -19,6 +19,14 @@ const DEFAULT_CONFIG: ApiConfig = {
const TOKEN_KEY = "fm.mc.token.v1";
function friendlyAuthError(status: number, kind: string): string {
if (status === 502 || status === 504) return `${kind} unavailable: the EA2 backend is not reachable from this environment right now. Try again shortly.`;
if (status === 503) return `${kind} unavailable: the auth service is temporarily down. Try again shortly.`;
if (status === 404) return `${kind} is not wired to this environment yet.`;
if (status === 401 || status === 403) return `${kind} rejected: check the email and password.`;
return `${kind} failed (HTTP ${status}). Try again or contact the operator on call.`;
}
let inflightLogin: Promise<string> | null = null;
async function login(cfg: ApiConfig, signal?: AbortSignal): Promise<string> {
@@ -30,7 +38,7 @@ async function login(cfg: ApiConfig, signal?: AbortSignal): Promise<string> {
body: JSON.stringify({ email: cfg.email }),
signal,
});
if (!r.ok) throw new Error(`dev-login ${r.status}`);
if (!r.ok) throw new Error(friendlyAuthError(r.status, "Developer sign-in"));
const body = (await r.json()) as { access_token: string };
sessionStorage.setItem(TOKEN_KEY, body.access_token);
return body.access_token;
@@ -213,8 +221,7 @@ export const api = {
};
const r = await instrumentedFetch("POST", `${this.config.baseUrl}/api/v1/auth/login`, init, { email, password });
if (!r.ok) {
const text = await r.text().catch(() => "");
throw new Error(`Login failed: ${r.status} ${text.slice(0, 100)}`);
throw new Error(friendlyAuthError(r.status, "Sign-in"));
}
const data = await r.json() as { access_token: string; refresh_token?: string };
this.setBearer(data.access_token);
@@ -230,8 +237,7 @@ export const api = {
};
const r = await instrumentedFetch("POST", `${this.config.baseUrl}/api/v1/auth/dev-login`, init, { email });
if (!r.ok) {
const text = await r.text().catch(() => "");
throw new Error(`Dev-login failed: ${r.status} ${text.slice(0, 100)}`);
throw new Error(friendlyAuthError(r.status, "Developer sign-in"));
}
const data = await r.json() as { access_token: string; refresh_token?: string };
this.setBearer(data.access_token);