1
0
forked from baron/baron-sso
Files
baron-sso/README.md

32 KiB

Baron SSO

Code Check Biome userfront e2e fast userfront e2e full adminfront coverage devfront coverage orgfront coverage

badge는 Code Check가 dev 브랜치에서 갱신합니다. 최신 HTML/LCOV/JSON summary는 Gitea Code Checkfront-vitest-coverage-report artifact에서 확인할 수 있습니다.

Baron 로그인은 화이트 라벨링된 가족사의 모든 소프트웨어 Auth를 총괄하는 사용자 인증/인가 허브입니다.

📂 프로젝트 구조 (Project Structure)

baron_sso/
├── backend/            # Go Fiber 애플리케이션
│   ├── cmd/server/     # 진입점 (Entry point)
│   ├── internal/       # 도메인, 핸들러, 저장소(Repository)
│   └── Dockerfile
├── userfront/          # Flutter 애플리케이션
│   ├── src/            # UI 및 로직
│   └── pubspec.yaml
├── adminfront/         # React 기반 관리
│   ├── src/            # UI 및 로직
│   └── pubspec.yaml
├── gateway/            # Nginx 기반 Gateway (UserFront 프록시)
├── compose.ory-stack.yaml  # DB 서비스 (Postgres, ClickHouse)
├── compose.infra.yaml  # DB 서비스 (Postgres, ClickHouse)
├── docker-compose.yaml # 앱 서비스 (Front, Back)
├── .env.sample         # 환경 설정 템플릿
└── README.md           # 본 파일
  • Ory Stack으로 모든 구성요소를 self-hosting 합니다.
  • Backend는 Go (Fiber)로 구성된 Ory Stack의 유일한 Command 전송 포인트입니다. 모든 Command는 ClickHouse로 강제 전송되며 Audit Log 시스템을 구성합니다.
  • Front는 Backend를 통해서만 연동하며 자체가 Ory Stack의 RP기도 합니다. 크게 3개 계층으로 분리됩니다.
    • UserFront: Flutter로 구현된 커스텀 UI를 통해 매끄러운 사용자 경험을 보장합니다.
      • 로그인 : 비밀번호, SMS, QR 스캔 등의 수단으로 로그인 가능
      • 향후 모바일 앱 지원으로 인증 Push 승인 등 MFA 확장 (예정)
      • 정보변경, 앱 연동 이력 확인 등 개인화 기능
      • 사용 가능한 앱 리스트 (예정)
    • AdminFront: 사용자 관리 등 Admin 기능
    • DevFront: RP 관리 등 개발자 기능

🏗 아키텍처 (Architecture)

0. Ory Stack

  • Ory Kratos: 사용자 인증/계정 관리(Identity).
  • Ory Hydra: OAuth2/OIDC 발급 및 토큰 관리.
  • Ory Keto: 권한/정책 기반 접근 제어.
  • Oathkeeper: 인증/인가 프록시 및 라우팅 게이트웨이.
flowchart
    subgraph Edge
      OK["Oathkeeper<br/>(Only Public Entry)"]
    end

    subgraph App
      BE["Backend<br/>(Only Upstream)"]
    end

    subgraph OryStack
      KR[Kratos]
      HY[Hydra]
      KE[Keto]
      KR --- HY --- KE
    end
    BE -->|Command| OryStack
    OK -->|Query| KR
    OK -->|Query| HY
    OK -->|Query| KE

1. Backend (Go Fiber)

  • Language: Go 1.26.2+
  • Framework: Fiber v2.25+
  • Database:
    • ClickHouse: 감사 로그 (고성능 데이터 수집)
    • PostgreSQL: 메타데이터 저장소 (Primary)
  • Features:
    • 인증용 SMS 발송 등 Ory-Stack으로 구현 어려운 부분 직접 구현
    • POST /api/v1/audit: 감사 로그 수집 API
    • userfront가 바라보는 backend

2. UserFront(Flutter Web/App)

  • Framework: Flutter 3.38.0+
  • Key Packages: flutter_riverpod, go_router
  • Features:
    • 탭 기반 로그인 UI (비밀번호 기반 / 링크 기반 / QR 기반 등)

3. adminfront(Web)

  • Framework: Vite, React 19+, Shadcn/ui 등
  • Features:
    • 사용자 관리, 권한 부여 등 관리자 기능
    • 앱 별 사용량(호출량) 등 통계
    • 핵심 Audit 대상

