diff --git a/backend/src/auth/routes.ts b/backend/src/auth/routes.ts index 90cabfe4..c58e17e5 100644 --- a/backend/src/auth/routes.ts +++ b/backend/src/auth/routes.ts @@ -175,7 +175,12 @@ export function registerAuthRoutes(app: FastifyInstance, deps: AuthRouteDependen app.get("/auth/config", async (request, reply) => { const snapshot = captureAuthConfigSnapshot(request, deps.authentication); - if (!snapshot) return unavailable(reply); + if (!snapshot) { + if (deps.authMode === "upstream") { + return reply.send({ mode: "upstream", localLogin: false, oidcLogin: false }); + } + return unavailable(reply); + } const mode = snapshot.value.mode; return reply.send({ mode, localLogin: mode === "local", oidcLogin: mode === "oidc" }); }); diff --git a/backend/test/app-auth-mode.test.ts b/backend/test/app-auth-mode.test.ts index 7b4515dd..95933669 100644 --- a/backend/test/app-auth-mode.test.ts +++ b/backend/test/app-auth-mode.test.ts @@ -88,3 +88,14 @@ test("configured OIDC initializes login from the literal secret bundle without a rmSync(directory, { recursive: true, force: true }); } }); + +test("upstream mode advertises its public authentication contract without a local auth provider", async () => { + const app = buildApp(loadConfig({ NODE_ENV: "production", AUTH_MODE: "upstream", THOTH_PUBLIC_EXPOSURE: "false" })); + try { + const response = await app.inject({ method: "GET", url: "/auth/config" }); + expect(response.statusCode).toBe(200); + expect(response.json()).toEqual({ mode: "upstream", localLogin: false, oidcLogin: false }); + } finally { + await app.close(); + } +});