Skip to main content

pi-generate-credentials

1. Descripción general

Según el pom.xml, el proyecto se describe como "PI generate credentials". Es un microservicio de gestión de credenciales que renueva automáticamente los tokens OAuth de los marketplaces Shopee (5 regiones), Miravia y MercadoLibre Colombia, almacenándolos como ficheros JSON en un volumen persistente, y los expone vía una pequeña API REST para que otros servicios (p. ej. marketplaces-api-rest) puedan leerlos.

El CLAUDE.md del proyecto describe además una integración activa con TikTok (job programado diario) que, verificado en el código, no existe como tal (ver hallazgo en la sección 13).

2. Información técnica

CampoValor
artifactIdpi-generate-credentials
groupIdcom.hawkersco
version1.0.25
Java25 (maven.compiler.release=25)
Spring Boot4.0.6
Tipo de artefactojar (ejecutable, API web de larga duración)
MódulosNo aplica (proyecto de módulo único)

3. Arquitectura y diseño

API REST simple con dos endpoints y jobs programados en segundo plano.

  • com.hawkersco.pigeneratecredentials — clase principal (PiGenerateCredentialsApplication).
  • .controllerPiGenerateCredentialsController (único controlador), GlobalExceptionHandler.
  • .jobsPiGenerateCredentialsJob (jobs programados activos), PiReGenerateCredentialsJob (herramienta manual de desarrollo, con todos sus métodos @Scheduled comentados — ver hallazgo de seguridad en la sección 13).
  • .utilsPiGenerateCredentialsUtils (firma HMAC-SHA256 para Shopee, codificación Base64 para MercadoLibre, llamadas a los clientes de marketplace).