4. devfront(Web)

  • Framework: Vite, React 19+, Shadcn/ui 등
  • Features:
    • RP 등록 및 관리
    • RP별 Consent 관리

관리 데이터 Export/Import 정책

AdminFront의 테넌트와 사용자 export/import는 운영자가 CSV를 직접 검토하고 재반입할 수 있는 흐름을 기준으로 설계합니다. 기본 원칙은 내부 UUID를 불필요하게 노출하지 않고, 사람이 이해하기 쉬운 slug와 이름을 우선 사용하는 것입니다.

공통 원칙

  • CSV는 Excel 호환을 위해 UTF-8 BOM을 포함해 내려받습니다.
  • 기본 export는 시스템 내부 ID를 제외합니다.
  • 같은 데이터를 정확히 재동기화해야 하는 운영 작업에서는 includeIds=true 옵션으로 내부 ID 컬럼을 포함할 수 있습니다.
  • import는 preview/검토 단계를 거친 뒤 실행하는 것을 기본으로 합니다.
  • 기존 데이터와 충돌 가능성이 있는 row는 자동 적용하지 않고 관리자 선택 또는 확인 상태로 표시합니다.
  • 삭제는 export/import로 암묵 처리하지 않습니다. 삭제가 필요하면 별도 삭제 기능을 사용합니다.

Tenant Export

  • 기본 컬럼은 운영자가 다시 import하기 쉬운 형태를 유지합니다.
  • includeIds=false가 기본이며, 이 경우 내부 tenant_id는 제외합니다.
  • includeIds=true를 사용하면 기존 테넌트 update 또는 staging/production 간 매핑 확인에 필요한 ID를 포함합니다.
  • 주요 의미:
    • tenant_id: 내부 UUID. 기본 export에서는 제외됩니다.
    • name: 테넌트 표시 이름입니다.
    • type: PERSONAL, COMPANY, COMPANY_GROUP, USER_GROUP 중 하나입니다.
    • parent_tenant_id: 상위 테넌트 내부 ID입니다.
    • parent_tenant_slug: 상위 테넌트를 slug로 연결할 때 사용합니다.
    • slug: 운영상 사람이 다루는 테넌트 식별자입니다.
    • memo: 설명 또는 비고입니다.
    • email_domain: 테넌트에 연결된 이메일 도메인입니다. 여러 도메인은 ;, ,, 줄바꿈으로 구분할 수 있습니다.

Tenant Import

  • 필수 컬럼은 name, type, slug입니다.
  • 허용되는 header alias:
    • tenant_id: id, tenantid, tenant_id
    • parent_tenant_id: parentid, parent_id, parenttenantid, parent_tenant_id
    • parent_tenant_slug: parenttenantslug, parent_tenant_slug
    • memo: memo, description
    • email_domain: email-domain, emaildomain, email_domain, domain, domains
  • tenant_id가 있고 기존 테넌트가 있으면 update 대상으로 봅니다.
  • tenant_id가 없으면 slug 기준으로 기존 테넌트를 찾고, 없으면 신규 생성 후보로 봅니다.
  • parent_tenant_slug가 같은 import 파일 안에 있으면 부모 row를 먼저 처리하도록 정렬합니다.
  • import preview는 이름/slug 유사도 기반 후보를 보여주며, 관리자가 기존 테넌트 사용, 신규 생성, skip 중 선택할 수 있어야 합니다.
  • 외부 시스템에서 가져온 tenant_id처럼 현재 DB에 없는 ID는 충돌로 표시하고, 관리자가 새 slug 또는 기존 테넌트 매핑을 결정해야 합니다.

User Export

  • 기본 컬럼은 Email, Name, Phone, Status, tenant_slug, Position, JobTitle, CreatedAt입니다.
  • includeIds=true이면 user_id, tenant_id를 함께 포함합니다.
  • 사용자 role은 export 기본 컬럼에서 제외합니다. role은 일괄 변경의 실수 위험이 크므로 명시적 관리 화면 또는 별도 정책으로 다룹니다.
  • 사용자 metadata는 Meta:<key> 컬럼으로 뒤에 추가됩니다.
  • includeIds=false일 때는 id, user_id, tenant_id, tenantid 성격의 metadata key를 export에서 제외합니다.
  • tenant admin의 export는 관리 가능한 테넌트 범위로 제한됩니다.

