§8 full browser E2E (todo §8); the real Playwright UI against the live stack — the browser-UI flows the earlier full-stack suites deferred here. New e2e/full-flow.spec.ts + compose.e2e-full.yml covering password login, mocked SSO, menu filtering by role, users/groups/roles CRUD, a permission-gated plugin page, and logout (6/6 green on a clean stack, then torn down). Same-origin gateway (e2e/proxy.mjs, stdlib reverse proxy) fronts web + Kratos on one host so the browser's cookies round-trip (the themed form posts straight to Kratos); ory/kratos/e2e-proxy.yml points Kratos at it + --dev so cookies aren't Secure over http. SSO backed by a stdlib mock OIDC provider (e2e/mock-oidc.mjs, RS256 id_token, nonce-bound codes). Found + fixed a real bug the E2E surfaced: the SSO submit button shares the form with the required email/password fields, so HTML5 validation blocked it — added formnovalidate to the SSO buttons (auth-card.ejs), tests-first. Stability-reviewer APPROVE, no Critical/High (every dev/insecure knob is e2e-overlay-scoped, base/prod compose unaffected). typecheck + 305 units green. Also marks the §8 E2E-harness item (full stack up + seeded admin/Keto roles + tear-down).
This commit is contained in:
24
e2e/proxy.mjs
Normal file
24
e2e/proxy.mjs
Normal file
@@ -0,0 +1,24 @@
|
||||
// Same-origin gateway for the browser E2E (todo §8). The themed login form posts straight to
|
||||
// Kratos' flow action and Kratos sets the session cookie for its own base_url host — so for a real
|
||||
// browser, web and Kratos must look like ONE origin (cookies are host-scoped). This tiny stdlib
|
||||
// reverse proxy fronts both on a single host (the browser's only origin), exactly as a production
|
||||
// reverse proxy would: Kratos-owned paths → kratos, everything else → web. NOT app code; dev/test only.
|
||||
import { createServer, request } from "node:http";
|
||||
|
||||
const WEB = new URL(process.env.WEB_URL ?? "http://web:3000");
|
||||
const KRATOS = new URL(process.env.KRATOS_URL ?? "http://kratos:4433");
|
||||
const PORT = Number(process.env.PORT ?? 80);
|
||||
|
||||
// Kratos public owns these prefixes (self-service flows, sessions, its well-known/schemas); the
|
||||
// browser hits them via the flow action + OIDC callbacks. Everything else is the web app.
|
||||
const toKratos = (path) => ["/self-service", "/sessions", "/.well-known/ory", "/schemas"].some((p) => path === p || path.startsWith(`${p}/`));
|
||||
|
||||
createServer((req, res) => {
|
||||
const target = toKratos(req.url ?? "/") ? KRATOS : WEB;
|
||||
const upstream = request(
|
||||
{ headers: req.headers, host: target.hostname, method: req.method, path: req.url, port: target.port },
|
||||
(up) => { res.writeHead(up.statusCode ?? 502, up.headers); up.pipe(res); },
|
||||
);
|
||||
upstream.on("error", (err) => { res.writeHead(502, { "content-type": "text/plain" }).end(`gateway: ${err.message}`); });
|
||||
req.pipe(upstream);
|
||||
}).listen(PORT, () => console.log(`e2e gateway on :${PORT} → web ${WEB.host} / kratos ${KRATOS.host}`));
|
||||
Reference in New Issue
Block a user