forked from baron/baron-sso
Trusted RP 설정 UX 및 안내 문구 개선
This commit is contained in:
@@ -48,7 +48,7 @@ interface ScopeItem {
|
||||
mandatory: boolean;
|
||||
}
|
||||
|
||||
type SecurityProfile = "private" | "pkce" | "private_key_jwt";
|
||||
type SecurityProfile = "private" | "pkce";
|
||||
|
||||
function readMetadataString(
|
||||
metadata: Record<string, unknown>,
|
||||
@@ -101,10 +101,11 @@ function ClientGeneralPage() {
|
||||
const [tokenEndpointAuthMethod, setTokenEndpointAuthMethod] = useState<
|
||||
"none" | "client_secret_basic" | "private_key_jwt"
|
||||
>("client_secret_basic");
|
||||
const [jwksSource, setJwksSource] = useState<"uri" | "inline">("uri");
|
||||
const [jwksSource, setJwksSource] = useState<"uri" | "inline">("inline");
|
||||
const [jwksUri, setJwksUri] = useState("");
|
||||
const [jwksText, setJwksText] = useState("");
|
||||
const [requestObjectSigningAlg, setRequestObjectSigningAlg] = useState("RS256");
|
||||
const [headlessLoginEnabled, setHeadlessLoginEnabled] = useState(false);
|
||||
|
||||
const [scopes, setScopes] = useState<ScopeItem[]>(() => [
|
||||
{
|
||||
@@ -150,6 +151,8 @@ function ClientGeneralPage() {
|
||||
if (typeof metadata.description === "string") setDescription(metadata.description);
|
||||
if (typeof metadata.logo_url === "string") setLogoUrl(metadata.logo_url);
|
||||
|
||||
setHeadlessLoginEnabled(!!metadata.headless_login_enabled);
|
||||
|
||||
// Fallbacks from metadata if top-level fields are empty
|
||||
if (!client.tokenEndpointAuthMethod) {
|
||||
const metaAuth = readMetadataString(metadata, "token_endpoint_auth_method");
|
||||
@@ -188,38 +191,25 @@ function ClientGeneralPage() {
|
||||
}
|
||||
}, [data]);
|
||||
|
||||
const securityProfile: SecurityProfile =
|
||||
tokenEndpointAuthMethod === "private_key_jwt"
|
||||
? "private_key_jwt"
|
||||
: clientType === "pkce"
|
||||
? "pkce"
|
||||
: "private";
|
||||
|
||||
const headlessLoginEnabled = securityProfile === "private_key_jwt";
|
||||
const securityProfile: SecurityProfile = clientType === "pkce" ? "pkce" : "private";
|
||||
|
||||
const handleSecurityProfileChange = (profile: SecurityProfile) => {
|
||||
setClientType(profile);
|
||||
if (profile === "pkce") {
|
||||
setClientType("pkce");
|
||||
setTokenEndpointAuthMethod("none");
|
||||
setJwksUri("");
|
||||
setJwksText("");
|
||||
setRequestObjectSigningAlg("");
|
||||
return;
|
||||
setTokenEndpointAuthMethod(headlessLoginEnabled ? "private_key_jwt" : "none");
|
||||
} else {
|
||||
setTokenEndpointAuthMethod("client_secret_basic");
|
||||
}
|
||||
};
|
||||
|
||||
setClientType("private");
|
||||
if (profile === "private_key_jwt") {
|
||||
setTokenEndpointAuthMethod("private_key_jwt");
|
||||
if (requestObjectSigningAlg.trim() === "") {
|
||||
const handleHeadlessToggle = (enabled: boolean) => {
|
||||
setHeadlessLoginEnabled(enabled);
|
||||
if (clientType === "pkce") {
|
||||
setTokenEndpointAuthMethod(enabled ? "private_key_jwt" : "none");
|
||||
if (enabled && requestObjectSigningAlg.trim() === "") {
|
||||
setRequestObjectSigningAlg("RS256");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
setTokenEndpointAuthMethod("client_secret_basic");
|
||||
setJwksUri("");
|
||||
setJwksText("");
|
||||
setRequestObjectSigningAlg("");
|
||||
};
|
||||
|
||||
const addScope = () => {
|
||||
@@ -794,7 +784,7 @@ function ClientGeneralPage() {
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
<div className="grid gap-4 md:grid-cols-3">
|
||||
<div className="grid gap-4 md:grid-cols-2">
|
||||
<label
|
||||
className={cn(
|
||||
"relative flex cursor-pointer flex-col gap-1 rounded-xl border-2 p-4 transition",
|
||||
@@ -856,43 +846,34 @@ function ClientGeneralPage() {
|
||||
<span className="absolute right-4 top-4 text-primary">
|
||||
{securityProfile === "pkce" ? "✓" : ""}
|
||||
</span>
|
||||
</label>
|
||||
|
||||
<label
|
||||
className={cn(
|
||||
"relative flex cursor-pointer flex-col gap-1 rounded-xl border-2 p-4 transition",
|
||||
securityProfile === "private_key_jwt"
|
||||
? "border-primary bg-primary/5"
|
||||
: "border-border bg-card hover:border-muted-foreground/40",
|
||||
{securityProfile === "pkce" && (
|
||||
<div
|
||||
className="mt-4 pt-4 border-t border-primary/20 flex items-center justify-between"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<div className="space-y-0.5">
|
||||
<Label className="text-xs font-bold cursor-pointer" htmlFor="trusted-rp-toggle">
|
||||
{t("ui.dev.clients.general.security.trusted_rp_enable", "Trusted RP (자체 로그인 UI 사용)")}
|
||||
</Label>
|
||||
<p className="text-[10px] text-muted-foreground">
|
||||
{t("ui.dev.clients.general.security.trusted_rp_enable_help", "Baron SSO 로그인 창을 거치지 않고 애플리케이션 내의 자체 로그인 화면을 직접 구현하고 싶은 경우 활성화합니다.")}
|
||||
</p>
|
||||
</div>
|
||||
<Switch
|
||||
id="trusted-rp-toggle"
|
||||
checked={headlessLoginEnabled}
|
||||
onCheckedChange={handleHeadlessToggle}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
>
|
||||
<input
|
||||
className="sr-only"
|
||||
type="radio"
|
||||
name="security-profile"
|
||||
checked={securityProfile === "private_key_jwt"}
|
||||
onChange={() => handleSecurityProfileChange("private_key_jwt")}
|
||||
/>
|
||||
<span className="flex items-center gap-2 text-sm font-bold uppercase text-foreground">
|
||||
<Shield className="h-4 w-4 text-primary" />
|
||||
{t("ui.dev.clients.general.security.trusted", "Trusted RP")}
|
||||
</span>
|
||||
<span className="whitespace-pre-line text-xs text-muted-foreground">
|
||||
{t(
|
||||
"msg.dev.clients.general.security.trusted_help",
|
||||
"private_key_jwt와 공개키 등록을 사용해 trusted RP로 운영합니다. Headless Login은 이 프로필에서만 사용할 수 있습니다.",
|
||||
)}
|
||||
</span>
|
||||
<span className="absolute right-4 top-4 text-primary">
|
||||
{securityProfile === "private_key_jwt" ? "✓" : ""}
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* 4. Public Key Registration (Trusted RP) */}
|
||||
{securityProfile === "private_key_jwt" && (
|
||||
{clientType === "pkce" && headlessLoginEnabled && (
|
||||
<Card className="glass-panel border-primary/20">
|
||||
<CardHeader className="pb-3">
|
||||
<div className="flex flex-wrap items-start justify-between gap-3">
|
||||
@@ -902,9 +883,6 @@ function ClientGeneralPage() {
|
||||
"ui.dev.clients.general.public_key.title",
|
||||
"Public Key Registration",
|
||||
)}
|
||||
<Badge variant="info" className="px-2 py-0.5 text-[10px] uppercase">
|
||||
Trusted RP
|
||||
</Badge>
|
||||
</CardTitle>
|
||||
<CardDescription>
|
||||
{t(
|
||||
@@ -928,7 +906,7 @@ function ClientGeneralPage() {
|
||||
<p className="mt-1 text-xs text-muted-foreground">
|
||||
{t(
|
||||
"msg.dev.clients.general.public_key.headless_help",
|
||||
"RP 자체 로그인 UI를 사용하고, 실제 인증 처리는 Baron Backend API를 통해 백그라운드에서 수행합니다.",
|
||||
"애플리케이션 고유의 디자인으로 로그인 화면을 구성할 수 있습니다. 실제 아이디/비밀번호 확인 및 보안 검증 로직은 Baron API를 통해 백그라운드에서 처리됩니다.",
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
@@ -985,21 +963,21 @@ function ClientGeneralPage() {
|
||||
<input
|
||||
type="radio"
|
||||
name="jwksSource"
|
||||
checked={jwksSource === "uri"}
|
||||
onChange={() => setJwksSource("uri")}
|
||||
checked={jwksSource === "inline"}
|
||||
onChange={() => setJwksSource("inline")}
|
||||
className="accent-primary"
|
||||
/>
|
||||
<span>JWKS URI (권장)</span>
|
||||
<span>Inline Public Key</span>
|
||||
</label>
|
||||
<label className="flex items-center gap-2 text-sm">
|
||||
<input
|
||||
type="radio"
|
||||
name="jwksSource"
|
||||
checked={jwksSource === "inline"}
|
||||
onChange={() => setJwksSource("inline")}
|
||||
checked={jwksSource === "uri"}
|
||||
onChange={() => setJwksSource("uri")}
|
||||
className="accent-primary"
|
||||
/>
|
||||
<span>Inline Public Key (직접 입력)</span>
|
||||
<span>JWKS URI</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
@@ -1117,4 +1095,4 @@ function ClientGeneralPage() {
|
||||
);
|
||||
}
|
||||
|
||||
export default ClientGeneralPage;
|
||||
export default ClientGeneralPage;
|
||||
|
||||
@@ -401,7 +401,8 @@ guide_intro = "A JWKS URI is not created by Baron. It is the URL where the RP ba
|
||||
guide_step_1 = "Generate a key pair on the RP server and keep the private key only in the RP backend."
|
||||
guide_step_2 = "Expose the public key from the RP backend through a JWKS (JSON Web Key Set) endpoint."
|
||||
guide_step_3 = "Enter a URL such as https://rp.example.com/.well-known/jwks.json in DevFront."
|
||||
headless_help = "Trusted RPs can keep their own login UI while Baron continues to handle authentication and OIDC progression."
|
||||
headless_help = "You can design your own login UI within the application. While the UI is yours, the actual identity verification and security checks are handled in the background via Baron's API."
|
||||
jwks_inline_help = "Prefer the SSH-RSA public key format first. If you paste an 'ssh-rsa AAA...' key, Baron converts it to OIDC-standard JWKS (JSON) before saving."
|
||||
jwks_uri_help = "Enter the public key endpoint URL exposed by the RP backend. Example: https://rp.example.com/.well-known/jwks.json"
|
||||
request_object_alg_help = "Specify the JAR (Request Object) signing algorithm used for headless login."
|
||||
source_help = "Register the JWKS URI served by the RP so Baron can verify the public key."
|
||||
@@ -1393,6 +1394,8 @@ private = "Server Side App"
|
||||
pkce = "PKCE"
|
||||
trusted = "Trusted RP"
|
||||
title = "Security Settings"
|
||||
trusted_rp_enable = "Trusted RP (Custom Login UI)"
|
||||
trusted_rp_enable_help = "Enable this if you want to implement your own login screen within the app instead of using the Baron SSO login page."
|
||||
|
||||
[ui.dev.clients.general.public_key]
|
||||
auth_method = "Token Endpoint Auth Method"
|
||||
@@ -1403,6 +1406,8 @@ guide_toggle = "JWKS URI Setup Guide"
|
||||
headless_disabled = "Headless Disabled"
|
||||
headless_enabled = "Headless Enabled"
|
||||
headless_toggle = "Headless Login"
|
||||
jwks_inline = "SSH-RSA or JWKS Public Key"
|
||||
jwks_inline_placeholder = "Paste an 'ssh-rsa AAA...' public key first. JWKS (JSON) is also accepted if needed."
|
||||
jwks_uri = "JWKS URI"
|
||||
jwks_uri_placeholder = "https://rp.example.com/.well-known/jwks.json"
|
||||
request_object_alg = "Request Object Signing Algorithm"
|
||||
|
||||
@@ -401,10 +401,11 @@ guide_intro = "JWKS URI는 Baron이 만드는 값이 아니라 RP backend가 공
|
||||
guide_step_1 = "RP 서버에서 key pair를 생성하고 private key는 RP backend에만 보관합니다."
|
||||
guide_step_2 = "RP backend가 public key를 JWKS(JSON Web Key Set) 형태로 제공하는 endpoint를 준비합니다."
|
||||
guide_step_3 = "예: https://rp.example.com/.well-known/jwks.json 같은 URL을 DevFront에 입력합니다."
|
||||
headless_help = "Trusted RP는 RP 자체 로그인 UI를 사용할 수 있지만, bootstrap 검증, 사용자 인증 처리, Hydra 연계, 최종 redirect 생성은 Baron backend가 담당합니다."
|
||||
headless_help = "애플리케이션 고유의 디자인으로 로그인 화면을 구성할 수 있습니다. 실제 아이디/비밀번호 확인 및 보안 검증 로직은 Baron API를 통해 백그라운드에서 처리됩니다."
|
||||
jwks_inline_help = "SSH-RSA 공개키 형식을 우선 권장합니다. 'ssh-rsa AAA...' 형식으로 입력하면 Baron이 OIDC 표준인 JWKS(JSON)로 자동 변환하여 저장합니다."
|
||||
jwks_uri_help = "RP backend가 제공하는 공개키 endpoint URL을 입력하세요. 예: https://rp.example.com/.well-known/jwks.json"
|
||||
request_object_alg_help = "Headless Login을 사용할 때 JAR(Request Object) 서명 알고리즘을 명시합니다."
|
||||
source_help = "운영 환경에서는 RP가 서빙하는 JWKS URI를 등록해 공개키를 검증합니다."
|
||||
source_help = "애플리케이션의 공개키(SSH-RSA)를 직접 등록하거나, 운영 환경이라면 JWKS URI를 통해 자동으로 검증할 수 있습니다."
|
||||
subtitle = "Trusted RP 판정에 필요한 공개키와 headless login 관련 설정을 관리합니다."
|
||||
|
||||
[msg.dev.clients.general.public_key.validation]
|
||||
@@ -412,6 +413,7 @@ headless_requires_alg = "Headless Login을 사용하려면 Request Object Signin
|
||||
headless_requires_private_key_jwt = "Headless Login을 사용하려면 token endpoint auth method가 private_key_jwt여야 합니다."
|
||||
headless_requires_public_key = "Headless Login을 사용하려면 JWKS URI가 필요합니다."
|
||||
invalid_jwks_uri = "JWKS URI 형식이 올바르지 않습니다."
|
||||
missing_jwks_inline = "공개키(SSH-RSA 또는 JWKS)를 입력해야 합니다."
|
||||
private_key_jwt_requires_public_key = "서명 키 기반 인증을 사용하려면 JWKS URI가 필요합니다."
|
||||
|
||||
[msg.dev.clients.help]
|
||||
@@ -1390,8 +1392,10 @@ delete = "삭제"
|
||||
[ui.dev.clients.general.security]
|
||||
private = "Server side App"
|
||||
pkce = "PKCE"
|
||||
trusted = "Trusted RP"
|
||||
title = "보안 설정"
|
||||
trusted_rp_enable = "Trusted RP (자체 로그인 UI 사용)"
|
||||
trusted_rp_enable_help = "Baron SSO 로그인 창을 거치지 않고 애플리케이션 내의 자체 로그인 화면을 직접 구현하고 싶은 경우 활성화합니다."
|
||||
|
||||
|
||||
[ui.dev.clients.general.public_key]
|
||||
auth_method = "Token Endpoint Auth Method"
|
||||
@@ -1402,6 +1406,8 @@ guide_toggle = "JWKS URI 준비 가이드"
|
||||
headless_disabled = "Headless Disabled"
|
||||
headless_enabled = "Headless Enabled"
|
||||
headless_toggle = "Headless Login"
|
||||
jwks_inline = "SSH-RSA 또는 JWKS 공개키"
|
||||
jwks_inline_placeholder = "'ssh-rsa AAA...' 형식의 공개키를 먼저 붙여넣으세요. 필요하면 JWKS (JSON)도 입력할 수 있습니다."
|
||||
jwks_uri = "JWKS URI"
|
||||
jwks_uri_placeholder = "https://rp.example.com/.well-known/jwks.json"
|
||||
request_object_alg = "Request Object Signing Algorithm"
|
||||
|
||||
@@ -402,6 +402,7 @@ guide_step_1 = ""
|
||||
guide_step_2 = ""
|
||||
guide_step_3 = ""
|
||||
headless_help = ""
|
||||
jwks_inline_help = ""
|
||||
jwks_uri_help = ""
|
||||
request_object_alg_help = ""
|
||||
source_help = ""
|
||||
@@ -1390,8 +1391,9 @@ delete = ""
|
||||
[ui.dev.clients.general.security]
|
||||
private = ""
|
||||
pkce = ""
|
||||
trusted = ""
|
||||
title = ""
|
||||
trusted_rp_enable = ""
|
||||
trusted_rp_enable_help = ""
|
||||
|
||||
[ui.dev.clients.general.public_key]
|
||||
auth_method = ""
|
||||
@@ -1402,6 +1404,8 @@ guide_toggle = ""
|
||||
headless_disabled = ""
|
||||
headless_enabled = ""
|
||||
headless_toggle = ""
|
||||
jwks_inline = ""
|
||||
jwks_inline_placeholder = ""
|
||||
jwks_uri = ""
|
||||
jwks_uri_placeholder = ""
|
||||
request_object_alg = ""
|
||||
|
||||
@@ -1459,6 +1459,8 @@ delete = "Delete"
|
||||
private = "Server Side App"
|
||||
pkce = "PKCE"
|
||||
title = "Security Settings"
|
||||
trusted_rp_enable = "Trusted RP (Custom Login UI)"
|
||||
trusted_rp_enable_help = "Enable this if you want to implement your own login screen within the app instead of using the Baron SSO login page."
|
||||
|
||||
[ui.dev.clients.help]
|
||||
docs_body = "Includes PKCE, client_secret_basic, redirect URI validation tips."
|
||||
|
||||
@@ -1712,6 +1712,8 @@ title = "스코프"
|
||||
private = "Server side App"
|
||||
pkce = "PKCE"
|
||||
title = "보안 설정"
|
||||
trusted_rp_enable = "Trusted RP (자체 로그인 UI 사용)"
|
||||
trusted_rp_enable_help = "Baron SSO 로그인 창을 거치지 않고 애플리케이션 내의 자체 로그인 화면을 직접 구현하고 싶은 경우 활성화합니다."
|
||||
|
||||
[ui.dev.dashboard.ops.card]
|
||||
consent_revoked = "Consent 회수 건수"
|
||||
|
||||
@@ -1706,6 +1706,8 @@ title = ""
|
||||
private = ""
|
||||
pkce = ""
|
||||
title = ""
|
||||
trusted_rp_enable = ""
|
||||
trusted_rp_enable_help = ""
|
||||
|
||||
[ui.dev.dashboard.ops.card]
|
||||
consent_revoked = ""
|
||||
|
||||
Reference in New Issue
Block a user