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
| Campo | Valor |
|---|---|
artifactId | pi-generate-credentials |
groupId | com.hawkersco |
version | 1.0.25 |
| Java | 25 (maven.compiler.release=25) |
| Spring Boot | 4.0.6 |
| Tipo de artefacto | jar (ejecutable, API web de larga duración) |
| Módulos | No 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)..controller—PiGenerateCredentialsController(único controlador),GlobalExceptionHandler..jobs—PiGenerateCredentialsJob(jobs programados activos),PiReGenerateCredentialsJob(herramienta manual de desarrollo, con todos sus métodos@Scheduledcomentados — ver hallazgo de seguridad en la sección 13)..utils—PiGenerateCredentialsUtils(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
| Dependencia | Propósito |
|---|---|
spring-boot-starter-web | Exposición de los endpoints REST |
com.google.code.gson:gson | Serialización/deserialización de los JSON de credenciales |
com.hawkersco:miravia-client:1.0.25-SNAPSHOT | Cliente para renovar el token de Miravia |
com.hawkersco:shopee-client:1.0.25-SNAPSHOT | Cliente para renovar los tokens de Shopee (firma HMAC) |
com.hawkersco:meli-client:1.0.25-SNAPSHOT | Cliente 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étodo | Ruta | Descripció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
| Sistema | Protocolo | Dirección | Detalle |
|---|---|---|---|
Shopee (partner.shopeemobile.com) | HTTP (ShopeeClient, firma HMAC-SHA256) | Saliente | Renovación de token de acceso por región (MY, PH, SG, TH, VN) |
Miravia (api.miravia.es) | HTTP (MiraviaClient, firma propia) | Saliente | Renovación de token de acceso |
MercadoLibre (api.mercadolibre.com) | HTTP (MeliTokenClient) | Saliente | Renovació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).
| Clave | Descripción | Ejemplo (producción) |
|---|---|---|
miravia.client.url / .appkey / .appsecret | Credenciales de Miravia | ${miraviaClientUrl}, etc. |
shopee.api.host / .partnerId / .partnerKey | Credenciales 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 / .clientsecret | Credenciales de MercadoLibre | ${meliCoCredUrl}, etc. |
tiktok.auth.url / .app-key / .app-secret / .grant-type | Credenciales 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:
application.propertieslocal 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.- Secretos reales hardcodeados directamente en
PiReGenerateCredentialsJob.java: el métodogenerateTokeMiravia()contiene elapp-keyyapp-secretde 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 elpartnerIdde Shopee como literal numérico. A diferencia de las credenciales enapplication.properties(rotables víaSecretde Kubernetes sin recompilar), estas quedan fijas en el binario compilado. Aunque esta clase es una herramienta de desarrollo con todos los métodos@Scheduledcomentados (no se ejecutan automáticamente), sigue siendo un@Componentque Spring instancia en cada arranque, y el código con los secretos permanece en el repositorio y en el artefacto desplegado.
Se recomienda:
- Rotar las credenciales de Shopee, Miravia y MercadoLibre expuestas en
application.properties. - Eliminar (o al menos enmascarar) los secretos hardcodeados en
PiReGenerateCredentialsJob, y valorar si esta clase debería seguir siendo un@Componentgestionado por Spring o convertirse en un script/herramienta externa al despliegue de producción. - 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:
| Job | Cron | Función |
|---|---|---|
generateTokensShopee | 0 0 */4 ? * * (cada 4h) | Renueva los tokens de las 5 regiones de Shopee, con reintento (hasta 5 intentos) y pausa de 10s entre llamadas |
generateTokenMiravia | 0 0 */12 ? * * (cada 12h) | Renueva el token de Miravia |
generateTokenMeliCo | 0 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(baseeclipse-temurin:25-jre,containerizingMode=packaged), publicada eneurope-west3-docker.pkg.dev/pi-saldum/pi-repo/pi-generate-credentials:<tag>. - Orquestación: Kubernetes
Deployment(k8s/deployment.yaml, noCronJob) en el clúster GKEpi-cluster-hw, namespacepi,replicas: 1, con afinidad de nodo forzada a la zonaeurope-west3-a(por el disco persistentepi-credentials). - CI/CD (Jenkins): pipeline con 3 etapas —
Checkout→Build & Push(sustituyeapplication-pro.propertiesporapplication.properties) →Deploy to GKE. - Las variables sensibles se inyectan en el pod mediante un
Secretde 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.mdno 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 elpom.xml, no existe ningún método de renovación de credenciales TikTok enPiGenerateCredentialsUtils, 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 propiedadestiktok.*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/gety/api/token/generateno requieren ningún tipo de autenticación (no hayspring-boot-starter-securityen elpom.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. PiReGenerateCredentialsJobcomo@Componentpese a ser una utilidad manual de un solo uso: aunque sus métodos no se ejecutan automáticamente (todos los@Scheduledestá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.propertiesy en código fuente.