forked from baron/baron-sso
Merge pull request 'RP scope 설정에 offline_access 안내 추가' (#1173) from feature/df-claim-tenant into dev
Reviewed-on: baron/baron-sso#1173
This commit is contained in:
@@ -450,6 +450,44 @@ describe("ClientGeneralPage RP claims", () => {
|
||||
expect(scopeInputs.some((input) => input.value === "old_claim")).toBe(true);
|
||||
});
|
||||
|
||||
it("shows the offline_access guide in the scopes section and expands its details", async () => {
|
||||
const { container } = await renderPage();
|
||||
|
||||
expect(container.textContent).toContain(
|
||||
"Refresh token 사용 시 offline_access scope가 필요합니다.",
|
||||
);
|
||||
expect(container.textContent).toContain(
|
||||
"scope 목록에 offline_access를 포함하고",
|
||||
);
|
||||
|
||||
const guideToggleButton = Array.from(
|
||||
container.querySelectorAll("button"),
|
||||
).find((button) =>
|
||||
(button.getAttribute("aria-label") ?? "").includes("상세 안내 보기"),
|
||||
);
|
||||
expect(guideToggleButton).toBeDefined();
|
||||
|
||||
await act(async () => {
|
||||
guideToggleButton?.dispatchEvent(
|
||||
new MouseEvent("click", { bubbles: true }),
|
||||
);
|
||||
});
|
||||
await flush();
|
||||
|
||||
expect(container.textContent).toContain(
|
||||
"Hydra 기준으로 refresh token 발급 조건",
|
||||
);
|
||||
expect(container.textContent).toContain(
|
||||
"authorization request scope에 offline 또는 offline_access 포함",
|
||||
);
|
||||
expect(container.textContent).toContain(
|
||||
"consent accept의 granted_scope에 offline 또는 offline_access 포함",
|
||||
);
|
||||
expect(container.textContent).toContain(
|
||||
"client grant_types에 refresh_token 포함",
|
||||
);
|
||||
});
|
||||
|
||||
it("blocks saving a number RP claim default value that is not numeric", async () => {
|
||||
const { container } = await renderPage();
|
||||
|
||||
|
||||
@@ -639,6 +639,8 @@ function ClientGeneralPage() {
|
||||
const [headlessLoginEnabled, setHeadlessLoginEnabled] = useState(false);
|
||||
|
||||
const [isScopePickerOpen, setIsScopePickerOpen] = useState(false);
|
||||
const [isOfflineAccessGuideOpen, setIsOfflineAccessGuideOpen] =
|
||||
useState(false);
|
||||
const [scopes, setScopes] = useState<ScopeItem[]>(() => [
|
||||
{
|
||||
id: "1",
|
||||
@@ -1970,6 +1972,77 @@ function ClientGeneralPage() {
|
||||
</Button>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-6">
|
||||
<div className="rounded-xl border border-amber-500/30 bg-amber-500/10 p-4">
|
||||
<div className="flex items-start justify-between gap-3">
|
||||
<div className="space-y-1">
|
||||
<div className="flex items-center gap-2 text-sm font-semibold text-amber-900 dark:text-amber-100">
|
||||
<Info className="h-4 w-4" />
|
||||
<span>
|
||||
{t(
|
||||
"ui.dev.clients.general.scopes.offline_access_title",
|
||||
"Refresh token 사용 시 offline_access scope가 필요합니다.",
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
<p className="text-xs leading-relaxed text-amber-950/80 dark:text-amber-50/80">
|
||||
{t(
|
||||
"msg.dev.clients.general.scopes.offline_access_summary",
|
||||
"RP가 refresh token을 사용하려면 scope 목록에 offline_access를 포함하고, consent와 grant type 설정도 함께 맞아야 합니다.",
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
<Button
|
||||
type="button"
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="h-8 shrink-0 gap-1 text-amber-900 hover:bg-amber-500/10 hover:text-amber-950 dark:text-amber-100 dark:hover:bg-amber-500/20 dark:hover:text-amber-50"
|
||||
onClick={() => setIsOfflineAccessGuideOpen((prev) => !prev)}
|
||||
aria-expanded={isOfflineAccessGuideOpen}
|
||||
aria-label={t(
|
||||
"ui.dev.clients.general.scopes.offline_access_toggle",
|
||||
"상세 안내 보기",
|
||||
)}
|
||||
>
|
||||
{isOfflineAccessGuideOpen ? (
|
||||
<X className="h-3.5 w-3.5" />
|
||||
) : (
|
||||
<Info className="h-3.5 w-3.5" />
|
||||
)}
|
||||
{t("ui.common.info", "상세 안내")}
|
||||
</Button>
|
||||
</div>
|
||||
{isOfflineAccessGuideOpen ? (
|
||||
<div className="mt-3 rounded-lg border border-amber-500/20 bg-background/70 p-3 text-xs leading-relaxed text-foreground shadow-sm">
|
||||
<p className="font-semibold">
|
||||
{t(
|
||||
"msg.dev.clients.general.scopes.offline_access_conditions_title",
|
||||
"Hydra 기준으로 refresh token 발급 조건",
|
||||
)}
|
||||
</p>
|
||||
<ul className="mt-2 list-disc space-y-1 pl-4">
|
||||
<li>
|
||||
{t(
|
||||
"msg.dev.clients.general.scopes.offline_access_condition_request",
|
||||
"authorization request scope에 offline 또는 offline_access 포함",
|
||||
)}
|
||||
</li>
|
||||
<li>
|
||||
{t(
|
||||
"msg.dev.clients.general.scopes.offline_access_condition_consent",
|
||||
"consent accept의 granted_scope에 offline 또는 offline_access 포함",
|
||||
)}
|
||||
</li>
|
||||
<li>
|
||||
{t(
|
||||
"msg.dev.clients.general.scopes.offline_access_condition_grant_type",
|
||||
"client grant_types에 refresh_token 포함",
|
||||
)}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
{isScopePickerOpen && (
|
||||
<div className="space-y-3 rounded-md border border-border bg-muted/10 p-4">
|
||||
<div className="flex items-center justify-between gap-3">
|
||||
|
||||
@@ -452,6 +452,11 @@ session_required_off = "Off: process logout using sub even if sid is missing."
|
||||
empty = "No scopes registered."
|
||||
subtitle = "Define the permission scopes this application can request."
|
||||
tenant = "Tenant access claim"
|
||||
offline_access_summary = "If the RP needs refresh tokens, include offline_access in the scope list and align the consent and grant type settings as well."
|
||||
offline_access_conditions_title = "Hydra conditions for issuing refresh tokens"
|
||||
offline_access_condition_request = "Include offline or offline_access in the authorization request scope."
|
||||
offline_access_condition_consent = "Include offline or offline_access in the consent accept granted_scope."
|
||||
offline_access_condition_grant_type = "Include refresh_token in the client grant_types."
|
||||
|
||||
[msg.dev.clients.general.id_token_claims]
|
||||
subtitle = "Manage RP-specific extension claims separately."
|
||||
@@ -1590,6 +1595,8 @@ add = "Scope Add"
|
||||
description_placeholder = "Description Placeholder"
|
||||
name_placeholder = "e.g. profile"
|
||||
title = "Scopes"
|
||||
offline_access_title = "offline_access scope is required when using refresh tokens."
|
||||
offline_access_toggle = "Show details"
|
||||
|
||||
[ui.dev.clients.general.scopes.table]
|
||||
description = "Scope Description"
|
||||
|
||||
@@ -452,6 +452,11 @@ session_required_off = "끄면: sid가 없어도 sub만으로 로그아웃 처
|
||||
empty = "등록된 스코프가 없습니다."
|
||||
subtitle = "이 앱이 요청할 수 있는 권한 범위를 정의합니다."
|
||||
tenant = "소속 테넌트 정보 접근"
|
||||
offline_access_summary = "RP가 refresh token을 사용하려면 scope 목록에 offline_access를 포함하고, consent와 grant type 설정도 함께 맞아야 합니다."
|
||||
offline_access_conditions_title = "Hydra 기준으로 refresh token 발급 조건"
|
||||
offline_access_condition_request = "authorization request scope에 offline 또는 offline_access 포함"
|
||||
offline_access_condition_consent = "consent accept의 granted_scope에 offline 또는 offline_access 포함"
|
||||
offline_access_condition_grant_type = "client grant_types에 refresh_token 포함"
|
||||
|
||||
[msg.dev.clients.general.id_token_claims]
|
||||
subtitle = "RP 전용 확장 claim을 구분해서 관리합니다."
|
||||
@@ -1589,6 +1594,8 @@ add = "스코프 추가"
|
||||
description_placeholder = "권한에 대한 설명"
|
||||
name_placeholder = "e.g. profile"
|
||||
title = "스코프"
|
||||
offline_access_title = "Refresh token 사용 시 offline_access scope가 필요합니다."
|
||||
offline_access_toggle = "상세 안내 보기"
|
||||
|
||||
[ui.dev.clients.general.scopes.table]
|
||||
description = "설명"
|
||||
|
||||
@@ -499,6 +499,11 @@ session_required_off = ""
|
||||
empty = ""
|
||||
subtitle = ""
|
||||
tenant = ""
|
||||
offline_access_summary = ""
|
||||
offline_access_conditions_title = ""
|
||||
offline_access_condition_request = ""
|
||||
offline_access_condition_consent = ""
|
||||
offline_access_condition_grant_type = ""
|
||||
|
||||
[msg.dev.clients.general.security]
|
||||
private_help = ""
|
||||
@@ -1638,6 +1643,8 @@ add = ""
|
||||
description_placeholder = ""
|
||||
name_placeholder = ""
|
||||
title = ""
|
||||
offline_access_title = ""
|
||||
offline_access_toggle = ""
|
||||
|
||||
[ui.dev.clients.general.scopes.table]
|
||||
description = ""
|
||||
|
||||
@@ -45,10 +45,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: characters
|
||||
sha256: faf38497bda5ead2a8c7615f4f7939df04333478bf32e4173fcb06d428b5716b
|
||||
sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.4.1"
|
||||
version: "1.4.0"
|
||||
cli_config:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -268,6 +268,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.5"
|
||||
js:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: js
|
||||
sha256: "53385261521cc4a0c4658fd0ad07a7d14591cf8fc33abbceae306ddb974888dc"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.7.2"
|
||||
leak_tracker:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -320,18 +328,18 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: matcher
|
||||
sha256: dc0b7dc7651697ea4ff3e69ef44b0407ea32c487a39fff6a4004fa585e901861
|
||||
sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.12.19"
|
||||
version: "0.12.17"
|
||||
material_color_utilities:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: material_color_utilities
|
||||
sha256: "9c337007e82b1889149c82ed242ed1cb24a66044e30979c44912381e9be4c48b"
|
||||
sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.13.0"
|
||||
version: "0.11.1"
|
||||
meta:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -653,26 +661,26 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: test
|
||||
sha256: "280d6d890011ca966ad08df7e8a4ddfab0fb3aa49f96ed6de56e3521347a9ae7"
|
||||
sha256: "75906bf273541b676716d1ca7627a17e4c4070a3a16272b7a3dc7da3b9f3f6b7"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.30.0"
|
||||
version: "1.26.3"
|
||||
test_api:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: test_api
|
||||
sha256: "8161c84903fd860b26bfdefb7963b3f0b68fee7adea0f59ef805ecca346f0c7a"
|
||||
sha256: ab2726c1a94d3176a45960b6234466ec367179b87dd74f1611adb1f3b5fb9d55
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.7.10"
|
||||
version: "0.7.7"
|
||||
test_core:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: test_core
|
||||
sha256: "0381bd1585d1a924763c308100f2138205252fb90c9d4eeaf28489ee65ccde51"
|
||||
sha256: "0cc24b5ff94b38d2ae73e1eb43cc302b77964fbf67abad1e296025b78deb53d0"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.6.16"
|
||||
version: "0.6.12"
|
||||
toml:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
||||
Reference in New Issue
Block a user