forked from baron/baron-sso
devfront 설정 탭 섹션 순서 조정
This commit is contained in:
@@ -1892,85 +1892,8 @@ function ClientGeneralPage() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Card className="glass-panel">
|
||||
<CardHeader className="pb-4">
|
||||
<div className="flex flex-col gap-2 md:flex-row md:items-start md:justify-between">
|
||||
<div className="space-y-1">
|
||||
<CardTitle className="text-xl font-bold">
|
||||
{t("ui.dev.clients.general.auto_login.title", "자동 로그인")}
|
||||
</CardTitle>
|
||||
<CardDescription>
|
||||
{t(
|
||||
"msg.dev.clients.general.auto_login.subtitle",
|
||||
"RP가 자체 로그인 시작 URL에서 OIDC 요청을 만들 수 있으면 userfront에서 바로 로그인 진입을 제공합니다.",
|
||||
)}
|
||||
</CardDescription>
|
||||
</div>
|
||||
<div className="flex items-center gap-3 rounded-xl border border-border bg-muted/30 px-4 py-3">
|
||||
<div className="space-y-0.5 text-right">
|
||||
<p className="text-sm font-semibold">
|
||||
{autoLoginSupported
|
||||
? t("ui.common.enabled", "사용")
|
||||
: t("ui.common.disabled", "사용 안 함")}
|
||||
</p>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{t(
|
||||
"ui.dev.clients.general.auto_login.supported",
|
||||
"자동 로그인 지원",
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
<Switch
|
||||
checked={autoLoginSupported}
|
||||
onCheckedChange={setAutoLoginSupported}
|
||||
id="auto-login-supported"
|
||||
aria-label={t(
|
||||
"ui.dev.clients.general.auto_login.supported",
|
||||
"자동 로그인 지원",
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="auto-login-url" className="text-sm font-semibold">
|
||||
{t(
|
||||
"ui.dev.clients.general.auto_login.url",
|
||||
"자동 로그인 시작 URL",
|
||||
)}
|
||||
</Label>
|
||||
<Input
|
||||
id="auto-login-url"
|
||||
value={autoLoginUrl}
|
||||
onChange={(event) => setAutoLoginUrl(event.target.value)}
|
||||
disabled={!autoLoginSupported}
|
||||
aria-invalid={!hasValidAutoLoginUrl}
|
||||
className={!hasValidAutoLoginUrl ? "border-destructive" : ""}
|
||||
placeholder={t(
|
||||
"ui.dev.clients.general.auto_login.url_placeholder",
|
||||
"https://app.example.com/login?auto=1",
|
||||
)}
|
||||
/>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{t(
|
||||
"msg.dev.clients.general.auto_login.help",
|
||||
"이 URL은 RP가 state, nonce, PKCE 값을 직접 생성한 뒤 Baron OIDC로 리다이렉트해야 합니다.",
|
||||
)}
|
||||
</p>
|
||||
{!hasValidAutoLoginUrl ? (
|
||||
<p className="text-xs text-destructive">
|
||||
{t(
|
||||
"msg.dev.clients.general.auto_login.invalid_url",
|
||||
"자동 로그인 URL 형식이 올바르지 않습니다. http 또는 https 주소를 입력하세요.",
|
||||
)}
|
||||
</p>
|
||||
) : null}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* 2. Scopes */}
|
||||
{/* 3. Custom Claims */}
|
||||
<Card className="glass-panel">
|
||||
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-4">
|
||||
<div>
|
||||
@@ -2389,229 +2312,6 @@ function ClientGeneralPage() {
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card className="glass-panel">
|
||||
<CardHeader className="pb-4">
|
||||
<div className="flex flex-col gap-2 md:flex-row md:items-start md:justify-between">
|
||||
<div className="space-y-1">
|
||||
<CardTitle className="text-xl font-bold">
|
||||
{t(
|
||||
"ui.dev.clients.general.tenant_access.title",
|
||||
"테넌트 접근 제한",
|
||||
)}
|
||||
</CardTitle>
|
||||
<div className="text-sm text-muted-foreground">
|
||||
<p className="leading-relaxed">
|
||||
{t(
|
||||
"ui.dev.clients.general.tenant_access.subtitle",
|
||||
"허용된 테넌트만 이 RP에 접근할 수 있도록 제한합니다.",
|
||||
)}
|
||||
</p>
|
||||
<p className="leading-relaxed">
|
||||
{t(
|
||||
"ui.dev.clients.general.tenant_access.hint",
|
||||
"제한을 켜면 tenants 스코프가 자동으로 포함되며, 허용 테넌트를 하나 이상 선택해야 합니다.",
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-3 rounded-xl border border-border bg-muted/30 px-4 py-3">
|
||||
<div className="space-y-0.5 text-right">
|
||||
<p className="text-sm font-semibold">
|
||||
{tenantAccessRestricted
|
||||
? t(
|
||||
"ui.dev.clients.general.tenant_access.enabled",
|
||||
"제한 있음",
|
||||
)
|
||||
: t(
|
||||
"ui.dev.clients.general.tenant_access.disabled",
|
||||
"제한 없음",
|
||||
)}
|
||||
</p>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{t(
|
||||
"ui.dev.clients.general.tenant_access.title",
|
||||
"테넌트 접근 제한",
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
<Switch
|
||||
checked={tenantAccessRestricted}
|
||||
onCheckedChange={handleTenantAccessToggle}
|
||||
id="tenant-access-toggle"
|
||||
disabled={isGeneralSettingsReadOnly}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-3">
|
||||
{tenantAccessRestricted ? (
|
||||
<div className="grid gap-4 lg:grid-cols-[0.8fr_1.2fr]">
|
||||
<div className="space-y-3">
|
||||
<Label className="text-sm font-semibold">
|
||||
{t(
|
||||
"ui.dev.clients.general.tenant_access.picker_label",
|
||||
"Add allowed tenant",
|
||||
)}{" "}
|
||||
<span className="text-destructive">*</span>
|
||||
</Label>
|
||||
<TenantAccessPicker
|
||||
disabled={isGeneralSettingsReadOnly}
|
||||
selectedCount={allowedTenantIds.length}
|
||||
onSelectTenant={(selection) =>
|
||||
handleSelectAllowedTenant(selection.id)
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="space-y-3">
|
||||
<Label className="text-sm font-semibold">
|
||||
{t(
|
||||
"ui.dev.clients.general.tenant_access.selected_title",
|
||||
"허용 테넌트",
|
||||
)}
|
||||
</Label>
|
||||
<SettingsTableShell bodyClassName="max-h-80">
|
||||
<SettingsTable>
|
||||
<SettingsTableHeader className="sticky top-0 z-10">
|
||||
<tr>
|
||||
<SettingsTableHead className="w-[28%]">
|
||||
{t(
|
||||
"ui.dev.clients.general.tenant_access.table.name",
|
||||
"테넌트명",
|
||||
)}
|
||||
</SettingsTableHead>
|
||||
<SettingsTableHead className="w-[18%]">
|
||||
{t(
|
||||
"ui.dev.clients.general.tenant_access.table.slug",
|
||||
"슬러그",
|
||||
)}
|
||||
</SettingsTableHead>
|
||||
<SettingsTableHead>
|
||||
{t(
|
||||
"ui.dev.clients.general.tenant_access.table.id",
|
||||
"테넌트 ID",
|
||||
)}
|
||||
</SettingsTableHead>
|
||||
<SettingsTableHead className="w-[112px] text-right">
|
||||
{t(
|
||||
"ui.dev.clients.general.tenant_access.table.actions",
|
||||
"작업",
|
||||
)}
|
||||
</SettingsTableHead>
|
||||
</tr>
|
||||
</SettingsTableHeader>
|
||||
<SettingsTableBody>
|
||||
{selectedAllowedTenants.length > 0 ? (
|
||||
<>
|
||||
{selectedAllowedTenants.map((tenant) => (
|
||||
<SettingsTableRow
|
||||
key={tenant.id}
|
||||
data-testid={`allowed-tenant-${tenant.id}`}
|
||||
>
|
||||
<SettingsTableCell className="align-middle font-medium">
|
||||
<span className="block truncate">
|
||||
{tenant.name}
|
||||
</span>
|
||||
</SettingsTableCell>
|
||||
<SettingsTableCell className="align-middle text-muted-foreground">
|
||||
{tenant.slug || "-"}
|
||||
</SettingsTableCell>
|
||||
<SettingsTableCell className="align-middle font-mono text-sm text-muted-foreground">
|
||||
<span className="break-all">{tenant.id}</span>
|
||||
</SettingsTableCell>
|
||||
<SettingsTableCell className="align-middle text-right">
|
||||
<div className="inline-flex items-center gap-1.5">
|
||||
<CopyButton
|
||||
aria-label="테넌트 UUID 복사"
|
||||
className="h-8 w-8"
|
||||
data-testid={`allowed-tenant-copy-${tenant.id}`}
|
||||
size="icon"
|
||||
value={tenant.id}
|
||||
variant="ghost"
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
aria-label={t("ui.common.delete", "삭제")}
|
||||
onClick={() =>
|
||||
toggleAllowedTenant(tenant.id)
|
||||
}
|
||||
className="inline-flex h-8 w-8 items-center justify-center rounded-md text-muted-foreground transition hover:text-destructive"
|
||||
data-testid={`allowed-tenant-remove-${tenant.id}`}
|
||||
disabled={isGeneralSettingsReadOnly}
|
||||
>
|
||||
<Trash2 className="h-4 w-4" />
|
||||
</button>
|
||||
</div>
|
||||
</SettingsTableCell>
|
||||
</SettingsTableRow>
|
||||
))}
|
||||
{allowedTenantIds
|
||||
.filter(
|
||||
(tenantId) =>
|
||||
!selectedAllowedTenants.some(
|
||||
(tenant) => tenant.id === tenantId,
|
||||
),
|
||||
)
|
||||
.map((tenantId) => (
|
||||
<SettingsTableRow
|
||||
key={tenantId}
|
||||
data-testid={`allowed-tenant-${tenantId}`}
|
||||
>
|
||||
<SettingsTableCell className="align-middle font-medium">
|
||||
<span className="block truncate">
|
||||
{tenantId}
|
||||
</span>
|
||||
</SettingsTableCell>
|
||||
<SettingsTableCell className="align-middle text-muted-foreground">
|
||||
-
|
||||
</SettingsTableCell>
|
||||
<SettingsTableCell className="align-middle font-mono text-sm text-muted-foreground">
|
||||
<span className="break-all">{tenantId}</span>
|
||||
</SettingsTableCell>
|
||||
<SettingsTableCell className="align-middle text-right">
|
||||
<div className="inline-flex items-center gap-1.5">
|
||||
<CopyButton
|
||||
aria-label="테넌트 UUID 복사"
|
||||
className="h-8 w-8"
|
||||
data-testid={`allowed-tenant-copy-${tenantId}`}
|
||||
size="icon"
|
||||
value={tenantId}
|
||||
variant="ghost"
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
aria-label={t("ui.common.delete", "삭제")}
|
||||
onClick={() =>
|
||||
toggleAllowedTenant(tenantId)
|
||||
}
|
||||
className="inline-flex h-8 w-8 items-center justify-center rounded-md text-muted-foreground transition hover:text-destructive"
|
||||
data-testid={`allowed-tenant-remove-${tenantId}`}
|
||||
disabled={isGeneralSettingsReadOnly}
|
||||
>
|
||||
<Trash2 className="h-4 w-4" />
|
||||
</button>
|
||||
</div>
|
||||
</SettingsTableCell>
|
||||
</SettingsTableRow>
|
||||
))}
|
||||
</>
|
||||
) : (
|
||||
<SettingsTableEmptyState colSpan={4} className="py-4">
|
||||
{t(
|
||||
"ui.dev.clients.general.tenant_access.selected_empty",
|
||||
"아직 선택된 테넌트가 없습니다.",
|
||||
)}
|
||||
</SettingsTableEmptyState>
|
||||
)}
|
||||
</SettingsTableBody>
|
||||
</SettingsTable>
|
||||
</SettingsTableShell>
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card className="glass-panel">
|
||||
<CardHeader className="pb-4">
|
||||
<div className="flex flex-col gap-2 md:flex-row md:items-start md:justify-between">
|
||||
@@ -3029,7 +2729,310 @@ function ClientGeneralPage() {
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* 3. Security Settings */}
|
||||
{/* 4. Tenant Access Restriction */}
|
||||
<Card className="glass-panel">
|
||||
<CardHeader className="pb-4">
|
||||
<div className="flex flex-col gap-2 md:flex-row md:items-start md:justify-between">
|
||||
<div className="space-y-1">
|
||||
<CardTitle className="text-xl font-bold">
|
||||
{t(
|
||||
"ui.dev.clients.general.tenant_access.title",
|
||||
"테넌트 접근 제한",
|
||||
)}
|
||||
</CardTitle>
|
||||
<div className="text-sm text-muted-foreground">
|
||||
<p className="leading-relaxed">
|
||||
{t(
|
||||
"ui.dev.clients.general.tenant_access.subtitle",
|
||||
"허용된 테넌트만 이 RP에 접근할 수 있도록 제한합니다.",
|
||||
)}
|
||||
</p>
|
||||
<p className="leading-relaxed">
|
||||
{t(
|
||||
"ui.dev.clients.general.tenant_access.hint",
|
||||
"제한을 켜면 tenants 스코프가 자동으로 포함되며, 허용 테넌트를 하나 이상 선택해야 합니다.",
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-3 rounded-xl border border-border bg-muted/30 px-4 py-3">
|
||||
<div className="space-y-0.5 text-right">
|
||||
<p className="text-sm font-semibold">
|
||||
{tenantAccessRestricted
|
||||
? t(
|
||||
"ui.dev.clients.general.tenant_access.enabled",
|
||||
"제한 있음",
|
||||
)
|
||||
: t(
|
||||
"ui.dev.clients.general.tenant_access.disabled",
|
||||
"제한 없음",
|
||||
)}
|
||||
</p>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{t(
|
||||
"ui.dev.clients.general.tenant_access.title",
|
||||
"테넌트 접근 제한",
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
<Switch
|
||||
checked={tenantAccessRestricted}
|
||||
onCheckedChange={handleTenantAccessToggle}
|
||||
id="tenant-access-toggle"
|
||||
disabled={isGeneralSettingsReadOnly}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-3">
|
||||
{tenantAccessRestricted ? (
|
||||
<div className="grid gap-4 lg:grid-cols-[0.8fr_1.2fr]">
|
||||
<div className="space-y-3">
|
||||
<Label className="text-sm font-semibold">
|
||||
{t(
|
||||
"ui.dev.clients.general.tenant_access.picker_label",
|
||||
"Add allowed tenant",
|
||||
)}{" "}
|
||||
<span className="text-destructive">*</span>
|
||||
</Label>
|
||||
<TenantAccessPicker
|
||||
disabled={isGeneralSettingsReadOnly}
|
||||
selectedCount={allowedTenantIds.length}
|
||||
onSelectTenant={(selection) =>
|
||||
handleSelectAllowedTenant(selection.id)
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="space-y-3">
|
||||
<Label className="text-sm font-semibold">
|
||||
{t(
|
||||
"ui.dev.clients.general.tenant_access.selected_title",
|
||||
"허용 테넌트",
|
||||
)}
|
||||
</Label>
|
||||
<SettingsTableShell bodyClassName="max-h-80">
|
||||
<SettingsTable>
|
||||
<SettingsTableHeader className="sticky top-0 z-10">
|
||||
<tr>
|
||||
<SettingsTableHead className="w-[28%]">
|
||||
{t(
|
||||
"ui.dev.clients.general.tenant_access.table.name",
|
||||
"테넌트명",
|
||||
)}
|
||||
</SettingsTableHead>
|
||||
<SettingsTableHead className="w-[18%]">
|
||||
{t(
|
||||
"ui.dev.clients.general.tenant_access.table.slug",
|
||||
"슬러그",
|
||||
)}
|
||||
</SettingsTableHead>
|
||||
<SettingsTableHead>
|
||||
{t(
|
||||
"ui.dev.clients.general.tenant_access.table.id",
|
||||
"테넌트 ID",
|
||||
)}
|
||||
</SettingsTableHead>
|
||||
<SettingsTableHead className="w-[112px] text-right">
|
||||
{t(
|
||||
"ui.dev.clients.general.tenant_access.table.actions",
|
||||
"작업",
|
||||
)}
|
||||
</SettingsTableHead>
|
||||
</tr>
|
||||
</SettingsTableHeader>
|
||||
<SettingsTableBody>
|
||||
{selectedAllowedTenants.length > 0 ? (
|
||||
<>
|
||||
{selectedAllowedTenants.map((tenant) => (
|
||||
<SettingsTableRow
|
||||
key={tenant.id}
|
||||
data-testid={`allowed-tenant-${tenant.id}`}
|
||||
>
|
||||
<SettingsTableCell className="align-middle font-medium">
|
||||
<span className="block truncate">
|
||||
{tenant.name}
|
||||
</span>
|
||||
</SettingsTableCell>
|
||||
<SettingsTableCell className="align-middle text-muted-foreground">
|
||||
{tenant.slug || "-"}
|
||||
</SettingsTableCell>
|
||||
<SettingsTableCell className="align-middle font-mono text-sm text-muted-foreground">
|
||||
<span className="break-all">{tenant.id}</span>
|
||||
</SettingsTableCell>
|
||||
<SettingsTableCell className="align-middle text-right">
|
||||
<div className="inline-flex items-center gap-1.5">
|
||||
<CopyButton
|
||||
aria-label="테넌트 UUID 복사"
|
||||
className="h-8 w-8"
|
||||
data-testid={`allowed-tenant-copy-${tenant.id}`}
|
||||
size="icon"
|
||||
value={tenant.id}
|
||||
variant="ghost"
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
aria-label={t("ui.common.delete", "삭제")}
|
||||
onClick={() =>
|
||||
toggleAllowedTenant(tenant.id)
|
||||
}
|
||||
className="inline-flex h-8 w-8 items-center justify-center rounded-md text-muted-foreground transition hover:text-destructive"
|
||||
data-testid={`allowed-tenant-remove-${tenant.id}`}
|
||||
disabled={isGeneralSettingsReadOnly}
|
||||
>
|
||||
<Trash2 className="h-4 w-4" />
|
||||
</button>
|
||||
</div>
|
||||
</SettingsTableCell>
|
||||
</SettingsTableRow>
|
||||
))}
|
||||
{allowedTenantIds
|
||||
.filter(
|
||||
(tenantId) =>
|
||||
!selectedAllowedTenants.some(
|
||||
(tenant) => tenant.id === tenantId,
|
||||
),
|
||||
)
|
||||
.map((tenantId) => (
|
||||
<SettingsTableRow
|
||||
key={tenantId}
|
||||
data-testid={`allowed-tenant-${tenantId}`}
|
||||
>
|
||||
<SettingsTableCell className="align-middle font-medium">
|
||||
<span className="block truncate">
|
||||
{tenantId}
|
||||
</span>
|
||||
</SettingsTableCell>
|
||||
<SettingsTableCell className="align-middle text-muted-foreground">
|
||||
-
|
||||
</SettingsTableCell>
|
||||
<SettingsTableCell className="align-middle font-mono text-sm text-muted-foreground">
|
||||
<span className="break-all">{tenantId}</span>
|
||||
</SettingsTableCell>
|
||||
<SettingsTableCell className="align-middle text-right">
|
||||
<div className="inline-flex items-center gap-1.5">
|
||||
<CopyButton
|
||||
aria-label="테넌트 UUID 복사"
|
||||
className="h-8 w-8"
|
||||
data-testid={`allowed-tenant-copy-${tenantId}`}
|
||||
size="icon"
|
||||
value={tenantId}
|
||||
variant="ghost"
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
aria-label={t("ui.common.delete", "삭제")}
|
||||
onClick={() =>
|
||||
toggleAllowedTenant(tenantId)
|
||||
}
|
||||
className="inline-flex h-8 w-8 items-center justify-center rounded-md text-muted-foreground transition hover:text-destructive"
|
||||
data-testid={`allowed-tenant-remove-${tenantId}`}
|
||||
disabled={isGeneralSettingsReadOnly}
|
||||
>
|
||||
<Trash2 className="h-4 w-4" />
|
||||
</button>
|
||||
</div>
|
||||
</SettingsTableCell>
|
||||
</SettingsTableRow>
|
||||
))}
|
||||
</>
|
||||
) : (
|
||||
<SettingsTableEmptyState colSpan={4} className="py-4">
|
||||
{t(
|
||||
"ui.dev.clients.general.tenant_access.selected_empty",
|
||||
"아직 선택된 테넌트가 없습니다.",
|
||||
)}
|
||||
</SettingsTableEmptyState>
|
||||
)}
|
||||
</SettingsTableBody>
|
||||
</SettingsTable>
|
||||
</SettingsTableShell>
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* 5. Auto Login */}
|
||||
<Card className="glass-panel">
|
||||
<CardHeader className="pb-4">
|
||||
<div className="flex flex-col gap-2 md:flex-row md:items-start md:justify-between">
|
||||
<div className="space-y-1">
|
||||
<CardTitle className="text-xl font-bold">
|
||||
{t("ui.dev.clients.general.auto_login.title", "자동 로그인")}
|
||||
</CardTitle>
|
||||
<CardDescription>
|
||||
{t(
|
||||
"msg.dev.clients.general.auto_login.subtitle",
|
||||
"RP가 자체 로그인 시작 URL에서 OIDC 요청을 만들 수 있으면 userfront에서 바로 로그인 진입을 제공합니다.",
|
||||
)}
|
||||
</CardDescription>
|
||||
</div>
|
||||
<div className="flex items-center gap-3 rounded-xl border border-border bg-muted/30 px-4 py-3">
|
||||
<div className="space-y-0.5 text-right">
|
||||
<p className="text-sm font-semibold">
|
||||
{autoLoginSupported
|
||||
? t("ui.common.enabled", "사용")
|
||||
: t("ui.common.disabled", "사용 안 함")}
|
||||
</p>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{t(
|
||||
"ui.dev.clients.general.auto_login.supported",
|
||||
"자동 로그인 지원",
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
<Switch
|
||||
checked={autoLoginSupported}
|
||||
onCheckedChange={setAutoLoginSupported}
|
||||
id="auto-login-supported"
|
||||
aria-label={t(
|
||||
"ui.dev.clients.general.auto_login.supported",
|
||||
"자동 로그인 지원",
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="auto-login-url" className="text-sm font-semibold">
|
||||
{t(
|
||||
"ui.dev.clients.general.auto_login.url",
|
||||
"자동 로그인 시작 URL",
|
||||
)}
|
||||
</Label>
|
||||
<Input
|
||||
id="auto-login-url"
|
||||
value={autoLoginUrl}
|
||||
onChange={(event) => setAutoLoginUrl(event.target.value)}
|
||||
disabled={!autoLoginSupported}
|
||||
aria-invalid={!hasValidAutoLoginUrl}
|
||||
className={!hasValidAutoLoginUrl ? "border-destructive" : ""}
|
||||
placeholder={t(
|
||||
"ui.dev.clients.general.auto_login.url_placeholder",
|
||||
"https://app.example.com/login?auto=1",
|
||||
)}
|
||||
/>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{t(
|
||||
"msg.dev.clients.general.auto_login.help",
|
||||
"이 URL은 RP가 state, nonce, PKCE 값을 직접 생성한 뒤 Baron OIDC로 리다이렉트해야 합니다.",
|
||||
)}
|
||||
</p>
|
||||
{!hasValidAutoLoginUrl ? (
|
||||
<p className="text-xs text-destructive">
|
||||
{t(
|
||||
"msg.dev.clients.general.auto_login.invalid_url",
|
||||
"자동 로그인 URL 형식이 올바르지 않습니다. http 또는 https 주소를 입력하세요.",
|
||||
)}
|
||||
</p>
|
||||
) : null}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* 6. Security Settings */}
|
||||
<Card className="glass-panel">
|
||||
<CardHeader className="pb-3">
|
||||
<CardTitle className="text-xl font-bold">
|
||||
|
||||
Reference in New Issue
Block a user