1
0
forked from baron/baron-sso

RP scope 설정에 offline_access 안내 추가

This commit is contained in:
2026-06-16 09:53:13 +09:00
parent ce8a1f46a7
commit 38091429f4
5 changed files with 134 additions and 0 deletions

View File

@@ -450,6 +450,46 @@ 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(
"offline_access 상세 안내 보기",
),
);
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();

View File

@@ -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",
"offline_access 상세 안내 보기",
)}
>
{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">

View File

@@ -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 offline_access help"
[ui.dev.clients.general.scopes.table]
description = "Scope Description"

View File

@@ -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 = "offline_access 상세 안내 보기"
[ui.dev.clients.general.scopes.table]
description = "설명"

View File

@@ -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 = ""