openapi: 3.0.3
info:
  title: Baron SSO API
  version: "0.2.0"
  description: |
    Baron SSO Backend API 문서입니다.
    이 문서는 Swagger UI/ReDoc 단일 엔드포인트에서 확인할 수 있으며,
    도메인별 API 스펙은 점진적으로 확장합니다.
servers:
  - url: /
    description: 동일 호스트 기준

tags:
  - name: System
    description: 공통/헬스/문서
  - name: Auth
    description: 인증/세션/비밀번호/링크 로그인
  - name: Signup
    description: 회원가입/검증
  - name: User
    description: 사용자 프로필
  - name: Session
    description: 세션 관리(계획)
  - name: Admin
    description: 관리자 기능/테넌트
  - name: Dev
    description: RP/Consent 관리
  - name: Audit
    description: 감사 로그
  - name: Webhook
    description: 외부 게이트웨이 연동

paths:
  /health:
    get:
      tags: [System]
      summary: 헬스 체크
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HealthResponse"

  /openapi.yaml:
    get:
      tags: [System]
      summary: OpenAPI YAML
      responses:
        "200":
          description: OK
          content:
            application/yaml:
              schema:
                type: string

  /docs:
    get:
      tags: [System]
      summary: Swagger UI
      responses:
        "200":
          description: HTML
          content:
            text/html:
              schema:
                type: string

  /redoc:
    get:
      tags: [System]
      summary: ReDoc
      responses:
        "200":
          description: HTML
          content:
            text/html:
              schema:
                type: string

  /api/v1/auth/password/login:
    post:
      tags: [Auth]
      summary: 비밀번호 로그인
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PasswordLoginRequest"
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PasswordLoginResponse"
        "401":
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /api/v1/auth/password/reset/initiate:
    post:
      tags: [Auth]
      summary: 비밀번호 재설정 시작
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PasswordResetInitiateRequest"
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MessageResponse"

  /api/v1/auth/password/reset/verify:
    get:
      tags: [Auth]
      summary: 비밀번호 재설정 토큰 확인 페이지
      responses:
        "200":
          description: HTML
          content:
            text/html:
              schema:
                type: string
    post:
      tags: [Auth]
      summary: 비밀번호 재설정 토큰 처리
      requestBody:
        required: false
        content:
          application/x-www-form-urlencoded:
            schema:
              type: object
              properties:
                token:
                  type: string
      responses:
        "302":
          description: Redirect

  /api/v1/auth/password/reset/complete:
    post:
      tags: [Auth]
      summary: 비밀번호 재설정 완료
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PasswordResetCompleteRequest"
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MessageResponse"

  /api/v1/auth/password/policy:
    get:
      tags: [Auth]
      summary: 비밀번호 정책 조회
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PasswordPolicyResponse"

  /api/v1/auth/enchanted-link/init:
    post:
      tags: [Auth]
      summary: Enchanted Link 시작
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/EnchantedLinkInitRequest"
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/EnchantedLinkInitResponse"

  /api/v1/auth/enchanted-link/poll:
    post:
      tags: [Auth]
      summary: Enchanted Link 폴링
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/EnchantedLinkPollRequest"
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/EnchantedLinkPollResponse"

  /api/v1/auth/magic-link/verify:
    post:
      tags: [Auth]
      summary: 매직 링크 검증
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/MagicLinkVerifyRequest"
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MessageResponse"

  /api/v1/auth/sms:
    post:
      tags: [Auth]
      summary: SMS 전송
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SmsSendRequest"
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MessageResponse"

  /api/v1/auth/verify-sms:
    post:
      tags: [Auth]
      summary: SMS 인증 코드 검증
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SmsVerifyRequest"
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MessageResponse"

  /api/v1/auth/qr/init:
    post:
      tags: [Auth]
      summary: QR 로그인 세션 생성
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/QrInitResponse"

  /api/v1/auth/qr/poll:
    post:
      tags: [Auth]
      summary: QR 로그인 폴링
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/QrPollRequest"
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/QrPollResponse"

  /api/v1/auth/qr/approve:
    post:
      tags: [Auth]
      summary: QR 로그인 승인
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/QrApproveRequest"
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MessageResponse"

  /api/v1/auth/signup/check-email:
    post:
      tags: [Signup]
      summary: 회원가입 이메일 확인
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SignupEmailCheckRequest"
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MessageResponse"

  /api/v1/auth/signup/send-email-code:
    post:
      tags: [Signup]
      summary: 회원가입 이메일 코드 발송
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SignupEmailCodeRequest"
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MessageResponse"

  /api/v1/auth/signup/send-sms-code:
    post:
      tags: [Signup]
      summary: 회원가입 SMS 코드 발송
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SignupSmsCodeRequest"
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MessageResponse"

  /api/v1/auth/signup/verify-code:
    post:
      tags: [Signup]
      summary: 회원가입 코드 검증
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SignupVerifyCodeRequest"
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MessageResponse"

  /api/v1/auth/signup:
    post:
      tags: [Signup]
      summary: 회원가입 완료
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SignupRequest"
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SignupResponse"

  /api/v1/user/me:
    get:
      tags: [User]
      summary: 내 프로필 조회
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/UserProfileResponse"
    put:
      tags: [User]
      summary: 내 프로필 수정
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/UserProfileUpdateRequest"
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/UserProfileResponse"

  /api/v1/user/me/send-code:
    post:
      tags: [User]
      summary: 프로필 변경 인증코드 전송
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/UserProfileSendCodeRequest"
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MessageResponse"

  /api/v1/user/me/verify-code:
    post:
      tags: [User]
      summary: 프로필 변경 인증코드 검증
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/UserProfileVerifyCodeRequest"
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MessageResponse"

  /api/v1/sessions:
    get:
      tags: [Session]
      summary: 세션 목록
      description: 세션 관리 API는 계획 단계입니다.
      x-status: planned
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SessionListResponse"

  /api/v1/sessions/{id}:
    get:
      tags: [Session]
      summary: 세션 상세
      description: 세션 관리 API는 계획 단계입니다.
      x-status: planned
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SessionDetailResponse"
    delete:
      tags: [Session]
      summary: 세션 로그아웃(폐기)
      description: 세션 관리 API는 계획 단계입니다.
      x-status: planned
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
      responses:
        "204":
          description: No Content

  /api/v1/sessions/logout-all:
    post:
      tags: [Session]
      summary: 모든 세션 로그아웃
      description: 세션 관리 API는 계획 단계입니다.
      x-status: planned
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MessageResponse"

  /api/v1/admin/check:
    get:
      tags: [Admin]
      summary: 관리자 인증 확인
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MessageResponse"
        "401":
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /api/v1/admin/tenants:
    get:
      tags: [Admin]
      summary: 테넌트 목록
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TenantListResponse"
    post:
      tags: [Admin]
      summary: 테넌트 생성
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/TenantCreateRequest"
      responses:
        "201":
          description: Created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TenantResponse"

  /api/v1/admin/tenants/{id}:
    get:
      tags: [Admin]
      summary: 테넌트 상세
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TenantResponse"
    put:
      tags: [Admin]
      summary: 테넌트 수정
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/TenantUpdateRequest"
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TenantResponse"
    delete:
      tags: [Admin]
      summary: 테넌트 삭제
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
      responses:
        "204":
          description: No Content

  /api/v1/dev/clients:
    get:
      tags: [Dev]
      summary: RP 목록
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ClientListResponse"
    post:
      tags: [Dev]
      summary: RP 생성
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ClientCreateRequest"
      responses:
        "201":
          description: Created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ClientDetailResponse"

  /api/v1/dev/clients/{id}:
    get:
      tags: [Dev]
      summary: RP 상세
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ClientDetailResponse"
    put:
      tags: [Dev]
      summary: RP 수정
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ClientUpdateRequest"
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ClientDetailResponse"
    delete:
      tags: [Dev]
      summary: RP 삭제
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
      responses:
        "204":
          description: No Content

  /api/v1/dev/clients/{id}/status:
    patch:
      tags: [Dev]
      summary: RP 상태 변경
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ClientStatusPatchRequest"
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ClientDetailResponse"

  /api/v1/dev/consents:
    get:
      tags: [Dev]
      summary: Consent 목록
      parameters:
        - in: query
          name: subject
          required: true
          schema:
            type: string
        - in: query
          name: client_id
          required: false
          schema:
            type: string
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ConsentListResponse"
    delete:
      tags: [Dev]
      summary: Consent 회수
      parameters:
        - in: query
          name: subject
          required: true
          schema:
            type: string
        - in: query
          name: client_id
          required: false
          schema:
            type: string
      responses:
        "204":
          description: No Content

  /api/v1/audit:
    post:
      tags: [Audit]
      summary: 감사 로그 수집
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/AuditLogRequest"
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MessageResponse"

  /api/v1/client-log:
    post:
      tags: [Audit]
      summary: 클라이언트 로그 수집
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ClientLogRequest"
      responses:
        "200":
          description: OK

  /api/v1/auth/webhooks/descope-sms:
    post:
      tags: [Webhook]
      summary: Descope SMS 릴레이
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/DescopeSmsWebhookRequest"
      responses:
        "200":
          description: OK

  /api/v1/auth/webhooks/descope-email:
    post:
      tags: [Webhook]
      summary: Descope Email 릴레이
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/DescopeEmailWebhookRequest"
      responses:
        "200":
          description: OK