User Import

  • 사용자 CSV의 기본 컬럼은 email, name, phone, role, tenant_slug, department, position, jobTitle입니다.
  • emailname은 CSV parsing 단계의 필수값입니다.
  • backend 생성 단계에서는 tenantSlug도 필수입니다.
  • tenant, tenant_slug, companyCode header는 사용자 소속 테넌트 slug로 매핑됩니다.
  • tenant_id, tenant_name, tenant_type, parent_tenant_id, parent_tenant_slug, parent_tenant_name, tenant_memo, email_domain 컬럼이 있으면 사용자 import 과정에서 필요한 테넌트 생성/매핑 preview에 사용합니다.
  • 위 기본 컬럼에 속하지 않는 컬럼은 사용자 metadata로 들어갑니다.
  • 테넌트에 userSchema가 있으면 import 중 metadata required/validation/loginId 규칙을 적용합니다.
  • 테넌트 schema에서 isLoginId로 지정된 metadata 값은 custom login ID로 동기화하며, 이메일/전화번호/예약어와 충돌하지 않아야 합니다.

한맥가족 User Import Email 정책

  • 전체 시스템에서 users.email은 unique입니다.
  • active, temporary_leave, suspended, preboarding, baron_guest, extended_leave, archived 등 모든 사용자 상태가 unique 검사 대상입니다. 특히 preboarding, baron_guest, archived 사용자는 email/local-part 선점 대상입니다.
  • 한맥가족 테넌트 root(hanmac-family)와 그 하위 subtree에서는 이메일 도메인과 무관하게 @ 앞 local-part도 unique 해야 합니다.
  • 예: han@hanmaceng.co.kr가 한맥가족 구성원으로 있으면 han@samaneng.com은 한맥가족 구성원으로 생성할 수 없습니다.
  • email 값이 @hanmaceng.co.kr처럼 도메인만 있으면 import preview에서 이름 기반 local-part를 제안합니다.
  • 이름 기반 local-part 기본 규칙은 이름 부분 초성 + 성 로마자입니다.
    • 예: 한치영 -> 치영의 초성 c + y + 성 han -> cyhan
  • 이미 cyhan, cyhan1이 있으면 다음 후보인 cyhan2를 제안합니다.
  • 외부 로마자화 패키지는 backend 의존성으로 추가하지 않고, 내부 한글 음절 분해와 성씨/초성 매핑을 사용합니다.
  • import preview의 row 상태:
    • valid: unique와 이름 기반 권장 규칙을 모두 만족합니다.
    • suggested: 도메인만 있거나 suffix 제안이 필요한 row입니다.
    • needsReview: 이름 매핑이 애매해 관리자가 직접 확인해야 합니다.
    • ruleMismatch: 최종 local-part가 이름 이니셜 + 성 + 숫자 suffix 규칙과 다릅니다. 예외 진행은 가능하지만 관리자에게 표시해야 합니다.
    • blockingError: local-part 중복, email 형식 오류, 필수값 누락처럼 생성을 차단해야 하는 상태입니다.
  • 단건 사용자 생성은 한맥가족 local-part 중복 시 자동 제안하지 않고 409 Conflict로 차단합니다.
  • bulk import는 preview에서 제안/수정된 최종 email을 사용하되, backend가 생성 직전에 다시 unique 규칙을 검증합니다.

User Status 정책

상태 표시명 Baron 사용 Works 처리 일반 조직도
active 재직 가능 생성/갱신 노출
temporary_leave 단기휴무 가능 계정 유지 노출
suspended 정지 불가 suspend 노출
preboarding 입사대기 불가 생성 안 함 비노출
baron_guest Baron 게스트 가능 생성 금지, 기존 계정 delete/deprovision 비노출
extended_leave 장기휴직 불가 delete/deprovision 비노출
archived 보관 불가 delete/deprovision 비노출
  • 기존 inactive 입력은 preboarding으로, leave_of_absence 입력은 temporary_leave로 호환 처리합니다.
  • 이슈 #862의 초기 명칭 baron_only는 구현 명칭으로 사용하지 않고 baron_guest로 정리합니다.
  • backend bootstrap은 남아 있는 legacy users.status 값을 inactive -> preboarding, leave_of_absence -> temporary_leave, baron_only -> baron_guest로 자동 정규화합니다.
  • archived 사용자는 과거 이력 보존용 계정이며 AdminFront 같은 관리자 화면에서만 감사/운영/중복 확인 목적으로 조회할 수 있습니다.

