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;
|
||||
|
||||
Reference in New Issue
Block a user