1
0
forked from baron/baron-sso
Files
baron-sso/devfront/tests/devfront-login-claims.spec.ts

240 lines
6.4 KiB
TypeScript

import { expect, test } from "@playwright/test";
import {
getPersistedOidcUser,
installDevApiMock,
seedAuth,
} from "./helpers/devfront-fixtures";
import { captureEvidence } from "./helpers/evidence";
type ClaimScenario = {
title: string;
role: "super_admin" | "user";
tenantName: string;
userMeTenantId: string;
userMeCompanyCode: string;
profileClaims: Record<string, unknown>;
expectedProfileAssertions: Record<string, unknown>;
expectTenantsToBeAbsent?: boolean;
};
const claimScenarios: ClaimScenario[] = [
{
title: "Server Side App preserves tenant and rp claims",
role: "super_admin",
tenantName: "Server Side Tenant",
userMeTenantId: "tenant-server",
userMeCompanyCode: "server-hq",
profileClaims: {
tenant_id: "tenant-server",
companyCode: "server-hq",
profile: {
names: {
name: "서버 앱 사용자",
},
emails: ["server@example.com"],
},
joined_tenants: ["tenant-server", "tenant-ops"],
tenants: {
"tenant-server": {
department: "Platform",
grade: "Lead",
},
"tenant-ops": {
department: "Operations",
grade: "Member",
},
},
rp_claims: {
approvalLevel: "A",
},
metadata: {
rp_custom_claims: {
"server-app": {
approvalLevel: "A",
},
},
},
},
expectedProfileAssertions: {
tenant_id: "tenant-server",
companyCode: "server-hq",
joined_tenants: ["tenant-server", "tenant-ops"],
rp_claims: {
approvalLevel: "A",
},
},
},
{
title: "PKCE preserves nested profile claims without tenant map expansion",
role: "user",
tenantName: "PKCE Tenant",
userMeTenantId: "tenant-pkce",
userMeCompanyCode: "pkce-hq",
profileClaims: {
tenant_id: "tenant-pkce",
companyCode: "pkce-hq",
profile: {
names: {
name: "PKCE 사용자",
},
emails: ["pkce@example.com"],
},
joined_tenants: ["tenant-pkce"],
rp_claims: {
features: ["sso", "claims"],
},
metadata: {
rp_custom_claims: {
"pkce-app": {
features: ["sso", "claims"],
},
},
},
},
expectedProfileAssertions: {
tenant_id: "tenant-pkce",
companyCode: "pkce-hq",
joined_tenants: ["tenant-pkce"],
rp_claims: {
features: ["sso", "claims"],
},
},
expectTenantsToBeAbsent: true,
},
{
title: "Headless login keeps session claims together with rp claims",
role: "super_admin",
tenantName: "Headless Tenant",
userMeTenantId: "tenant-headless",
userMeCompanyCode: "headless-hq",
profileClaims: {
tenant_id: "tenant-headless",
companyCode: "headless-hq",
profile: {
names: {
name: "헤드리스 사용자",
},
emails: ["headless@example.com"],
},
joined_tenants: ["tenant-headless", "tenant-support"],
tenants: {
"tenant-headless": {
department: "Automation",
grade: "Manager",
},
"tenant-support": {
department: "Support",
grade: "Agent",
},
},
rp_claims: {
approvalLevel: "B",
loginMode: "headless",
},
sid: "session-headless-1",
session_id: "session-headless-1",
metadata: {
rp_custom_claims: {
"headless-app": {
approvalLevel: "B",
loginMode: "headless",
},
},
},
},
expectedProfileAssertions: {
tenant_id: "tenant-headless",
companyCode: "headless-hq",
joined_tenants: ["tenant-headless", "tenant-support"],
rp_claims: {
approvalLevel: "B",
loginMode: "headless",
},
sid: "session-headless-1",
session_id: "session-headless-1",
},
},
];
test.describe("DevFront login claims", () => {
test.afterEach(async ({ page }, testInfo) => {
if (testInfo.status === "passed") {
await captureEvidence(page, testInfo, testInfo.title);
}
});
for (const scenario of claimScenarios) {
test(scenario.title, async ({ page }) => {
await seedAuth(page, {
role: scenario.role,
profile: scenario.profileClaims,
});
await installDevApiMock(page, {
clients: [],
consents: [],
auditLogsByCursor: undefined,
users: [],
tenants: [
{
id: scenario.userMeTenantId,
name: scenario.tenantName,
slug: scenario.userMeCompanyCode,
},
],
});
await page.route("**/api/v1/user/me", async (route) => {
await route.fulfill({
status: 200,
contentType: "application/json",
body: JSON.stringify({
id: "playwright-user",
loginId: "playwright@example.com",
email: "playwright@example.com",
name: "Playwright User",
phoneNumber: "",
department: "QA",
tenantId: "",
tenantName: "",
role: scenario.role,
createdAt: "2026-06-01T00:00:00.000Z",
updatedAt: "2026-06-01T00:00:00.000Z",
}),
});
});
await page.goto("/profile");
await expect(
page.getByRole("heading", { name: "내 정보" }),
).toBeVisible();
const storedUser = await getPersistedOidcUser(page);
expect(storedUser).not.toBeNull();
expect(storedUser?.profile).toMatchObject(
scenario.expectedProfileAssertions,
);
if (scenario.expectTenantsToBeAbsent) {
expect(storedUser?.profile).not.toHaveProperty("tenants");
} else {
expect(storedUser?.profile).toHaveProperty("tenants");
}
await expect(
page.getByText(String(scenario.profileClaims.tenant_id)),
).toBeVisible();
await expect(page.getByText(scenario.userMeCompanyCode)).toBeVisible();
await page.getByRole("button", { name: "권한 및 역할" }).click();
await expect(
page.getByRole("heading", { name: "시스템 역할" }),
).toBeVisible();
await expect(
page.getByText(
scenario.role === "super_admin"
? /시스템 관리자|Super Admin/i
: /일반 사용자|General User/i,
),
).toBeVisible();
});
}
});