openapi: 3.0.1
info:
  version: 2.0.0
  title: IAD Service Rest API
  description: |
    API REST de detección de ataques de inyección (Injection Attack Detection, IAD) en capturas biométricas. Este servicio proporciona endpoints para verificar el liveness, extraer imágenes de capturas biométricas y monitorizar el estado del servicio.

    **Aviso de cambio incompatible (2.0.0):** la API REST pública no es retrocompatible con la serie 1.x.x.
    Los clientes que actualicen desde 1.x.x deben actualizar:
    - las rutas de los endpoints (`/api/v1/iad/check-capture` -> `/api/v1/iad/liveness/evaluate`, `/api/v1/iad/extract-image` -> `/api/v1/iad/extract`)
    - los nombres de los campos de las solicitudes multipart (`file` -> `capture`)
    - el análisis de las respuestas correctas (los payloads privados heredados se sustituyen por el contrato público de Facephi)

servers:
 - url: http://127.0.0.1:6982/

# Autenticación básica
components:
  #securitySchemes:
  #  BasicAuth:
  #    type: http
  #    scheme: basic

  schemas:

    # Esquemas comunes

    BinaryData:
      type: string
      format: binary
      description: Buffer de datos binarios, típicamente datos de captura biométrica (imágenes, fotogramas de vídeo, etc.).

    TimeStamp:
      type: string
      description: Un valor de fecha/hora en formato "%Y-%m-%dT%H:%M:%SZ".
      example: "2022-01-14T10:42:05.521922Z"

    CaptureLivenessResult:
      type: object
      description: Respuesta pública de liveness expuesta por la API de Facephi.
      properties:
        diagnostic:
          type: string
          enum:
            - Live
            - NoLive
        reason:
          type: string
          description: |
            Motivo público de liveness.

            Valores posibles:
            - `None`: la captura fue aceptada como `Live`.
            - `Unknown`: el servicio no pudo asignar la respuesta a un motivo de rechazo público documentado.
            - `UntrustedEnvironment`: el entorno de ejecución no se considera de confianza.
            - `SuspiciousActivity`: el dispositivo mostró patrones de actividad asociados a un ataque.
            - `UntrustedDevice`: no se pudo confiar en que el dispositivo fuera el que dice ser.
            - `SdkIntegrityViolation`: el SDK de captura o sus librerías parecen haber sido alterados.
            - `UntrustedCorruptedPayload`: el payload parece estar corrupto o manipulado.
            - `UntrustedContent`: se detectó un ataque de inyección.
            - `UntrustedContentLowConfidence`: se detectaron indicios de un ataque de inyección con menor confianza y la captura debe tratarse igualmente como rechazada.
          enum:
            - None
            - Unknown
            - UntrustedEnvironment
            - SuspiciousActivity
            - UntrustedDevice
            - SdkIntegrityViolation
            - UntrustedCorruptedPayload
            - UntrustedContent
            - UntrustedContentLowConfidence
          example: UntrustedContent
        probability:
          type: number
          format: float
          example: 1.0
        score:
          type: number
          format: float
          example: 1.0
        faceProbability:
          type: number
          format: float
          example: 0.9969
        sdkDuration:
          type: integer
          format: int64
          example: 12
        queueDuration:
          type: integer
          format: int64
          example: 3
      example:
        diagnostic: Live
        reason: None
        probability: 1.0
        score: 1.0
        sdkDuration: 12
        queueDuration: 3

    FacephiErrorMessage:
      type: string
      description: Mensaje de error público documentado devuelto en las respuestas HTTP 400.
      enum:
        - NoneBecauseFaceTooClose
        - NoneBecauseFaceNotFound
        - NoneBecauseFaceTooSmall
        - NoneBecauseAngleTooLarge
        - NoneBecauseImageDataError
        - NoneBecauseLicenseError
        - NoneBecauseTooManyFaces
        - NoneBecauseFaceTooCloseToBorder
        - NoneBecauseFaceCropped
        - NoneBecauseFaceOccluded
        - NoneBecauseEyesClosed
        - Replay attack detected
        - ErrorProcessing
        - ErrorFacialImage

    ExtractImageResult:
      type: object
      properties:
        image:
          type: string
          format: byte
          description: Imagen extraída codificada en base64.
        mimeType:
          type: string
          description: Tipo MIME de la imagen extraída.
          example: "image/jpeg"

    MessageResult:
      type: object
      properties:
        message:
          type: string
          description: Mensaje de respuesta del servicio.
          example: "2.0.0 Copyright © 2026 FacePhi Biometria. All rights reserved."

    ConfigData:
      type: object
      description: Vista pública de la configuración en tiempo de ejecución devuelta por GET /api/v1/iad/config. Las claves de autenticación JWT se omiten intencionadamente.
      properties:
        port:
          type: integer
          example: 6982
        number_of_threads:
          type: integer
          example: 1
        connection_timeout:
          type: integer
          example: 60
        keep_alive_request_number:
          type: integer
          example: 0
        client_max_body_size:
          type: integer
          example: 100
        logger_path:
          type: string
          example: "/app/logs"
        logger_level:
          type: string
          example: "info"
        logger_rotation:
          type: string
          example: "daily"
        logger_max_files:
          type: integer
          example: 7
        engine_connection_timeout:
          type: integer
          description: Timeout de conexión del análisis de capturas en milisegundos.
          example: 10000
        engine_request_timeout:
          type: integer
          description: Timeout de solicitud del análisis de capturas en milisegundos.
          example: 60000
        engine_max_retries:
          type: integer
          description: Número máximo de intentos de reintento.
          example: 3
        engine_retry_delay:
          type: integer
          description: Retardo entre reintentos en milisegundos.
          example: 1000
        engine_verify_ssl:
          type: boolean
          description: Indica si se deben verificar los certificados SSL del análisis de capturas.
          example: false
        engine_verbose:
          type: boolean
          example: false
        engine_pool_size:
          type: integer
          example: 4
        engine_url:
          type: string
          description: URL del runtime de análisis de capturas.
          example: "http://localhost:8080"

    ConfigUpdateRequest:
      type: object
      required:
        - config_json_string
      properties:
        config_json_string:
          type: string
          description: Cadena JSON con la configuración en tiempo de ejecución a cargar. Las claves de autenticación JWT (`auth_*`) se rechazan y deben configurarse únicamente al inicio.
          example: '{"port":6982,"number_of_threads":1,"engine_url":"http://localhost:8080"}'

    BadRequestResult:
      type: object
      description: Resultado cuando llegan parámetros incorrectos en la solicitud.
      properties:
        status:
          type: number
          example: 400
        error:
          type: string
          example: "BadRequest"
        message:
          type: string
          description: Mensaje de error del servicio o error de validación normalizado por Facephi.
          oneOf:
            - $ref: '#/components/schemas/FacephiErrorMessage'
            - type: string
          example: "Service not initialized"
        timeStamp:
          $ref: '#/components/schemas/TimeStamp'

    ExceptionResult:
      type: object
      description: Se ha producido un error en la llamada al backend.
      properties:
        status:
          type: number
          example: 500
        error:
          type: string
          example: "Backend Exception"
        message:
          type: string
          example: "Unexpected error while processing request"
        exception:
          type: string
          example: "NullPointerException: Something went wrong"
        timeStamp:
          $ref: '#/components/schemas/TimeStamp'


