Files
serverAppConfigs/tests/e2e/specs/20-grafana-sso.spec.ts
Frank 200039d589 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).
2026-10-20 20:40:15 -07:00

57 lines
2.3 KiB
TypeScript

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);
});