import assert from "node:assert/strict"; import { chromium } from "playwright-core"; const username = process.env.E2E_USERNAME ?? "regular-user"; const password = process.env.E2E_PASSWORD; assert.ok(password, "E2E_PASSWORD must be set"); 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(); let authorizationUrl; page.on("request", (request) => { if (request.url().includes("/protocol/openid-connect/auth")) { authorizationUrl = new URL(request.url()); } }); await page.goto("http://localhost:8088"); await page.locator("#login").click(); await page.waitForURL(/localhost:8080/u); await page.locator("#username").fill(username); await page.locator("#password").fill(password); await page.locator("#kc-login").click(); await page.waitForURL("http://localhost:8088/"); await page.locator('[data-authenticated="true"]').waitFor(); assert.equal(authorizationUrl?.searchParams.get("response_type"), "code"); assert.equal(authorizationUrl?.searchParams.get("code_challenge_method"), "S256"); assert.ok(authorizationUrl?.searchParams.get("code_challenge")); const accessToken = await page.evaluate(() => window.__pattern1.getAccessToken()); assert.ok(accessToken, "access token must exist in browser memory"); const payload = JSON.parse( Buffer.from(accessToken.split(".")[1], "base64url").toString("utf8"), ); const audiences = Array.isArray(payload.aud) ? payload.aud : [payload.aud]; assert.ok( audiences.includes("keycloak-pattern-api"), "access token must target keycloak-pattern-api", ); const storageSnapshot = await page.evaluate(() => ({ localStorage: Object.values(localStorage), sessionStorage: Object.values(sessionStorage), })); assert.equal( JSON.stringify(storageSnapshot).includes(accessToken), false, "access token must not be persisted in Web Storage", ); await page.locator("#call-api").click(); await page.waitForFunction(() => { const text = document.querySelector("#result")?.textContent ?? ""; return text.includes('"httpStatus": 200'); }); if (process.env.WRONG_AUDIENCE_URL) { const response = await fetch(process.env.WRONG_AUDIENCE_URL, { headers: { Authorization: `Bearer ${accessToken}` }, }); assert.equal( response.status, 401, "the same signed token must fail when the Resource Server expects another audience", ); } if (process.env.WRONG_ISSUER_URL) { const response = await fetch(process.env.WRONG_ISSUER_URL, { headers: { Authorization: `Bearer ${accessToken}` }, }); assert.equal( response.status, 401, "the same signed token must fail when the Resource Server expects another issuer", ); } await page.reload(); await page.locator('[data-authenticated="false"]').waitFor(); assert.equal( await page.evaluate(() => window.__pattern1.getAccessToken()), null, "reload must clear the memory-only token", ); console.log( "pattern1 browser verified: code+PKCE S256, audience/issuer negatives 401, Web Storage token 0, reload clears token", ); } finally { await browser.close(); }