124 lines
3.9 KiB
JavaScript
124 lines
3.9 KiB
JavaScript
import assert from "node:assert/strict";
|
|
import { chromium } from "playwright-core";
|
|
|
|
const keycloakUrl = process.env.KEYCLOAK_URL ?? "http://localhost:8080";
|
|
const adminUsername = process.env.KC_BOOTSTRAP_ADMIN_USERNAME;
|
|
const adminPassword = process.env.KC_BOOTSTRAP_ADMIN_PASSWORD;
|
|
const mockPassword = process.env.MOCK_GOOGLE_USER_PASSWORD;
|
|
assert.ok(adminUsername && adminPassword && mockPassword);
|
|
|
|
async function adminToken() {
|
|
const response = await fetch(
|
|
`${keycloakUrl}/realms/master/protocol/openid-connect/token`,
|
|
{
|
|
method: "POST",
|
|
body: new URLSearchParams({
|
|
client_id: "admin-cli",
|
|
grant_type: "password",
|
|
username: adminUsername,
|
|
password: adminPassword,
|
|
}),
|
|
},
|
|
);
|
|
assert.equal(response.status, 200);
|
|
return (await response.json()).access_token;
|
|
}
|
|
|
|
async function usersByEmail(token) {
|
|
const response = await fetch(
|
|
`${keycloakUrl}/admin/realms/keycloak-patterns/users?email=${encodeURIComponent(
|
|
"broker-new-user@example.test",
|
|
)}&exact=true`,
|
|
{ headers: { Authorization: `Bearer ${token}` } },
|
|
);
|
|
assert.equal(response.status, 200);
|
|
return response.json();
|
|
}
|
|
|
|
async function userById(token, userId) {
|
|
const response = await fetch(
|
|
`${keycloakUrl}/admin/realms/keycloak-patterns/users/${userId}`,
|
|
{ headers: { Authorization: `Bearer ${token}` } },
|
|
);
|
|
assert.equal(response.status, 200);
|
|
return response.json();
|
|
}
|
|
|
|
async function removePreviousUser(token) {
|
|
for (const user of await usersByEmail(token)) {
|
|
const response = await fetch(
|
|
`${keycloakUrl}/admin/realms/keycloak-patterns/users/${user.id}`,
|
|
{
|
|
method: "DELETE",
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
},
|
|
);
|
|
assert.equal(response.status, 204);
|
|
}
|
|
}
|
|
|
|
async function brokerLogin(page) {
|
|
const url = new URL(
|
|
`${keycloakUrl}/realms/keycloak-patterns/protocol/openid-connect/auth`,
|
|
);
|
|
url.search = new URLSearchParams({
|
|
client_id: "spa-public",
|
|
redirect_uri: "http://localhost:8088/",
|
|
response_type: "code",
|
|
scope: "openid profile email",
|
|
state: crypto.randomUUID(),
|
|
nonce: crypto.randomUUID(),
|
|
code_challenge: "K2qUEfBl-nQvF2gB4dNxC2zYVwZc1CVnZb5CsX2L7fI",
|
|
code_challenge_method: "S256",
|
|
kc_idp_hint: "mock-google",
|
|
prompt: "login",
|
|
});
|
|
await page.goto(url.toString());
|
|
await page.waitForURL(/\/realms\/mock-google\//u);
|
|
await page.locator("#username").fill("mock-new-user");
|
|
await page.locator("#password").fill(mockPassword);
|
|
await page.locator("#kc-login").click();
|
|
await page.waitForURL(/localhost:8088\/\?.*code=/u);
|
|
}
|
|
|
|
const token = await adminToken();
|
|
await removePreviousUser(token);
|
|
|
|
const browser = await chromium.launch({
|
|
executablePath: process.env.CHROME_BIN ?? "/usr/bin/google-chrome",
|
|
headless: true,
|
|
args: ["--no-sandbox"],
|
|
});
|
|
|
|
try {
|
|
const page = await browser.newPage();
|
|
await brokerLogin(page);
|
|
|
|
const users = await usersByEmail(token);
|
|
assert.equal(users.length, 1);
|
|
const user = await userById(token, users[0].id);
|
|
assert.match(user.username, /^mock-google\.[0-9a-f-]+$/u);
|
|
assert.equal(user.firstName, "Broker");
|
|
assert.equal(user.lastName, "New");
|
|
assert.deepEqual(user.attributes.picture, [
|
|
"https://images.example.test/mock-user.png",
|
|
]);
|
|
assert.deepEqual(user.attributes.hd, ["example.test"]);
|
|
|
|
const identitiesResponse = await fetch(
|
|
`${keycloakUrl}/admin/realms/keycloak-patterns/users/${user.id}/federated-identity`,
|
|
{ headers: { Authorization: `Bearer ${token}` } },
|
|
);
|
|
assert.equal(identitiesResponse.status, 200);
|
|
const identities = await identitiesResponse.json();
|
|
assert.equal(identities.length, 1);
|
|
assert.equal(identities[0].identityProvider, "mock-google");
|
|
assert.ok(identities[0].userId);
|
|
|
|
console.log(
|
|
"Google claim mapping verified: stable sub username, profile attributes, federated identity",
|
|
);
|
|
} finally {
|
|
await browser.close();
|
|
}
|