+ {formatDateTime(currentHeadlessJwksCache.expiresAt)}
+
- {currentHeadlessJwksCache.parsedKeys.map((key, index) => {
- const normalizedAlgorithm = key.alg?.trim() ?? "";
- const isMissingAlgorithm =
- normalizedAlgorithm === "";
- const isUnsupportedAlgorithm =
- !isMissingAlgorithm &&
- !HEADLESS_LOGIN_ALLOWED_ALGORITHM_SET.has(
- normalizedAlgorithm,
- );
+ {currentHeadlessJwksCache.parsedKeys.map(
+ (key, index) => {
+ const normalizedAlgorithm = key.alg?.trim() ?? "";
+ const isMissingAlgorithm =
+ normalizedAlgorithm === "";
+ const isUnsupportedAlgorithm =
+ !isMissingAlgorithm &&
+ !HEADLESS_LOGIN_ALLOWED_ALGORITHM_SET.has(
+ normalizedAlgorithm,
+ );
- return (
-
-
-
-
- KID
-
-
- {key.kid || "-"}
-
-
-
-
- KTY
-
-
- {key.kty || "-"}
-
-
-
-
- USE
-
-
- {key.use || "-"}
-
-
-
-
- ALG
-
-
+
+
+
+ KID
+
+
+ {key.kid || "-"}
+
+
+
+
+ KTY
+
+
+ {key.kty || "-"}
+
+
+
+
+ USE
+
+
+ {key.use || "-"}
+
+
+
+
+ ALG
+
+
+ {key.alg ||
+ t(
+ "msg.dev.clients.general.public_key.cache.missing_algorithm_badge",
+ "알고리즘 미선언",
+ )}
+
+ {isMissingAlgorithm && (
+
+ {t(
+ "msg.dev.clients.general.public_key.cache.missing_algorithm_reason",
+ "이 키는 `alg`가 비어 있어서 저장할 수 없습니다.",
+ )}
+
+ )}
+ {isUnsupportedAlgorithm && (
+
+ {t(
+ "msg.dev.clients.general.public_key.cache.unsupported_algorithm_reason",
+ "이 알고리즘은 Headless Login에서 지원되지 않습니다.",
+ )}
+
+ )}
+
+
+
+
+ {t(
+ "ui.dev.clients.general.public_key.cache.parsed_key_n",
+ "N",
)}
- >
- {key.alg ||
- t(
- "msg.dev.clients.general.public_key.cache.missing_algorithm_badge",
- "알고리즘 미선언",
- )}
- {isMissingAlgorithm && (
-
- {t(
- "msg.dev.clients.general.public_key.cache.missing_algorithm_reason",
- "이 키는 `alg`가 비어 있어서 저장할 수 없습니다.",
- )}
-
- )}
- {isUnsupportedAlgorithm && (
-
- {t(
- "msg.dev.clients.general.public_key.cache.unsupported_algorithm_reason",
- "이 알고리즘은 Headless Login에서 지원되지 않습니다.",
- )}
-
- )}
+
+ {key.n || "-"}
+
-
-
- {t(
- "ui.dev.clients.general.public_key.cache.parsed_key_n",
- "N",
- )}
-
-
- {key.n || "-"}
-
-
-
- );
- })}
+ );
+ },
+ )}
) : (
diff --git a/devfront/tests/devfront-clients-lifecycle.spec.ts b/devfront/tests/devfront-clients-lifecycle.spec.ts
index a67bff76..a048eaa7 100644
--- a/devfront/tests/devfront-clients-lifecycle.spec.ts
+++ b/devfront/tests/devfront-clients-lifecycle.spec.ts
@@ -152,8 +152,7 @@ test.describe("DevFront clients lifecycle", () => {
kty: "RSA",
use: "sig",
alg: "RS256",
- n:
- "voVbHlo_UHkjtT7Q_8owyjZ2omE8n8mbGlpraZziStHPfe08q_RGiEXO6Pyiz42NVi-Yo0c7qiaqRwB4h9s5phpT2wwcUxnkrQeRhe7BpigInZPzpwq1hsaB2zyhE7zTRCC3hinGtFdVpNzTVKYKGPbXfeEXaRL3P838vi-_iB4IN3WQk_pAakUQvajL2H-vcWSMSNslMGPDZxobqE9MHSWocNXemrcmtCeE7ruUND0qHZOb8k-hHUBqsNoJ63WKdapzGYF6e2qgDRveYrjgOCBigZPi8npN0xStQ0YcrH_RxeTogsdRZ8SuXmLqavryVDnrT8czPkkJ-EHb8PiTCQ",
+ n: "voVbHlo_UHkjtT7Q_8owyjZ2omE8n8mbGlpraZziStHPfe08q_RGiEXO6Pyiz42NVi-Yo0c7qiaqRwB4h9s5phpT2wwcUxnkrQeRhe7BpigInZPzpwq1hsaB2zyhE7zTRCC3hinGtFdVpNzTVKYKGPbXfeEXaRL3P838vi-_iB4IN3WQk_pAakUQvajL2H-vcWSMSNslMGPDZxobqE9MHSWocNXemrcmtCeE7ruUND0qHZOb8k-hHUBqsNoJ63WKdapzGYF6e2qgDRveYrjgOCBigZPi8npN0xStQ0YcrH_RxeTogsdRZ8SuXmLqavryVDnrT8czPkkJ-EHb8PiTCQ",
},
],
},
@@ -162,11 +161,13 @@ test.describe("DevFront clients lifecycle", () => {
consents: [] as Consent[],
auditLogsByCursor: undefined,
onRefreshHeadlessJwks(clientId: string) {
- this.clients[0].headlessJwksCache = {
- ...this.clients[0].headlessJwksCache!,
- lastRefreshStatus: "success",
- lastCheckedAt: "2026-04-01T00:00:00.000Z",
- };
+ if (this.clients[0].headlessJwksCache) {
+ this.clients[0].headlessJwksCache = {
+ ...this.clients[0].headlessJwksCache,
+ lastRefreshStatus: "success",
+ lastCheckedAt: "2026-04-01T00:00:00.000Z",
+ };
+ }
expect(clientId).toBe("client-headless-login");
},
onRevokeHeadlessJwksCache(clientId: string) {
@@ -184,13 +185,17 @@ test.describe("DevFront clients lifecycle", () => {
.click();
await expect(
- page.getByRole("heading", { name: /공개키 등록|Public Key Registration/i }),
+ page.getByRole("heading", {
+ name: /공개키 등록|Public Key Registration/i,
+ }),
).toBeVisible();
await expect(
page.getByText(/Request Object Signing Algorithm/i),
).toHaveCount(0);
- await expect(page.getByText(/Allowed algorithms|허용 알고리즘/i)).toHaveCount(0);
+ await expect(
+ page.getByText(/Allowed algorithms|허용 알고리즘/i),
+ ).toHaveCount(0);
await page
.getByPlaceholder(/https:\/\/rp\.example\.com\/\.well-known\/jwks\.json/i)
.fill(jwksUri);
@@ -256,7 +261,9 @@ test.describe("DevFront clients lifecycle", () => {
name: /공개키 등록|Public Key Registration/i,
}),
).toBeVisible();
- await expect(page.getByRole("textbox", { name: /JWKS URI|JWKS URI/i })).toHaveValue(jwksUri);
+ await expect(
+ page.getByRole("textbox", { name: /JWKS URI|JWKS URI/i }),
+ ).toHaveValue(jwksUri);
});
test("pkce headless login blocks save when parsed jwks algorithm is unsupported", async ({
@@ -306,9 +313,13 @@ test.describe("DevFront clients lifecycle", () => {
.fill(jwksUri);
await expect(
- page.getByText("지원하지 않는 알고리즘이 감지되었습니다.", { exact: true }),
+ page.getByText("지원하지 않는 알고리즘이 감지되었습니다.", {
+ exact: true,
+ }),
).toBeVisible();
- await expect(page.getByRole("button", { name: /^저장$|^Save$/i })).toBeDisabled();
+ await expect(
+ page.getByRole("button", { name: /^저장$|^Save$/i }),
+ ).toBeDisabled();
});
test("pkce headless login blocks save when parsed jwks algorithm is missing", async ({
@@ -356,6 +367,8 @@ test.describe("DevFront clients lifecycle", () => {
await expect(
page.getByText(/알고리즘이 선언되지 않았습니다|algorithm is missing/i),
).toBeVisible();
- await expect(page.getByRole("button", { name: /^저장$|^Save$/i })).toBeDisabled();
+ await expect(
+ page.getByRole("button", { name: /^저장$|^Save$/i }),
+ ).toBeDisabled();
});
});