components:
  schemas:
    ErrorResponse:
      type: object
      properties:
        error:
          type: string

    MessageResponse:
      type: object
      properties:
        message:
          type: string
        status:
          type: string

    HealthResponse:
      type: object
      properties:
        status:
          type: string
        checks:
          type: object

    PasswordLoginRequest:
      type: object
      required: [loginId, password]
      properties:
        loginId:
          type: string
        password:
          type: string

    PasswordLoginResponse:
      type: object
      properties:
        sessionJwt:
          type: string
        refreshJwt:
          type: string
        status:
          type: string
        provider:
          type: string
        subject:
          type: string

    PasswordResetInitiateRequest:
      type: object
      required: [loginId]
      properties:
        loginId:
          type: string

    PasswordResetCompleteRequest:
      type: object
      required: [newPassword]
      properties:
        newPassword:
          type: string

    PasswordPolicyResponse:
      type: object
      properties:
        minLength:
          type: integer
        lowercase:
          type: boolean
        uppercase:
          type: boolean
        number:
          type: boolean
        nonAlphanumeric:
          type: boolean

    EnchantedLinkInitRequest:
      type: object
      properties:
        loginId:
          type: string

    EnchantedLinkInitResponse:
      type: object
      properties:
        pendingRef:
          type: string
        expiresIn:
          type: integer

    EnchantedLinkPollRequest:
      type: object
      properties:
        pendingRef:
          type: string

    EnchantedLinkPollResponse:
      type: object
      properties:
        status:
          type: string
        sessionJwt:
          type: string

    MagicLinkVerifyRequest:
      type: object
      properties:
        token:
          type: string

    SmsSendRequest:
      type: object
      properties:
        phone:
          type: string
        message:
          type: string

    SmsVerifyRequest:
      type: object
      properties:
        phone:
          type: string
        code:
          type: string

    QrInitResponse:
      type: object
      properties:
        qrCode:
          type: string
        pendingRef:
          type: string
        expiresIn:
          type: integer

    QrPollRequest:
      type: object
      properties:
        pendingRef:
          type: string

    QrPollResponse:
      type: object
      properties:
        status:
          type: string
        sessionJwt:
          type: string

    QrApproveRequest:
      type: object
      properties:
        pendingRef:
          type: string
        token:
          type: string

    SignupEmailCheckRequest:
      type: object
      properties:
        email:
          type: string

    SignupEmailCodeRequest:
      type: object
      properties:
        email:
          type: string

    SignupSmsCodeRequest:
      type: object
      properties:
        phone:
          type: string

    SignupVerifyCodeRequest:
      type: object
      properties:
        target:
          type: string
        code:
          type: string

    SignupRequest:
      type: object
      properties:
        email:
          type: string
        phone:
          type: string
        name:
          type: string
        password:
          type: string
        department:
          type: string
        affiliationType:
          type: string
        companyCode:
          type: string

    SignupResponse:
      type: object
      properties:
        success:
          type: boolean
        message:
          type: string
        provider:
          type: string
        subject:
          type: string

    UserProfileResponse:
      type: object
      properties:
        id:
          type: string
        email:
          type: string
        name:
          type: string
        phone:
          type: string
        department:
          type: string
        affiliationType:
          type: string
        companyCode:
          type: string

    UserProfileUpdateRequest:
      type: object
      properties:
        name:
          type: string
        phone:
          type: string
        department:
          type: string
        affiliationType:
          type: string
        companyCode:
          type: string

    UserProfileSendCodeRequest:
      type: object
      properties:
        phone:
          type: string

    UserProfileVerifyCodeRequest:
      type: object
      properties:
        phone:
          type: string
        code:
          type: string

    SessionSummary:
      type: object
      properties:
        id:
          type: string
        issuedAt:
          type: string
        expiresAt:
          type: string
        device:
          type: string
        ip:
          type: string
        userAgent:
          type: string

    SessionListResponse:
      type: object
      properties:
        items:
          type: array
          items:
            $ref: "#/components/schemas/SessionSummary"

    SessionDetailResponse:
      type: object
      properties:
        session:
          $ref: "#/components/schemas/SessionSummary"

    TenantResponse:
      type: object
      properties:
        id:
          type: string
        name:
          type: string
        slug:
          type: string
        description:
          type: string
        status:
          type: string
        createdAt:
          type: string
        updatedAt:
          type: string

    TenantListResponse:
      type: object
      properties:
        items:
          type: array
          items:
            $ref: "#/components/schemas/TenantResponse"
        limit:
          type: integer
        offset:
          type: integer
        total:
          type: integer

    TenantCreateRequest:
      type: object
      required: [name]
      properties:
        name:
          type: string
        slug:
          type: string
        description:
          type: string
        status:
          type: string

    TenantUpdateRequest:
      type: object
      properties:
        name:
          type: string
        slug:
          type: string
        description:
          type: string
        status:
          type: string

    ClientSummary:
      type: object
      properties:
        id:
          type: string
        name:
          type: string
        type:
          type: string
        status:
          type: string
        createdAt:
          type: string
        redirectUris:
          type: array
          items:
            type: string
        scopes:
          type: array
          items:
            type: string
        metadata:
          type: object
          additionalProperties: true

    ClientListResponse:
      type: object
      properties:
        items:
          type: array
          items:
            $ref: "#/components/schemas/ClientSummary"
        limit:
          type: integer
        offset:
          type: integer

    ClientEndpoints:
      type: object
      properties:
        discovery:
          type: string
        issuer:
          type: string
        authorization:
          type: string
        token:
          type: string
        userinfo:
          type: string

    ClientDetailResponse:
      type: object
      properties:
        client:
          $ref: "#/components/schemas/ClientSummary"
        endpoints:
          $ref: "#/components/schemas/ClientEndpoints"

    ClientCreateRequest:
      type: object
      required: [redirectUris]
      properties:
        id:
          type: string
        name:
          type: string
        type:
          type: string
        status:
          type: string
        redirectUris:
          type: array
          items:
            type: string
        scopes:
          type: array
          items:
            type: string
        grantTypes:
          type: array
          items:
            type: string
        responseTypes:
          type: array
          items:
            type: string
        tokenEndpointAuthMethod:
          type: string
        metadata:
          type: object
          additionalProperties: true

    ClientUpdateRequest:
      type: object
      properties:
        name:
          type: string
        type:
          type: string
        status:
          type: string
        redirectUris:
          type: array
          items:
            type: string
        scopes:
          type: array
          items:
            type: string
        grantTypes:
          type: array
          items:
            type: string
        responseTypes:
          type: array
          items:
            type: string
        tokenEndpointAuthMethod:
          type: string
        metadata:
          type: object
          additionalProperties: true

    ClientStatusPatchRequest:
      type: object
      required: [status]
      properties:
        status:
          type: string

    ConsentSummary:
      type: object
      properties:
        subject:
          type: string
        clientId:
          type: string
        clientName:
          type: string
        grantedScopes:
          type: array
          items:
            type: string
        authenticatedAt:
          type: string

    ConsentListResponse:
      type: object
      properties:
        items:
          type: array
          items:
            $ref: "#/components/schemas/ConsentSummary"

    AuditLogRequest:
      type: object
      properties:
        service:
          type: string
        event:
          type: string
        payload:
          type: object
          additionalProperties: true

    ClientLogRequest:
      type: object
      properties:
        level:
          type: string
        message:
          type: string
        data:
          type: object
          additionalProperties: true

    DescopeSmsWebhookRequest:
      type: object
      properties:
        recipient:
          type: string
        body:
          type: string

    DescopeEmailWebhookRequest:
      type: object
      properties:
        to:
          type: string
        subject:
          type: string
        text:
          type: string
