# 멀티 IDP 아키텍처 및 마이그레이션 전략

본 문서는 Primary IDP 변경(예: Descope → Hydra/Authentik) 시 시스템의 유연성을 보장하기 위한 아키텍처 설계 전략을 기술합니다.

## 핵심 전략

1.  **Broker 패턴 고도화**: 백엔드가 모든 인증 요청을 중계하며, 클라이언트는 특정 IDP에 종속되지 않습니다.
2.  **추상화 계층 (Abstraction Layer)**: 비즈니스 로직은 `IdentityProvider` 인터페이스에만 의존하며, 구체적인 구현체(Descope, Hydra 등)는 알 필요가 없습니다.
3.  **식별자 분리 (Identifier Decoupling)**: 외부 IDP의 `sub` 값과 내부 시스템의 `user_id`를 분리하여 매핑합니다.

## 상세 설계

### 1. IdentityProvider 인터페이스 확장
단순 메타데이터 조회를 넘어, 인증의 핵심 라이프사이클을 모두 포함하도록 인터페이스를 확장해야 합니다.

```go
type IdentityProvider interface {
    // 메타데이터 및 스키마 검증
    Name() string
    GetMetadata() (*IDPMetadata, error)

    // 핵심 인증/인가 기능
    SignUp(ctx context.Context, user *BrokerUser, password string) (*AuthResult, error)
    Login(ctx context.Context, loginID, password string) (*AuthResult, error)
    VerifyToken(ctx context.Context, token string) (*TokenInfo, error)
    
    // 사용자 관리
    GetUser(ctx context.Context, id string) (*BrokerUser, error)
    UpdateUser(ctx context.Context, user *BrokerUser) error
    DeleteUser(ctx context.Context, id string) error
}
```

### 2. Provider Factory 패턴
설정(`config.yaml` 또는 환경변수)에 따라 사용할 IDP 구현체를 런타임에 결정합니다.

```go
func NewIdentityProvider(config Config) (domain.IdentityProvider, error) {
    switch config.IDPType {
    case "descope":
        return service.NewDescopeProvider(config.Descope)
    case "hydra":
        return service.NewHydraProvider(config.Hydra)
    case "authentik":
        return service.NewAuthentikProvider(config.Authentik)
    default:
        return nil, fmt.Errorf("unsupported IDP type: %s", config.IDPType)
    }
}
```

### 3. 식별자 매핑 (ID Mapping)
IDP 변경 시 가장 큰 문제는 사용자 고유 ID(`sub`)가 바뀌는 것입니다. 이를 해결하기 위해 Baron SSO 내부 전용 UUID를 사용하고 매핑 테이블을 둡니다.

*   **User Table (Baron Internal)**: `id` (UUID), `email`, ...
*   **IDP Link Table**: `baron_user_id`, `idp_provider` (e.g., descope), `idp_subject_id` (e.g., U12345)

## 구현 예시 (Descope)

현재 단계에서 즉시 사용 가능한 `DescopeProvider`의 구현 예시입니다.

```go
// DescopeProvider는 IdentityProvider 인터페이스를 구현합니다.
type DescopeProvider struct {
    Client *client.DescopeClient
}

func (d *DescopeProvider) SignUp(ctx context.Context, user *domain.BrokerUser, password string) (*domain.AuthResult, error) {
    // BrokerUser를 Descope User 객체로 변환
    descopeUser := &descope.User{
        Name:  user.Name,
        Email: user.Email,
        Phone: user.PhoneNumber,
        CustomAttributes: user.Attributes,
    }

    // SDK 호출
    authInfo, err := d.Client.Auth.Password().SignUp(ctx, user.Email, descopeUser, password)
    if err != nil {
        return nil, err\n    }

    // 결과 반환
    return &domain.AuthResult{
        UserID: authInfo.User.UserID,
        Token:  authInfo.SessionToken.JWT,
    }, nil
}
```

## 향후 지원 후보 (Candidates)

*   **Ory Hydra**: 자체적인 OAuth2/OIDC 서버 구축이 필요할 때 적합. 높은 커스터마이징 가능.
*   **Authentik**: 오픈소스 IDP로, 설치형(Self-hosted) 환경에서 강력한 기능을 제공.