# Rutas / Endpoints

paths:

  # Endpoints de gestión

  /api/v1/iad/version:
    get:
      summary: Obtener la versión del servicio
      description: Devuelve la versión del servicio y la información de copyright. Si la licencia no es válida, el endpoint sigue respondiendo con HTTP 200 y añade los detalles del error de licencia al mensaje.
      operationId: getVersion
      tags:
        - Management
      responses:
        '200':
          description: Información de la versión
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MessageResult'
        '400':
          description: El servicio no está inicializado.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BadRequestResult'

  /api/v1/iad/health:
    get:
      summary: Comprobación de estado
      description: Devuelve el estado de salud del servicio y la conectividad del procesamiento de capturas. Si la licencia no es válida, el endpoint sigue respondiendo con HTTP 200 e incluye los detalles del fallo en el mensaje.
      operationId: getHealth
      tags:
        - Management
      responses:
        '200':
          description: El servicio está en buen estado, o la licencia no es válida pero el servicio sigue respondiendo.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MessageResult'
        '400':
          description: El servicio no está inicializado o la comprobación de estado del procesamiento de capturas ha fallado.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BadRequestResult'

  /api/v1/iad/config:
    get:
      summary: Obtener la configuración actual
      description: Obtiene la configuración pública actual del servicio. Este endpoint no expone las claves de autenticación JWT.
      operationId: getConfig
      tags:
        - Management
      responses:
        '200':
          description: Configuración actual
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ConfigData'
        '400':
          description: El servicio no está inicializado o no se pudo analizar el JSON de configuración.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BadRequestResult'

    post:
      summary: Actualizar la configuración
      description: Actualiza la configuración del servicio dinámicamente sin reiniciar el servicio. Las claves de autenticación JWT se rechazan y deben configurarse únicamente al inicio mediante config.json o variables de entorno.
      operationId: updateConfig
      tags:
        - Management
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ConfigUpdateRequest'
      responses:
        '200':
          description: Configuración actualizada correctamente
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MessageResult'
        '400':
          description: Llegan parámetros incorrectos en la solicitud.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BadRequestResult'

        '500':
          description: Se ha producido un error en el backend.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ExceptionResult'

  # Endpoints de IAD

  /api/v1/iad/liveness/evaluate:
    post:
      summary: Evaluar liveness
      description: |
        Verifica el liveness en una captura biométrica cifrada y expone un resultado público de Facephi.
        El nombre del campo de la solicitud multipart debe ser `capture`; el campo heredado `file` no se acepta.

        Las respuestas correctas HTTP 200 devuelven los campos públicos `diagnostic`, `reason`, `probability`, `score`,
        `sdkDuration` y `queueDuration`.

        Si la mitigación experimental de ataques de repetición está habilitada en el momento del despliegue, las capturas más antiguas que la
        vigencia configurada se rechazan con HTTP 400 y el mensaje `Replay attack detected`.
      operationId: evaluateLiveness
      tags:
        - IAD
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required:
                - capture
              properties:
                capture:
                  $ref: '#/components/schemas/BinaryData'
      responses:
        '200':
          description: Resultado público de liveness
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CaptureLivenessResult'
              examples:
                live:
                  summary: Captura Live
                  value:
                    diagnostic: Live
                    reason: None
                    probability: 1.0
                    score: 1.0
                    sdkDuration: 12
                    queueDuration: 3
                noLive:
                  summary: Captura NoLive
                  value:
                    diagnostic: NoLive
                    reason: UntrustedContent
                    probability: 0.2
                    score: 0.2
                    sdkDuration: 12
                    queueDuration: 3
        '400':
          description: |
            Error de validación de la solicitud o de validación de vigencia de la captura.

            Los errores de validación de captura se normalizan en el campo público `message`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BadRequestResult'
              examples:
                faceNotFound:
                  summary: Cara no encontrada
                  value:
                    status: 400
                    error: "BadRequest"
                    message: "NoneBecauseFaceNotFound"
                    timeStamp: "2026-04-28T10:30:00Z"
                tooManyFaces:
                  summary: Demasiadas caras detectadas
                  value:
                    status: 400
                    error: "BadRequest"
                    message: "NoneBecauseTooManyFaces"
                    timeStamp: "2026-04-28T10:30:00Z"
                imageTooSmall:
                  summary: Imagen demasiado pequeña
                  value:
                    status: 400
                    error: "BadRequest"
                    message: "NoneBecauseImageDataError"
                    timeStamp: "2026-04-28T10:30:00Z"
                replayAttack:
                  summary: Ventana de vigencia de repetición superada
                  value:
                    status: 400
                    error: "BadRequest"
                    message: "Replay attack detected"
                    timeStamp: "2026-04-28T10:30:00Z"
                unexpectedFailure:
                  summary: Fallo de liveness sin clasificar
                  value:
                    status: 400
                    error: "BadRequest"
                    message: "ErrorProcessing"
                    timeStamp: "2026-04-28T10:30:00Z"

        '500':
          description: Se ha producido un error en el backend.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ExceptionResult'

  /api/v1/iad/extract:
    post:
      summary: Extraer imagen de la captura
      description: |
        Extrae y valida los datos de imagen de la captura biométrica.
        El nombre del campo de la solicitud multipart debe ser `capture`; el campo heredado `file` no se acepta.

        Si el servicio no puede analizar o desencriptar el payload, devuelve HTTP 400.
        Los errores del payload de captura se normalizan a `NoneBecauseImageDataError`.
        Si la mitigación experimental de ataques de repetición está habilitada en el momento del despliegue, las capturas expiradas se rechazan con
        HTTP 400 y el mensaje `Replay attack detected`.
        Los fallos de extracción sin clasificar se devuelven como `ErrorFacialImage`.
      operationId: extractImage
      tags:
        - IAD
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required:
                - capture
              properties:
                capture:
                  $ref: '#/components/schemas/BinaryData'
      responses:
        '200':
          description: Datos de la imagen extraída
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ExtractImageResult'
        '400':
          description: |
            Parámetros incorrectos, error en el payload o error de validación de vigencia de la captura.

            Errores públicos típicos para este endpoint:
            - Fallo al analizar el archivo (`NoneBecauseImageDataError`)
            - Fallo al desencriptar el mensaje (`NoneBecauseImageDataError`)
            - Ventana de vigencia de repetición superada (`Replay attack detected`)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BadRequestResult'
              examples:
                parseFailed:
                  summary: Fallo al analizar el archivo
                  value:
                    status: 400
                    error: "BadRequest"
                    message: "NoneBecauseImageDataError"
                    timeStamp: "2026-04-28T10:30:00Z"
                decryptFailed:
                  summary: Fallo al desencriptar el mensaje
                  value:
                    status: 400
                    error: "BadRequest"
                    message: "NoneBecauseImageDataError"
                    timeStamp: "2026-04-28T10:30:00Z"
                replayAttack:
                  summary: Ventana de vigencia de repetición superada
                  value:
                    status: 400
                    error: "BadRequest"
                    message: "Replay attack detected"
                    timeStamp: "2026-04-28T10:30:00Z"
                unexpectedFailure:
                  summary: Fallo de extracción sin clasificar
                  value:
                    status: 400
                    error: "BadRequest"
                    message: "ErrorFacialImage"
                    timeStamp: "2026-04-28T10:30:00Z"

        '500':
          description: Se ha producido un error en el backend.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ExceptionResult'

tags:
  - name: Management
    description: Endpoints de gestión y monitorización del servicio
  - name: IAD
    description: Endpoints de detección de ataques de inyección
