import { expect, test } from "@playwright/test"; // OpenWebUI (ai.franksdank.cloud) authenticates through authentik via the // OIDC authorization-code flow. With ONLY the standard authentik session, // visiting the vhost must show the "Continue with authentik" button, and // clicking it must complete the round-trip silently (no password prompt on // the IdP) and land in the logged-in app. test("SSO: OpenWebUI login completes through authentik silently", async ({ page }) => { test.setTimeout(180_000); await page.goto("https://ai.franksdank.cloud/"); // The app is a React SPA; the login page (no local session) renders the // OAuth button labelled after the provider name. const ssoButton = page.getByRole("button", { name: /continue with authentik/i }); await expect(ssoButton).toBeVisible({ timeout: 90_000 }); // The round-trip: ai -> auth (silent, the session is already there) -> // back to ai with the code. No authentik password form may appear. await ssoButton.click(); await expect .poll( async () => { const body = (await page.locator("body").innerText()).toLowerCase(); return !body.includes("continue with authentik"); }, { timeout: 90_000 }, ) .toBe(true); await expect .poll(() => new URL(page.url()).hostname, { timeout: 60_000 }) .toBe("ai.franksdank.cloud"); const body = await page.locator("body").innerText(); expect(body.toLowerCase()).not.toContain("powered by authentik"); // IdP flow shell expect(body.toLowerCase()).not.toContain("password"); // no credential prompt });