4. 주요 시나리오 (Core Scenarios)

  1. Same Browser SSO: Baron 로그인 서비스에 로그인된 상태에서 런처를 통해 타 앱/서비스로 이동 (자동 로그인). 1.1. 단 약관동의(Consent) 이력이 없으면 Consent 단계로 이동
  2. Cross-Device Auth: 이메일 SMS 등의 수단으로 링크를 전달받고 해당 링크를 사용자가 클릭하면 최초 로그인 요청한 세션이 활성화 2.1 향후 App Push 등 2차 인증 강화수단 검토 필요
  3. QR Login: 최초 진입 시 사전 로그인되어 있는 웹/앱을 이용해 QR 코드를 스캔하여, QR코드가 로딩된 Device를 로그인 상태로 전환

5. Headless Login ID/Password Flow

  • 목적: headless login을 허용한 클라이언트가 자체 로그인 화면에서 ID/password를 수집하되, Baron Backend가 OIDC 로그인 흐름만 계속 진행하고 RP에는 sessionJwt를 직접 넘기지 않습니다.
  • 대상 엔드포인트: POST /api/v1/auth/headless/password/login
  • 관련 구현:
    • backend/internal/handler/auth_handler.go
    • backend/internal/domain/hydra_models.go
    • backend/internal/handler/auth_handler_login_test.go

호출 순서

  1. RP 브라우저가 Hydra Public의 /oauth2/auth를 호출해 OIDC 인증을 시작합니다.
  2. Hydra가 로그인 단계로 넘긴 login_challenge를 RP가 확보합니다.
  3. RP backend가 자기 private key로 client_assertion JWT를 서명합니다.
  4. RP backend가 Baron Backend의 POST /api/v1/auth/headless/password/loginclient_id, client_assertion, login_challenge, loginId, password를 전송합니다.
  5. Baron Backend가 Hydra login request와 RP 설정을 검증한 뒤 Kratos sign-in 및 Hydra login accept를 수행합니다.
  6. 성공 시 Baron Backend는 redirectTo만 반환하고, RP 브라우저는 그 URL로 이동해 OIDC 흐름을 이어갑니다.

요청 바디

{
  "client_id": "headless-login-client",
  "client_assertion": "<signed-jwt>",
  "login_challenge": "<hydra-login-challenge>",
  "loginId": "employee001",
  "password": "secret"
}

성공 응답

{
  "status": "ok",
  "provider": "ory",
  "redirectTo": "https://rp.example.com/callback?code=..."
}

RP / Hydra 선행 조건

  • Hydra login request의 client.client_id와 요청 바디의 client_id가 반드시 같아야 합니다.
  • client가 headless login 선행 조건을 만족해야 합니다.
    • headless_token_endpoint_auth_method == "private_key_jwt" 또는 top-level token_endpoint_auth_method == "private_key_jwt"
    • headless_jwks_uri가 존재해야 합니다.
    • inline headless_jwks는 더 이상 지원하지 않습니다.
  • headless_login_enabled == true가 필요합니다.
  • metadata.status == "inactive"인 client는 차단됩니다.

client_assertion 규칙

  • 구현상 client_assertion은 현재 필수입니다.
  • 허용 서명 알고리즘:
    • RS256, RS384, RS512
    • PS256, PS384, PS512
    • ES256, ES384, ES512
    • EdDSA
  • JWT claim의 isssub는 모두 client_id와 같아야 합니다.
  • exp는 현재 시각 이후여야 합니다.
  • nbf, iat가 있으면 미래 시각이면 안 됩니다.
  • aud는 다음 둘 중 하나와 일치해야 합니다.
    • https://<backend-origin>/api/v1/auth/headless/password/login
    • /api/v1/auth/headless/password/login
  • 서명 검증용 public key는 headless_jwks_uri에서만 읽습니다.

내부 JWKS 캐시 정책

  • Baron Backend는 headless_jwks_uri를 직접 외부 스펙으로 저장하고, 실제 JWKS 문서는 내부 캐시에 저장해 사용합니다.
  • 등록/수정 이후에는 내부 캐시 동기화를 시도하고, 성공/실패 상태를 DevFront에서 확인할 수 있습니다.
  • 로그인 시 재조회는 다음 조건으로 제한합니다.
    • 캐시에 kid가 없을 때
    • kid는 있지만 서명 검증이 실패할 때
    • 캐시 TTL이 만료되었을 때
  • 그 외에는 내부 캐시를 사용합니다.
  • 백그라운드 worker가 TTL보다 짧은 주기로 jwksUri를 선제 점검해 첫 사용자 실패를 줄입니다.

