1
0
forked from baron/baron-sso

feat: implement multi-identifier architecture (Issue #496)

- Database: Add user_login_ids table for 1:N identifier mapping and remove legacy login_id column
- Kratos: Update identity schema to use custom_login_ids array instead of a single id trait
- Backend: Implement syncCustomLoginIDs to collect isLoginId fields across tenant schemas
- Backend: Add backtracking logic to auto-assign session tenant based on used login identifier
- Backend: Add 409 Conflict exception handling for Create/Update operations
- AdminFront: Refactor UserDetailPage to a tabbed grid layout (Info, Tenants, Security)
- AdminFront: Show '로그인 ID' badge on tenant schema fields used for authentication
- UserFront: Remove legacy optional 'Login ID' input from signup flow
- Tests: Add multi-identifier repository tests and update handler tests
This commit is contained in:
2026-04-02 16:07:33 +09:00
parent 71a006cd7b
commit b582c82c6f
25 changed files with 1154 additions and 1160 deletions

View File

@@ -43,7 +43,7 @@ func (o *OryProvider) Name() string {
func (o *OryProvider) GetMetadata() (*domain.IDPMetadata, error) {
return &domain.IDPMetadata{
SupportedFields: []string{
"id", "login_id", "email", "name", "phone_number",
"id", "custom_login_ids", "login_id", "email", "name", "phone_number",
"grade", "department", "affiliationType", "companyCode",
},
}, nil
@@ -67,6 +67,21 @@ func (o *OryProvider) CreateUser(user *domain.BrokerUser, password string) (stri
return "", fmt.Errorf("ory provider: identity already exists for email=%s", user.Email)
}
// [New] Check all custom login IDs for collisions
for _, lid := range user.CustomLoginIDs {
if lid == "" {
continue
}
existing, err := o.findIdentityID(lid)
if err != nil {
return "", fmt.Errorf("ory provider: search identity failed for %s: %w", lid, err)
}
if existing != "" {
return "", fmt.Errorf("ory provider: identifier %s already exists", lid)
}
}
// [Legacy] check single LoginID
if user.LoginID != "" {
existingLoginID, err := o.findIdentityID(user.LoginID)
if err != nil {
@@ -91,13 +106,20 @@ func (o *OryProvider) CreateUser(user *domain.BrokerUser, password string) (stri
"email": user.Email,
"name": user.Name,
}
if user.LoginID != "" {
traits["id"] = user.LoginID
if len(user.CustomLoginIDs) > 0 {
traits["custom_login_ids"] = user.CustomLoginIDs
} else if user.LoginID != "" {
traits["custom_login_ids"] = []string{user.LoginID}
}
if user.PhoneNumber != "" {
traits["phone_number"] = user.PhoneNumber
}
for k, v := range user.Attributes {
// [SoT Fix] Don't let attributes overwrite core traits or use old 'id' trait
if k == "id" || k == "email" || k == "custom_login_ids" {
continue
}
traits[k] = v
}