112 lines
4.0 KiB
JavaScript
112 lines
4.0 KiB
JavaScript
import assert from "node:assert/strict";
|
|
import { chromium } from "playwright-core";
|
|
|
|
const password = process.env.E2E_PASSWORD;
|
|
assert.ok(password, "E2E_PASSWORD must be set");
|
|
|
|
async function completeKeycloakLogin(page) {
|
|
for (let attempt = 1; attempt <= 2; attempt += 1) {
|
|
await page.locator("#username").fill(
|
|
process.env.E2E_USERNAME ?? "regular-user",
|
|
);
|
|
await page.locator("#password").fill(password);
|
|
await page.locator("#kc-login").click();
|
|
await page.waitForLoadState("domcontentloaded");
|
|
|
|
if (page.url() === "http://localhost:4180/edge/me") {
|
|
return;
|
|
}
|
|
if (attempt === 1) {
|
|
await page.goto("http://localhost:4180/oauth2/start?rd=%2Fedge%2Fme");
|
|
await page.waitForURL(/localhost:8080/u);
|
|
}
|
|
}
|
|
throw new Error(`Keycloak login did not return to AP4: ${page.url()}`);
|
|
}
|
|
|
|
const browser = await chromium.launch({
|
|
executablePath: process.env.CHROME_BIN ?? "/usr/bin/google-chrome",
|
|
headless: true,
|
|
args: ["--no-sandbox"],
|
|
});
|
|
|
|
try {
|
|
const context = await browser.newContext();
|
|
const page = await context.newPage();
|
|
const browserRequests = [];
|
|
page.on("request", (request) =>
|
|
browserRequests.push({ method: request.method(), url: request.url() }),
|
|
);
|
|
|
|
const edgeResponsePromise = page.waitForResponse(
|
|
(response) =>
|
|
response.url() === "http://localhost:4180/edge/me" &&
|
|
response.status() === 302,
|
|
);
|
|
const authorizationRequestPromise = page.waitForRequest((request) =>
|
|
request.url().includes(
|
|
"/protocol/openid-connect/auth?approval_prompt=",
|
|
),
|
|
);
|
|
await page.goto("http://localhost:4180/edge/me");
|
|
const unauthenticatedEdgeResponse = await edgeResponsePromise;
|
|
assert.equal(unauthenticatedEdgeResponse.status(), 302);
|
|
|
|
const authorizationRequest = await authorizationRequestPromise;
|
|
const authorizationUrl = new URL(authorizationRequest.url());
|
|
assert.equal(authorizationUrl.searchParams.get("client_id"), "edge-proxy");
|
|
assert.equal(authorizationUrl.searchParams.get("code_challenge_method"), "S256");
|
|
assert.ok(authorizationUrl.searchParams.get("code_challenge"));
|
|
|
|
await page.waitForURL(/localhost:8080/u);
|
|
await completeKeycloakLogin(page);
|
|
const edgeIdentity = JSON.parse(await page.locator("body").innerText());
|
|
assert.equal(edgeIdentity.pattern, "AP4-edge-forward-auth");
|
|
assert.ok(edgeIdentity.user);
|
|
assert.equal(edgeIdentity.identityHeader, "X-Forwarded-User");
|
|
|
|
const callbackRequest = browserRequests.find(({ url }) =>
|
|
url.startsWith("http://localhost:4180/oauth2/callback?"),
|
|
);
|
|
assert.ok(callbackRequest);
|
|
assert.equal(callbackRequest.method, "GET");
|
|
assert.equal(
|
|
browserRequests.some(({ url }) =>
|
|
url.includes("/protocol/openid-connect/token"),
|
|
),
|
|
false,
|
|
"the confidential token exchange must be server-to-server",
|
|
);
|
|
|
|
const cookies = await context.cookies("http://localhost:4180/");
|
|
const sessionCookie = cookies.find((cookie) => cookie.name === "AP4_SESSION");
|
|
assert.ok(sessionCookie);
|
|
assert.equal(sessionCookie.httpOnly, true);
|
|
assert.equal(sessionCookie.sameSite, "Lax");
|
|
assert.equal(sessionCookie.secure, false);
|
|
|
|
const storage = await page.evaluate(() => ({
|
|
localStorage: Object.values(localStorage),
|
|
sessionStorage: Object.values(sessionStorage),
|
|
readableCookies: document.cookie,
|
|
}));
|
|
assert.deepEqual(storage.localStorage, []);
|
|
assert.deepEqual(storage.sessionStorage, []);
|
|
assert.equal(storage.readableCookies.includes("AP4_SESSION"), false);
|
|
|
|
const missingHeader = await fetch("http://localhost:8081/edge/me");
|
|
assert.equal(missingHeader.status, 401);
|
|
const directSpoof = await fetch("http://localhost:8081/edge/me", {
|
|
headers: { "X-Forwarded-User": "spoofed-admin" },
|
|
});
|
|
assert.equal(directSpoof.status, 200);
|
|
const spoofedIdentity = await directSpoof.json();
|
|
assert.equal(spoofedIdentity.user, "spoofed-admin");
|
|
|
|
console.log(
|
|
"pattern4 oauth2-proxy verified: redirect, PKCE login, forwarded-user 200, direct spoof precondition",
|
|
);
|
|
} finally {
|
|
await browser.close();
|
|
}
|