test(e2e): cover Grafana relying-party SSO landing (spec 20)
13-sso proves an authenticated session reaches Grafana login page but never follows the OAuth button, so the relying-party login could be dead (500, or an email-claim gap bouncing to /login) while the suite stayed green. Spec 20 drives /login/generic_oauth through the real Anubis+authentik round-trip and asserts a live session (/api/user 200 with a non-empty email). Wire 20- into the standard project (qa-e2e session).
This commit is contained in:
@@ -29,8 +29,10 @@ export default defineConfig({
|
||||
// green suite with a test that does not exist. 17- joined on 2026-09-16
|
||||
// (the hermes vhost, whose upstream is a host address rather than a
|
||||
// container name, so it needs its own pass condition). 18- joined on
|
||||
// 2026-09-24 (OpenWebUI SSO through authentik).
|
||||
testMatch: /(10|12|13|16|17|18)-.*\.spec\.ts/,
|
||||
// 2026-09-24 (OpenWebUI SSO through authentik). 20- joined on 2026-10-21
|
||||
// (Grafana's OAuth2 relying-party login, which the edge-only 13- never
|
||||
// follows).
|
||||
testMatch: /(10|12|13|16|17|18|20)-.*\.spec\.ts/,
|
||||
use: { storageState: ".auth/qa-e2e.json" },
|
||||
dependencies: ["setup"],
|
||||
},
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
import { expect, test } from "@playwright/test";
|
||||
|
||||
// Grafana (grafana.franksdank.cloud) is an OAuth2 RELYING PARTY, not an
|
||||
// outpost-fronted proxy: the guard chain (13-sso) only proves an authenticated
|
||||
// session reaches Grafana's login page. The login itself happens through
|
||||
// Grafana's own "Sign in with Authentik" button, which the edge test never
|
||||
// follows — that is exactly how the RP login could be dead (500 on authorize,
|
||||
// or an email-claim gap bouncing back to /login) while 13- still passed green.
|
||||
// This spec drives the real RP round-trip and asserts a live Grafana session.
|
||||
|
||||
const HOST = "grafana.franksdank.cloud";
|
||||
const CHALLENGE = "Making sure you're not a bot";
|
||||
|
||||
test("SSO: Grafana relying-party login lands a real session", async ({ page, request }) => {
|
||||
test.setTimeout(180_000);
|
||||
|
||||
// Warm the guard: the vhost runs its own Anubis proof-of-work on first visit.
|
||||
await page.goto(`https://${HOST}/`);
|
||||
await expect
|
||||
.poll(
|
||||
async () => !(await page.locator("body").innerText()).includes(CHALLENGE),
|
||||
{ timeout: 90_000 },
|
||||
)
|
||||
.toBe(true);
|
||||
|
||||
// The "Sign in with Authentik" button targets this RP start URL exactly.
|
||||
// Going straight there is the same code path without depending on the SPA's
|
||||
// button markup.
|
||||
await page.goto(`https://${HOST}/login/generic_oauth`);
|
||||
|
||||
// The chain: grafana -> auth (implicit consent, the session is already
|
||||
// there) -> back to grafana with the code -> grafana provisions the user and
|
||||
// redirects to "/". A dead provider/claims config bounces back to /login.
|
||||
await expect
|
||||
.poll(
|
||||
async () => {
|
||||
const u = new URL(page.url());
|
||||
return u.hostname === HOST && u.pathname !== "/login";
|
||||
},
|
||||
{ timeout: 90_000 },
|
||||
)
|
||||
.toBe(true);
|
||||
|
||||
const body = await page.locator("body").innerText();
|
||||
expect(body).not.toContain("Login failed");
|
||||
expect(body.toLowerCase()).not.toContain("powered by authentik"); // IdP flow shell
|
||||
expect(body).not.toContain(CHALLENGE);
|
||||
|
||||
// A genuine logged-in session exists: /api/user answers 200 with an email,
|
||||
// not 401. This is the assertion the edge-only spec could never make.
|
||||
const resp = await page.request.get(`https://${HOST}/api/user`);
|
||||
expect(resp.status()).toBe(200);
|
||||
const user = await resp.json();
|
||||
expect(typeof user.email).toBe("string");
|
||||
expect(user.email.length).toBeGreaterThan(0);
|
||||
});
|
||||
Reference in New Issue
Block a user