flowchart TD
A["GET /api/token/get?tokenName=X"] --> B[Lee credentialsdata/X.json]
C["POST /api/token/generate?tokenName=X"] --> D[PiGenerateCredentialsJob]
D -->|Shopee| E[ShopeeClient]
D -->|Miravia| F[MiraviaClient]
D -->|MercadoLibre CO| G[MeliTokenClient]
E --> H[(credentialsdata/*.json<br/>volumen persistente GCE)]
F --> H
G --> H

I["@Scheduled cada 4h/12h<br/>(solo perfil pro)"] --> D

4. Dependencias principales

DependenciaPropósito
spring-boot-starter-webExposición de los endpoints REST
com.google.code.gson:gsonSerialización/deserialización de los JSON de credenciales
com.hawkersco:miravia-client:1.0.25-SNAPSHOTCliente para renovar el token de Miravia
com.hawkersco:shopee-client:1.0.25-SNAPSHOTCliente para renovar los tokens de Shopee (firma HMAC)
com.hawkersco:meli-client:1.0.25-SNAPSHOTCliente para renovar el token de MercadoLibre Colombia
com.global.iop:* (dependencia transitiva no declarada explícitamente, usada solo en PiReGenerateCredentialsJob)Cliente IOP usado en el flujo manual de obtención inicial del token de Miravia
spring-boot-starter-test (test)JUnit 5 + Spring Test

No hay dependencia de un cliente TikTok en el pom.xml.

5. API / Endpoints

MétodoRutaDescripción
GET/api/token/get?tokenName=<NOMBRE>Devuelve el contenido del fichero de credenciales correspondiente
POST/api/token/generate?tokenName=<NOMBRE>Fuerza la renovación del token indicado y devuelve el resultado

Valores válidos de tokenName: MIRAVIA, SHOPEE_MY, SHOPEE_PH, SHOPEE_SG, SHOPEE_TH, SHOPEE_VN, MELICO. Cualquier otro valor devuelve 400 Bad Request con el mensaje "There is no such token".

No hay autenticación ni autorización en estos endpoints (no se ha detectado spring-boot-starter-security en las dependencias) — ver hallazgo en la sección 13.

6. Integraciones externas

SistemaProtocoloDirecciónDetalle
Shopee (partner.shopeemobile.com)HTTP (ShopeeClient, firma HMAC-SHA256)SalienteRenovación de token de acceso por región (MY, PH, SG, TH, VN)
Miravia (api.miravia.es)HTTP (MiraviaClient, firma propia)SalienteRenovación de token de acceso
MercadoLibre (api.mercadolibre.com)HTTP (MeliTokenClient)SalienteRenovación de token de acceso para MercadoLibre Colombia

7. Configuración

En producción (application-pro.properties) las credenciales llegan por variables de entorno inyectadas como Secret de Kubernetes; en local (application.properties) el repositorio contiene actualmente valores reales hardcodeados (ver alerta de seguridad).

ClaveDescripciónEjemplo (producción)
miravia.client.url / .appkey / .appsecretCredenciales de Miravia${miraviaClientUrl}, etc.
shopee.api.host / .partnerId / .partnerKeyCredenciales de partner de Shopee${shopeeApiHost}, etc.
shopee.api.shopId.{my,th,sg,ph,vn}IDs de tienda por región de Shopee${shopeeApiShopIdMy}, etc.
meli.credentials.url / .clientid / .clientsecretCredenciales de MercadoLibre${meliCoCredUrl}, etc.
tiktok.auth.url / .app-key / .app-secret / .grant-typeCredenciales de TikTok declaradas e inyectadas, pero sin ningún uso real en el código (ver sección 13)${tiktokAuthUrl}, etc.

⚠️ Alerta de seguridad (severidad alta)

Se han encontrado dos problemas de seguridad relevantes:

  1. application.properties local con credenciales reales: contiene las claves de partner de Shopee, de Miravia y de MercadoLibre Colombia, así como las credenciales de TikTok (aunque estas últimas no se usan en la práctica). Ninguno de estos valores se ha reproducido en este documento.
  2. Secretos reales hardcodeados directamente en PiReGenerateCredentialsJob.java: el método generateTokeMiravia() contiene el app-key y app-secret de Miravia y un código de autorización OAuth de un solo uso, todos como literales de cadena en el código fuente; generateCodeShopee() contiene el partnerId de Shopee como literal numérico. A diferencia de las credenciales en application.properties (rotables vía Secret de Kubernetes sin recompilar), estas quedan fijas en el binario compilado. Aunque esta clase es una herramienta de desarrollo con todos los métodos @Scheduled comentados (no se ejecutan automáticamente), sigue siendo un @Component que Spring instancia en cada arranque, y el código con los secretos permanece en el repositorio y en el artefacto desplegado.

Se recomienda:

  1. Rotar las credenciales de Shopee, Miravia y MercadoLibre expuestas en application.properties.
  2. Eliminar (o al menos enmascarar) los secretos hardcodeados en PiReGenerateCredentialsJob, y valorar si esta clase debería seguir siendo un @Component gestionado por Spring o convertirse en un script/herramienta externa al despliegue de producción.
  3. Revisar el historial de control de versiones para valorar el alcance real de la exposición.

8. Persistencia

No usa base de datos. El estado (tokens de acceso/refresco) se persiste como ficheros JSON en el directorio credentialsdata/, montado sobre un volumen persistente de GCE (gcePersistentDisk, disco pi-credentials) para sobrevivir a reinicios del pod. El propio disco fija la afinidad del pod a la zona europe-west3-a.

9. Procesos programados y mensajería

Jobs activos en PiGenerateCredentialsJob, condicionados a que spring.profiles.active=pro:

JobCronFunción
generateTokensShopee0 0 */4 ? * * (cada 4h)Renueva los tokens de las 5 regiones de Shopee, con reintento (hasta 5 intentos) y pausa de 10s entre llamadas
generateTokenMiravia0 0 */12 ? * * (cada 12h)Renueva el token de Miravia
generateTokenMeliCo0 0 */4 ? * * (cada 4h)Renueva el token de MercadoLibre Colombia

No existe ningún job programado para TikTok (ver hallazgo en la sección 13). PiReGenerateCredentialsJob contiene métodos auxiliares de un solo uso (obtención inicial de código/token) con sus anotaciones @Scheduled comentadas; se ejecutan manualmente durante el desarrollo, no en producción.

10. Ejecución en local

Requisitos previos: JDK 25, Maven, credenciales válidas de Shopee/Miravia/MercadoLibre en un application.properties local.

# Compilar sin tests
mvn -B -DskipTests clean install

# Ejecutar tests
mvn test

# Ejecutar la aplicación localmente (perfil dev, puerto 8081)
mvn spring-boot:run

# Build Docker
docker build -t pi-generate-credentials .

Verificación de que el servicio está operativo: GET /api/token/get?tokenName=MIRAVIA (o cualquier otro tokenName válido) debería devolver el JSON de credenciales si el fichero existe. No hay Actuator configurado.

11. Despliegue

  • Imagen: construida con jib-maven-plugin (base eclipse-temurin:25-jre, containerizingMode=packaged), publicada en europe-west3-docker.pkg.dev/pi-saldum/pi-repo/pi-generate-credentials:<tag>.
  • Orquestación: Kubernetes Deployment (k8s/deployment.yaml, no CronJob) en el clúster GKE pi-cluster-hw, namespace pi, replicas: 1, con afinidad de nodo forzada a la zona europe-west3-a (por el disco persistente pi-credentials).
  • CI/CD (Jenkins): pipeline con 3 etapas — CheckoutBuild & Push (sustituye application-pro.properties por application.properties) → Deploy to GKE.
  • Las variables sensibles se inyectan en el pod mediante un Secret de Kubernetes llamado igual que la app (pi-generate-credentials).

Job de Jenkins: https://jenkins-pi.hawkersco.net/job/pi-generate-credentials/

12. Manejo de errores y logging

GlobalExceptionHandler centraliza el manejo de errores del controlador (no se ha revisado su contenido exhaustivamente por el alcance de este documento, pero está presente como @RestControllerAdvice/similar). Los jobs de renovación capturan excepciones genéricas por marketplace y solo registran un WARNING, sin notificar por Slack ni otro canal — a diferencia de la mayoría de runners del ecosistema, este proyecto no depende de slack-client. Logging mediante java.util.logging.Logger estándar (consola).

13. Notas y consideraciones

  • La integración con TikTok descrita en CLAUDE.md no existe realmente: el documento afirma que hay un job programado diario a medianoche para renovar credenciales de TikTok, y lista variables de entorno (tiktokAuthUrl, tiktokAppKey, etc.) como si estuvieran en uso activo. En el código real: no hay dependencia de ningún cliente TikTok en el pom.xml, no existe ningún método de renovación de credenciales TikTok en PiGenerateCredentialsUtils, y el único método relacionado (PiReGenerateCredentialsJob.generateCodeTiktok()) se limita a imprimir por consola una URL de autorización manual, sin llamar a ninguna API ni usar las propiedades tiktok.* inyectadas. Las propiedades TikTok están declaradas y pobladas en ambos perfiles de configuración pero no tienen ningún efecto funcional real.
  • Secretos hardcodeados en PiReGenerateCredentialsJob: ver alerta de seguridad en la sección 7.
  • Endpoints sin autenticación: /api/token/get y /api/token/generate no requieren ningún tipo de autenticación (no hay spring-boot-starter-security en el pom.xml). Cualquiera con acceso de red al servicio puede leer los tokens de acceso vigentes de los tres marketplaces o forzar su renovación. Dado que expone credenciales operativas de integraciones de negocio, convendría al menos restringir el acceso a nivel de red/Ingress si no se añade autenticación a nivel de aplicación.
  • PiReGenerateCredentialsJob como @Component pese a ser una utilidad manual de un solo uso: aunque sus métodos no se ejecutan automáticamente (todos los @Scheduled están comentados), la clase se instancia en cada arranque de la aplicación como cualquier otro bean gestionado, incluyendo sus dependencias inyectadas; no tiene efecto funcional negativo pero mezcla código de herramienta de desarrollo con el árbol de fuentes de producción.
  • Ver alerta de seguridad en la sección 7 sobre credenciales reales expuestas en application.properties y en código fuente.