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:
@@ -80,6 +80,7 @@ type UserProfileResponse struct {
|
||||
AffiliationType string `json:"affiliationType"`
|
||||
CompanyCode string `json:"companyCode,omitempty"`
|
||||
TenantID *string `json:"tenantId,omitempty"` // 추가
|
||||
SessionTenantID *string `json:"sessionTenantId,omitempty"` // [New] 로그인에 사용된 식별자 기반 테넌트
|
||||
RelyingPartyID *string `json:"relyingPartyId,omitempty"` // 추가
|
||||
Metadata map[string]any `json:"metadata,omitempty"`
|
||||
Tenant *Tenant `json:"tenant,omitempty"`
|
||||
|
||||
@@ -12,11 +12,12 @@ var ErrNotSupported = errors.New("idp: not supported")
|
||||
// BrokerUser is the standard user model used within Baron SSO business logic.
|
||||
// It defines the canonical set of fields that must be supported by any underlying IDP.
|
||||
type BrokerUser struct {
|
||||
ID string `json:"id" required:"true"`
|
||||
Email string `json:"email" required:"true"`
|
||||
LoginID string `json:"login_id"`
|
||||
Name string `json:"name"`
|
||||
PhoneNumber string `json:"phone_number"`
|
||||
ID string `json:"id" required:"true"`
|
||||
Email string `json:"email" required:"true"`
|
||||
LoginID string `json:"login_id"`
|
||||
CustomLoginIDs []string `json:"custom_login_ids"` // [New] 다중 로그인 ID
|
||||
Name string `json:"name"`
|
||||
PhoneNumber string `json:"phone_number"`
|
||||
// Attributes stores custom user attributes.
|
||||
// The "required_keys" tag specifies which keys MUST be present in the IDP's schema support.
|
||||
Attributes map[string]interface{} `json:"attributes" required_keys:"grade,department"`
|
||||
|
||||
@@ -35,14 +35,13 @@ func NormalizeRole(role string) string {
|
||||
type User struct {
|
||||
ID string `gorm:"primaryKey;type:uuid;default:gen_random_uuid()" json:"id"`
|
||||
Email string `gorm:"uniqueIndex;not null" json:"email"`
|
||||
LoginID string `gorm:"column:login_id;uniqueIndex:idx_tenant_login_id" json:"loginId"`
|
||||
PasswordHash *string `gorm:"column:password_hash" json:"-"`
|
||||
Name string `gorm:"column:name;not null" json:"name"`
|
||||
Phone string `gorm:"column:phone" json:"phone"`
|
||||
Role string `gorm:"column:role;default:'user';not null" json:"role"` // super_admin, tenant_admin, rp_admin, user
|
||||
AffiliationType string `gorm:"column:affiliation_type" json:"affiliationType"`
|
||||
CompanyCode string `gorm:"column:company_code;index" json:"companyCode"`
|
||||
TenantID *string `gorm:"column:tenant_id;type:uuid;index;uniqueIndex:idx_tenant_login_id" json:"tenantId,omitempty"`
|
||||
TenantID *string `gorm:"column:tenant_id;type:uuid;index" json:"tenantId,omitempty"`
|
||||
Tenant *Tenant `gorm:"foreignKey:TenantID" json:"tenant,omitempty"`
|
||||
RelyingPartyID *string `gorm:"column:relying_party_id;type:uuid;index" json:"relyingPartyId,omitempty"` // RP Admin용
|
||||
Department string `gorm:"column:department" json:"department"`
|
||||
@@ -53,6 +52,18 @@ type User struct {
|
||||
CreatedAt time.Time `gorm:"column:created_at" json:"createdAt"`
|
||||
UpdatedAt time.Time `gorm:"column:updated_at" json:"updatedAt"`
|
||||
DeletedAt gorm.DeletedAt `gorm:"column:deleted_at;index" json:"-"`
|
||||
|
||||
// Multiple identifiers support
|
||||
UserLoginIDs []UserLoginID `gorm:"foreignKey:UserID" json:"userLoginIds,omitempty"`
|
||||
}
|
||||
|
||||
// UserLoginID represents multiple custom identifiers for a user
|
||||
type UserLoginID struct {
|
||||
ID string `gorm:"primaryKey;type:uuid;default:gen_random_uuid()" json:"id"`
|
||||
UserID string `gorm:"type:uuid;not null;index" json:"userId"`
|
||||
TenantID string `gorm:"type:uuid;not null;index" json:"tenantId"` // 발급 테넌트
|
||||
FieldKey string `gorm:"not null" json:"fieldKey"` // 스키마 필드 키 (예: emp_id)
|
||||
LoginID string `gorm:"uniqueIndex;not null" json:"loginId"` // 실제 값 (예: EMP001)
|
||||
}
|
||||
|
||||
// BeforeCreate hook to generate UUID if not present
|
||||
|
||||
Reference in New Issue
Block a user