From 498fdd802c984d3430ec961262e79d316913e5c4 Mon Sep 17 00:00:00 2001 From: kyy Date: Wed, 13 May 2026 11:24:28 +0900 Subject: [PATCH] =?UTF-8?q?Server=20side=20app=20=ED=81=B4=EB=9D=BC?= =?UTF-8?q?=EC=9D=B4=EC=96=B8=ED=8A=B8=20=ED=82=A4=20=ED=91=9C=EC=8B=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../features/clients/ClientDetailsPage.tsx | 30 +++++++------------ .../clients/clientSecretPolicy.test.ts | 28 +++++++++++++++++ .../features/clients/clientSecretPolicy.ts | 7 +++++ 3 files changed, 45 insertions(+), 20 deletions(-) create mode 100644 devfront/src/features/clients/clientSecretPolicy.test.ts create mode 100644 devfront/src/features/clients/clientSecretPolicy.ts diff --git a/devfront/src/features/clients/ClientDetailsPage.tsx b/devfront/src/features/clients/ClientDetailsPage.tsx index d9487d72..0f2c50b0 100644 --- a/devfront/src/features/clients/ClientDetailsPage.tsx +++ b/devfront/src/features/clients/ClientDetailsPage.tsx @@ -39,6 +39,7 @@ import { import { t } from "../../lib/i18n"; import { cn } from "../../lib/utils"; import { ClientDetailTabs } from "./ClientDetailTabs"; +import { canDisplayClientSecret } from "./clientSecretPolicy"; function ClientDetailsPage() { const params = useParams(); @@ -175,7 +176,6 @@ function ClientDetailsPage() { } const client = data?.client; - const isHeadlessLogin = client?.metadata?.headless_login_enabled === true; if (!client) { return null; } @@ -214,21 +214,16 @@ function ClientDetailsPage() { }, ]; - const hasClientSecret = client.type === "private" && !isHeadlessLogin; + const hasClientSecret = canDisplayClientSecret(client); const secretPlaceholder = "SECRET_NOT_AVAILABLE"; const clientSecret = hasClientSecret ? client?.clientSecret || secretPlaceholder : t("ui.common.na", "N/A"); const displaySecret = !hasClientSecret - ? isHeadlessLogin - ? t( - "msg.dev.clients.details.secret_not_applicable_headless", - "이 앱은 Headless Login용 signed key 인증을 사용하므로 Client Secret을 사용하지 않습니다.", - ) - : t( - "msg.dev.clients.details.secret_not_applicable", - "PKCE 앱에는 Client Secret이 없습니다.", - ) + ? t( + "msg.dev.clients.details.secret_not_applicable", + "PKCE 앱에는 Client Secret이 없습니다.", + ) : clientSecret === secretPlaceholder ? t("msg.dev.clients.details.secret_unavailable", "SECRET_NOT_AVAILABLE") : clientSecret; @@ -400,15 +395,10 @@ function ClientDetailsPage() { {!hasClientSecret ? (

- {isHeadlessLogin - ? t( - "msg.dev.clients.details.secret_not_applicable_headless", - "이 앱은 Headless Login용 signed key 인증을 사용하므로 Client Secret을 사용하지 않습니다.", - ) - : t( - "msg.dev.clients.details.secret_not_applicable", - "PKCE 앱에는 Client Secret이 없습니다.", - )} + {t( + "msg.dev.clients.details.secret_not_applicable", + "PKCE 앱에는 Client Secret이 없습니다.", + )}

) : null} diff --git a/devfront/src/features/clients/clientSecretPolicy.test.ts b/devfront/src/features/clients/clientSecretPolicy.test.ts new file mode 100644 index 00000000..c273f530 --- /dev/null +++ b/devfront/src/features/clients/clientSecretPolicy.test.ts @@ -0,0 +1,28 @@ +import { describe, expect, it } from "vitest"; +import { canDisplayClientSecret } from "./clientSecretPolicy"; + +describe("client secret policy", () => { + it("allows client secret display for server-side apps", () => { + expect( + canDisplayClientSecret({ + type: "private", + }), + ).toBe(true); + }); + + it("still allows client secret display for server-side apps even when headless login is enabled in metadata", () => { + expect( + canDisplayClientSecret({ + type: "private", + }), + ).toBe(true); + }); + + it("does not allow client secret display for PKCE apps", () => { + expect( + canDisplayClientSecret({ + type: "pkce", + }), + ).toBe(false); + }); +}); diff --git a/devfront/src/features/clients/clientSecretPolicy.ts b/devfront/src/features/clients/clientSecretPolicy.ts new file mode 100644 index 00000000..a2ccbf85 --- /dev/null +++ b/devfront/src/features/clients/clientSecretPolicy.ts @@ -0,0 +1,7 @@ +type ClientSecretPolicyTarget = { + type: string; +}; + +export function canDisplayClientSecret(client: ClientSecretPolicyTarget) { + return client.type === "private"; +}