DevFront 운영 액션

  • Settings > Public Key Registration 카드에서 다음 정보를 확인할 수 있습니다.
    • 최근 JWKS 캐시 갱신 시각
    • 최근 검증 성공 시각
    • 최근 에러와 연속 실패 횟수
    • 현재 cached kid 목록
    • 파싱된 key summary
      • kid
      • kty
      • use
      • alg
      • RSA key의 n preview (앞/뒤 일부만 표시)
  • 수동 운영 액션:
    • Refresh JWKS Cache
    • Revoke JWKS Cache
  • RP가 키를 교체했으면 실제 트래픽 전에 Refresh JWKS Cache를 먼저 호출하는 것을 권장합니다.

일반 로그인과의 차이

  • POST /api/v1/auth/password/login
    • UserFront 기본 비밀번호 로그인용입니다.
    • login_challenge가 없으면 sessionJwt를 반환합니다.
    • login_challenge가 있으면 Hydra accept 후 redirectTo를 반환합니다.
  • POST /api/v1/auth/headless/password/login
    • headless login 허용 클라이언트 전용입니다.
    • client_assertion 검증이 추가됩니다.
    • 항상 sessionJwt 없이 redirectTo만 반환합니다.

실패 패턴 요약

  • 400 bad_request
    • 필수 필드 누락
    • client_assertion 누락
  • 401 invalid_client_assertion
    • jwksUri 조회 실패
    • 서명 불일치
    • aud/iss/sub/exp 검증 실패
  • 401 invalid_client_assertion with explicit message
    • Headless login requires jwksUri. Inline jwks is not supported.
    • Configured jwksUri returned no keys for headless login.
    • Failed to refresh headless login jwks from jwksUri.
  • 403 forbidden
    • client_id 불일치
    • headless_login_enabled 미설정
    • inactive client
  • 401 password_or_email_mismatch
    • 사용자 인증 실패

전체 연결 구조도

flowchart TD
    subgraph Clients ["External Clients"]
        AF[adminfront]
        DF[devfront]
        UF["userfront"]
        DS["일반SW"]
    end

    subgraph AppService ["Control Plane"]
        BE["Backend (Command/Audit Controller)"]
    end

    subgraph OryBundle ["Ory Deployment Stack"]
        direction TB
        OK["Oathkeeper (Public Proxy/OIDC)"]
        
        subgraph OryEngines ["Ory Services"]
            direction LR
            HY["Hydra"]
            KR["Kratos"]
            KE["Keto"]
        end
        
        ICH[(Internal Clickhouse)]
        
        %% Internal Flow within Bundle
        OK -->|Routing/Queries| OryEngines
        OK -.->|Access/Usage Log| ICH
    end

    subgraph AuditDB ["Audit Storage"]
        ECH[(External Clickhouse)]
    end

    %% Key Command Path
    AF & DF & UF & DS ==>|Actions / Commands| BE
    
    %% Backend Responsibilities
    BE -->|Admin/State Control| OryEngines
    BE -.->|Mandatory Audit Log| ECH

    %% Connection Note (Hidden flow mentioned in logic)
    %% OK is technically the entry for OIDC, but removed as per request

    %% Styles
    style OryBundle fill:#f8f9fa,stroke:#333,stroke-width:2px
    style BE fill:#bbf,stroke:#333,stroke-width:2px
    style ECH fill:#fdd,stroke:#333
    style ICH fill:#dfd,stroke:#333
    style OK fill:#f9f,stroke:#333
    style OryEngines fill:#fff,stroke:#999,stroke-dasharray: 5 5

Kratos가 사용자 SoT이며 Hydra는 순수 OIDC 토큰 엔진입니다. 비지니스로직은 Backend를 통해서, 기본 인증 로직은 Ory Stack을 통해 진행됩니다.


🚀 시작하기 (Getting Started)

사전 요구사항 (Prerequisites)

  • Docker & Docker Compose
  • Flutter SDK (로컬 개발용, 3.38.0+)
  • Go (로컬 백엔드 개발용)

환경 설정 (Environment Setup)

  1. 예제 환경 설정 파일을 복사합니다.
    cp .env.sample .env
    
  2. .env를 작성합니다. (아래 작성 규칙 필수)

