1
0
forked from baron/baron-sso

feat: 커스텀 필드 기반 로그인 ID 연동 기능 추가 (#440)

- Kratos Identity 스키마에 로그인 전용 `id` 속성 추가
- 테넌트 Config의 `loginIdField` 설정에 따라 User의 `login_id` 및 Kratos `traits.id` 동기화 로직 구현
- Admin UI 테넌트 스키마 설정 내 '로그인 ID로 사용' 체크박스 추가
- Admin UI 사용자 생성/수정/조회 화면에 로그인 ID 관리 필드 및 컬럼 반영
- Userfront 로그인 화면 접속 시 테넌트 설정에 따라 동적 로그인 ID 라벨 적용
- 관련 다국어(ko/en) 번역 추가 및 로그인 ID 설계 문서 업데이트
This commit is contained in:
2026-03-25 15:27:44 +09:00
parent 8cadd82a2b
commit d10f80d41d
18 changed files with 799 additions and 420 deletions

View File

@@ -34,6 +34,7 @@ type SchemaField = {
adminOnly: boolean;
validation?: string;
unsigned?: boolean;
isLoginId?: boolean;
};
function createFieldId() {
@@ -96,6 +97,8 @@ export function TenantSchemaPage() {
useEffect(() => {
const rawSchema = tenantQuery.data?.config?.userSchema;
const loginIdField = tenantQuery.data?.config?.loginIdField;
if (Array.isArray(rawSchema)) {
setFields(
rawSchema.map((field) => ({
@@ -115,19 +118,23 @@ export function TenantSchemaPage() {
validation:
typeof field?.validation === "string" ? field.validation : "",
unsigned: Boolean(field?.unsigned),
isLoginId: field?.key === loginIdField,
})),
);
}
}, [tenantQuery.data]);
const updateMutation = useMutation({
mutationFn: (newFields: SchemaField[]) =>
updateTenant(tenantId, {
mutationFn: (newFields: SchemaField[]) => {
const loginIdField = newFields.find((f) => f.isLoginId)?.key || "";
return updateTenant(tenantId, {
config: {
...tenantQuery.data?.config,
userSchema: newFields,
loginIdField: loginIdField,
},
}),
});
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["tenant", tenantId] });
toast.success(
@@ -334,6 +341,26 @@ export function TenantSchemaPage() {
)}
</span>
</label>
<label className="flex items-center gap-2 cursor-pointer">
<input
type="checkbox"
checked={field.isLoginId}
onChange={(e) => {
const newFields = fields.map((f, i) => ({
...f,
isLoginId: i === index ? e.target.checked : false,
}));
setFields(newFields);
}}
className="w-4 h-4 rounded border-gray-300 text-primary focus:ring-primary"
/>
<span className="text-sm font-medium text-blue-600">
{t(
"ui.admin.tenants.schema.field.is_login_id",
"로그인 ID로 사용",
)}
</span>
</label>
{(field.type === "number" || field.type === "float") && (
<label className="flex items-center gap-2 cursor-pointer">
<input