From 0fcff1f5204c628c3dc15df590cf3d582208d74a Mon Sep 17 00:00:00 2001 From: Daniel Noulet Date: Mon, 5 Oct 2026 23:07:06 +0200 Subject: [PATCH] fix: fall back to the password form when a tenant has no Go Loose credentials Deploying to the development cluster showed a case the tests had not: the API answers 503, not 404, when a host is a known tenant whose client credentials have not been supplied. Only 404 was treated as "no Go Loose login", so the sign-in screen offered the button, and following it dead-ended on an error page with no way for the person to sign in at all. Both answers mean the same thing to the browser, so both now select the password form. A successful answer still short-circuits as the single sign-on case. --- .../src/components/actions/Account.test.tsx | 16 ++++++++++++++ frontend/src/components/actions/Account.tsx | 22 +++++++++++++------ 2 files changed, 31 insertions(+), 7 deletions(-) diff --git a/frontend/src/components/actions/Account.test.tsx b/frontend/src/components/actions/Account.test.tsx index 7ae6588..7cf5b74 100644 --- a/frontend/src/components/actions/Account.test.tsx +++ b/frontend/src/components/actions/Account.test.tsx @@ -128,6 +128,22 @@ describe('useAuthSession Go Loose bootstrap', () => { expect(location.assign).not.toHaveBeenCalled() }) + it('falls back to password sign-in when the tenant has no client credentials', async () => { + // A 503 is what a configured tenant without Go Loose credentials returns. It is + // just as unusable as a 404, and treating it as "available" left the sign-in + // button leading to an error page with no way in. + const location = setHost('nmbs.guess-dev.urpi.be') + vi.stubGlobal( + 'fetch', + vi.fn(() => json({ error: 'Go Loose login is not configured for tenant "nmbs"' }, 503)), + ) + + renderProbe() + + await waitFor(() => expect(screen.getByText('false|false')).toBeInTheDocument()) + expect(location.assign).not.toHaveBeenCalled() + }) + it('falls back to password sign-in when the API reports no Go Loose login', async () => { const location = setHost('nmbs.guess-dev.urpi.be') vi.stubGlobal( diff --git a/frontend/src/components/actions/Account.tsx b/frontend/src/components/actions/Account.tsx index 63e685c..f080801 100644 --- a/frontend/src/components/actions/Account.tsx +++ b/frontend/src/components/actions/Account.tsx @@ -68,17 +68,25 @@ export function useAuthSession() { redirect: 'manual', }) if (cancelled) return - if (response.status === 404) { - // Go Loose login is not configured for this host: sign in with a password. + if (response.ok) { + // An existing Go Loose session. This is also the single sign-on case: + // having signed in anywhere in the family, the person lands here + // already authenticated. + setSsoAvailable(true) + authStorage.save(await response.json()) + setAuthenticated(true) + setSessionExpired(false) + return + } + if (response.status === 404 || response.status === 503) { + // Either Go Loose login is not set up for this host at all (404), or the + // host is a known tenant whose client credentials have not been supplied + // (503). Neither can produce a sign-in, so offering the button would + // dead-end on an error page instead of letting the person in. setSsoAvailable(false) return } setSsoAvailable(true) - if (response.ok) { - authStorage.save(await response.json()) - setAuthenticated(true) - setSessionExpired(false) - } } catch { // An unreachable API leaves ssoAvailable null, which keeps the single // sign-on button on screen: it is the only way in on such a host anyway.