.env 작성 규칙 (중요)

  • KEY=value 한 줄만 사용하고, 값 뒤에 같은 줄 주석을 붙이지 않습니다.
  • 주석이 필요하면 반드시 윗줄에 별도 주석 라인으로 작성합니다.
  • URL 값 끝에 공백이 들어가면 Hydra/Kratos 기동 실패로 이어질 수 있습니다.

잘못된 예:

USERFRONT_URL=https://sso.example.com # 이렇게 같은 줄 주석 금지

올바른 예:

# UserFront 공개 URL
USERFRONT_URL=https://sso.example.com

.env 핵심 변수 가이드

  • IDP_PROVIDER: 기본 ory
  • USERFRONT_URL: 브라우저 기준 공개 도메인 (예: https://sso.example.com)
  • OATHKEEPER_PUBLIC_URL: 보통 ${USERFRONT_URL}
  • HYDRA_PUBLIC_URL: 보통 ${OATHKEEPER_PUBLIC_URL}/oidc
  • KRATOS_BROWSER_URL: 보통 ${OATHKEEPER_PUBLIC_URL}/auth
  • KRATOS_UI_URL: UserFront UI URL (로컬 예: http://localhost:5000)
  • ADMINFRONT_CALLBACK_URLS: 콤마 구분 콜백 목록 (예: http://localhost:5173/auth/callback)
  • DEVFRONT_CALLBACK_URLS: 콤마 구분 콜백 목록 (예: http://localhost:5174/auth/callback)
    • 주의: callback URL 끝에 /가 붙으면 make validate-auth-config에서 실패 처리됩니다.
  • KRATOS_ALLOWED_RETURN_URLS_EXTRA: 추가 허용 return URL (선택)
    • 빈값: []
    • 다중값: ["https://a.example.com/callback","https://b.example.com/callback"] 또는 https://a.example.com/callback,https://b.example.com/callback
  • KRATOS_ALLOWED_RETURN_URLS_JSON: stage/prod에서 권장하는 전체 허용 return URL 목록
    • 공개 도메인, /ko, /en, /auth/callback, /ko/auth/callback, /en/auth/callback, 각 front callback을 포함해야 합니다.
  • CLIENT_LOG_DEBUG: 클라이언트 로그 디버그 모드 강제 (기본: 비운영 true, 운영 false)
    • 운영(APP_ENV=production|prod)에서 true|1|on|yes 설정 시 INFO/DEBUG 클라이언트 로그 수집 허용
    • 미설정(기본) 시 운영에서는 WARN/ERROR만 수집
  • BACKEND_LOG_LEVEL: Backend slog 레벨 override (선택)
    • 허용 값: debug, info, warn, error
    • 미설정 시 APP_ENV 기준으로 결정됩니다.
      • dev|local|development: debug
      • 그 외(stage, production, prod 등): info
  • USERFRONT_DEBUG_LOG: UserFront 측 디버그 로그 fallback 플래그
    • CLIENT_LOG_DEBUG가 없을 때만 UserFront에서 대체로 참조

클라이언트 로그 정책 (중요)

  • 기본 원칙: 운영 환경에서는 민감정보 보호를 우선하며, 과도한 로그 수집을 제한합니다.
  • 환경별 동작:
    • 비운영(dev/local/stage 등): 디버그 로그 허용 (INFO/DEBUG/WARN/ERROR)
    • 운영(production/prod) + CLIENT_LOG_DEBUG 미설정: WARN/ERROR만 수집
    • 운영(production/prod) + CLIENT_LOG_DEBUG=true: 디버그 로그 허용
  • 민감정보는 환경과 무관하게 마스킹합니다.
    • 예: password, newPassword, token, authorization, cookie, sessionJwt
    • 문자열 패턴(token=..., authorization:..., JSON body 내 민감 key)도 마스킹
  • 상세 정책 문서: docs/client-log-policy.md

Backend 로그 정책 (중요)

  • Backend 서버 로그는 APP_ENV 기준으로 기본 레벨이 정해집니다.
    • dev|local|development: debug
    • 그 외(stage, production, prod 등): info
  • 운영/스테이징에서 장애 분석이 필요할 때만 BACKEND_LOG_LEVEL=debug를 일시적으로 설정하는 것을 권장합니다.
  • headless login 같은 경로의 상세 진단 필드는 debug 레벨에서만 추가로 남습니다.
  • 상세 정책 문서: docs/backend-log-policy.md

.env 작성 후 권장 점검

make validate-auth-config

위 검증은 callback/allowed_return_urls/게이트웨이 매핑 규칙을 점검하고 config/.generated/auth-config.env를 생성합니다.

전체 스택 실행 (Running the Stack)

1. 네트워크 생성 (최초 1회)

Ory Stack과 애플리케이션 간 통신을 위한 도커 네트워크를 생성합니다.

# ory-net은 bridge 모드로 생성
docker network create -d bridge ory-net
docker network create hydranet
docker network create kratosnet
docker network create public_net #서비스용

2. 인프라 및 Ory Stack 실행

데이터베이스와 Ory 서비스(Kratos, Hydra, Keto 등)를 실행합니다.

# 권장: Make 실행 (인증 설정 검증 포함)
make up-dev

3. 애플리케이션 실행

userfront와 backend 서비스를 실행합니다.

make up-app

(또는 전체 스택 한번에 실행: make up-all)

Make 기반 인증 설정 검증 (권장)

up-* 타깃은 실행 전 인증 리다이렉트 설정을 자동 검증합니다.

# 1) 인증 설정 생성
make build-auth-config

# 2) 정적 검증 (callback / allowed_return_urls / 게이트웨이 매핑)
make validate-auth-config

# 3) 배선 + (가능 시) 런타임 Hydra client 검증
make verify-auth-config
  • 생성 파일: config/.generated/auth-config.env (compose 실행 시 자동 주입)
  • 게이트웨이 경유 환경은 URL 문자열 완전일치 대신 매핑 유효성(direct_match / mapped_match) 기준으로 검증합니다.
  • 관련 정책 문서: docs/oidc_redirect_mapping_validation_policy.md

권장 실행 순서

cp .env.sample .env
# .env 편집
make validate-auth-config
make up-dev
make up-app

직접 Compose를 사용하려면 다음처럼 env 파일을 함께 주입하세요.

docker compose --env-file .env --env-file config/.generated/auth-config.env -f compose.infra.yaml -f compose.ory.yaml up -d
docker compose --env-file .env --env-file config/.generated/auth-config.env -f docker-compose.yaml up -d

MCP 서버 (Hydra/Kratos/Keto)

MCP 서버는 기존 Hydra/Kratos에 연결하며 별도 Ory 스택이나 포트를 추가로 띄우지 않습니다.
프로덕션에서는 실행하지 않도록 mcp 프로파일을 로컬에서만 켜세요.

docker compose -f compose.ory.yaml --profile mcp up -d hydra-mcp-server kratos-mcp-server keto-mcp-server
  • MCP 서버는 stdio 기반이라 외부 포트를 열지 않습니다.
  • 최초 실행시거나 빌드된 이미지가 없으면 `docker compose -f mcp/compose.mcp.ory.yaml build' 후에 사용 가능합니다
[mcp_servers.kratos-mcp]
command = "docker"
args = ["compose", "-f", "mcp/compose.mcp.ory.yaml", "run", "--rm", "--no-deps", "kratos-mcp-server"]

[mcp_servers.kratos-mcp.env]
KRATOS_ADMIN_URL = "http://kratos:4434"

[mcp_servers.hydra-mcp]
command = "docker"
args = ["compose", "-f", "mcp/compose.mcp.ory.yaml", "run", "--rm", "--no-deps", "hydra-mcp-server"]

[mcp_servers.hydra-mcp.env]
HYDRA_PUBLIC_URL = "http://hydra:4444"
HYDRA_ADMIN_URL = "http://hydra:4445"

[mcp_servers.keto-mcp]
command = "docker"
args = ["compose", "-f", "mcp/compose.mcp.ory.yaml", "run", "--rm", "--no-deps", "keto-mcp-server"]

[mcp_servers.keto-mcp.env]
KETO_READ_URL = "http://keto:4466"
KETO_WRITE_URL = "http://keto:4467"

🌐 i18n 구조 (간략)

  • Root locales: locales/template.toml, locales/ko.toml, locales/en.toml은 현재 userfront와 전역 i18n 검증 기준 리소스입니다.
  • Common locales: common/locales/template.toml, common/locales/ko.toml, common/locales/en.tomlui.common.*, msg.common.* 같은 React 공통 문구 레이어입니다.
  • React(Admin/Dev/Org): adminfront/src/lib/i18n.ts, devfront/src/lib/i18n.ts, orgfront/src/lib/i18n.ts에서 t(key, fallback, vars)를 사용하며 common locale -> app locale override 순서로 TOML을 ?raw 로드합니다.
  • Flutter(User): userfront/lib/i18n.dart에서 tr(key, fallback, params) 사용. locales/*.tomltools/i18n-scanner/gen-flutter-i18n.jsuserfront/lib/i18n_data.dart에 사전 생성합니다.
  • UserFront 동기화 규칙: locales/*.toml을 수정한 뒤에는 반드시 ./scripts/sync_userfront_locales.sh를 실행해 userfront/assets/translations/*.toml과 런타임 번역 리소스를 동기화합니다.
  • 검증: node tools/i18n-scanner/index.jsroot localescommon/locales의 코드-키-로케일 동기화 상태를 함께 점검합니다.

🧪 Code Check CI

워크플로우 파일: .gitea/workflows/code_check.yml

트리거

  • push (dev 브랜치)
  • pull_request (dev 대상)
  • workflow_dispatch (수동 실행)

workflow_dispatch 입력값

  • run_lint: Go/Flutter lint 실행 여부
  • run_backend_tests: backend 테스트 실행 여부
  • run_userfront_tests: userfront 테스트 실행 여부
  • run_userfront_e2e_tests: userfront WASM Playwright E2E 실행 여부
  • run_adminfront_tests: adminfront 테스트 실행 여부
  • run_devfront_tests: devfront 테스트 실행 여부

실행 잡

  • lint
  • backend-tests
  • userfront-tests
  • userfront-e2e-tests
  • adminfront-tests
  • devfront-tests

프런트 테스트 브라우저 프리비저닝 정책

  • userfront-tests
    • flutter test(VM)만 실행
    • locale_storage 정책 테스트는 엔진 단위로 통합되어 별도 브라우저 실행이 필요하지 않음
  • adminfront-tests, devfront-tests
    • Playwright 기반 테스트
    • npx playwright install --with-deps로 브라우저/OS 의존성을 사전 설치

실패 보고서 확인 방법

테스트가 실패하면 다음이 자동 생성됩니다.

  • Job Summary: 실패 원인 요약(Markdown) 즉시 확인
  • Artifact: 상세 로그/리포트 다운로드
    • backend-test-failure-report
    • userfront-test-failure-report
    • adminfront-test-failure-report
    • devfront-test-failure-report

userfront locale_storage 테스트 정책

  • locale_storage_platform_test.dartLocaleStorageEngine 기반 정책 테스트로 통합되었습니다.
  • 일반 flutter test(VM) 실행에 포함되며, 브라우저 전용 kIsWeb 케이스를 사용하지 않습니다.
  • 단일 파일만 확인하려면 다음 명령을 사용합니다.
    • flutter test test/locale_storage_platform_test.dart

userfront WASM Playwright E2E

  • 워크스페이스: userfront-e2e/
  • 빌드+실행:
    • cd userfront-e2e && npm run test:wasm
  • 빌드 결과가 이미 있을 때:
    • cd userfront-e2e && npm test
  • Makefile 타깃:
    • make code-check-userfront-e2e-tests
  • 전수 인벤토리:
    • https://gitea.hmac.kr/baron/baron-sso/wiki/UserFront-WASM-E2E-Inventory.-

로컬 개발 (Manual)

Docker 없이는 개발할 수 없지만 Backend 및 [user/admin/dev]Front 코드는 개발모드로 수정하며 개발가능. 백그라운드로 infra 및 ory stack이 구동중이라는 가정

Backend:

cd backend
go mod tidy
go run cmd/server/main.go

userfront:

cd userfront
flutter pub get
flutter run -d chrome
# 정책: 웹 빌드는 기본적으로 WASM 사용
flutter build web --wasm

adminfront:

cd adminfront
npm install
npm run dev

devfront:

cd devfront
npm install
npm run dev

버그 대응 대원칙 (필수)

  • 모든 버그 수정은 반드시 재현 테스트를 먼저 작성합니다. (Failing test first)
  • 재현 테스트 없이 코드만 먼저 고치는 행위를 금지합니다.
  • 수정 후에는 해당 재현 테스트가 통과할 때까지 반복해서 원인 분석/수정/검증을 수행합니다.
  • “테스트 통과”는 최소 기준입니다. 실제 재현 시나리오(로그인, 새로고침, 리다이렉트 등)까지 확인한 뒤에만 이슈를 종료합니다.
  • 관련 변경이 발생하면 테스트 문서(docs/test-plan/*, docs/trouble-shooting/*)를 함께 업데이트합니다.