ikl-sdk
v0.0.5
Published
IKLicensing SDK client with online/offline validation, signed licenses, device binding, software matching, and watchdog support.
Downloads
729
Readme
IKLicensing Client
Cliente Node.js para activar, almacenar y validar licencias de software. La libreria soporta activacion online, validacion online/offline, solicitudes offline, importacion de licencias offline, verificacion de firma digital, validacion por dispositivo, validacion por nombre/version de software y watchdog manual de integridad.
Requisitos
- Node.js con soporte para ES Modules.
- Backend de licencias compatible con los endpoints
/activatey/validate. - Llave publica RSA configurada en
src/crypto/PublicKey.js. - Dependencias instaladas con
npm install.
Ejemplos
La carpeta examples/ contiene scripts ejecutables para los flujos principales:
| Archivo | Caso de uso |
| --- | --- |
| examples/activate-online.js | Activacion online con verificacion antes de guardar. |
| examples/validate-startup.js | Validacion recomendada al iniciar la aplicacion. |
| examples/validate-offline.js | Validacion local sin backend. |
| examples/offline-generate-request.js | Generacion de solicitud offline. |
| examples/offline-import-license.js | Importacion y validacion de licencia offline. |
| examples/watchdog-manual.js | Inicio manual del watchdog. |
| examples/request-error-handling.js | Manejo de errores tipados. |
| examples/check-fingerprint.js | Consulta del fingerprint actual. |
| examples/clear-license.js | Eliminacion de licencia local. |
Consulta examples/README.md para comandos y variables de entorno.
Conceptos
La licencia guardada tiene esta forma general:
{
"payload": {
"licenseId": 5,
"licenseKey": "VIDEO-TEST-0002-EEEE",
"type": "PRO",
"fingerprint": "5e5c97012c5ef30ad082a07be45a43a5ea1722861f819fecf6405f2f75bf5abb",
"expiresAt": "2027-12-31",
"softwareName": "VideoConverter",
"softwareVersion": "1.0.*"
},
"signature": "..."
}El cliente valida:
- Que exista una licencia.
- Que la firma digital sea valida.
- Que no este expirada.
- Que pertenezca al software que se esta ejecutando.
- Que pertenezca a la version del software o a un patron compatible.
- Que pertenezca al dispositivo actual mediante fingerprint.
Versiones con comodines
softwareName se compara de forma exacta. softwareVersion acepta coincidencia exacta o comodines * dentro de la licencia:
| Version en licencia | Version actual | Resultado |
| --- | --- | --- |
| 1.0.0 | 1.0.0 | Valida |
| 1.0.* | 1.0.7 | Valida |
| 1.* | 1.2.3 | Valida |
| 2.* | 1.9.0 | Invalida |
Configuracion basica
Ejemplo de integracion usando los modulos de la libreria:
import {
LicenseClient,
FileStorage,
HardwareProvider,
ApiClient,
ActivationService,
ValidationService,
OfflineRequestService,
OfflineLicenseImporter,
PUBLIC_KEY
} from "ikl-sdk";
const SOFTWARE_NAME = "PhotoEditor";
const SOFTWARE_VERSION = "2.5.1";
const apiClient = new ApiClient({
baseURL: "https://tu-api.com/api",
timeout: 10000
});
const storage = new FileStorage({
softwareName: SOFTWARE_NAME
});
const hardwareProvider = new HardwareProvider();
const activationService = new ActivationService({
apiClient,
publicKey: PUBLIC_KEY,
hardwareProvider,
storage,
softwareName: SOFTWARE_NAME,
softwareVersion: SOFTWARE_VERSION
});
const validationService = new ValidationService({
apiClient,
hardwareProvider,
storage,
softwareName: SOFTWARE_NAME,
softwareVersion: SOFTWARE_VERSION
});
const offlineRequestService = new OfflineRequestService({
hardwareProvider,
softwareName: SOFTWARE_NAME,
softwareVersion: SOFTWARE_VERSION
});
const offlineLicenseImporter = new OfflineLicenseImporter({
storage
});
const client = new LicenseClient({
publicKey: PUBLIC_KEY,
storage,
hardwareProvider,
activationService,
validationService,
offlineRequestService,
offlineLicenseImporter,
softwareName: SOFTWARE_NAME,
softwareVersion: SOFTWARE_VERSION,
watchdogIntervalMs: 5 * 60 * 1000
});Activacion online
activateOnline(licenseKey) llama al backend y recibe una licencia generada online. Antes de guardar la licencia, el cliente ejecuta todas las verificaciones locales contra la licencia recibida:
- Firma digital.
- Expiracion.
- Software y version.
- Fingerprint del dispositivo.
Solo si la licencia recibida es valida se guarda en disco.
try {
const activation = await client.activateOnline("PHOTO-TEST-0001-AAAA");
if (!activation.success) {
console.error("No se pudo activar", activation.reason || activation.error);
process.exit(1);
}
console.log("Licencia activada");
} catch (error) {
console.error(error.code, error.message);
}Validacion
Validacion automatica online/offline
validate() intenta validar online. Si el backend confirma que la licencia sigue activa, igualmente ejecuta la validacion local completa contra license.dat. Si no puede validar online por un error de red no definitivo, cae a validacion offline.
const result = await client.validate();
if (!result.valid) {
console.error("Licencia invalida:", result.reason);
process.exit(1);
}
console.log("Licencia valida", result.license.payload);Validacion online
const result = await client.validateOnline();El endpoint /validate recibe:
{
"licenseId": 5,
"fingerprint": "...",
"softwareName": "PhotoEditor",
"softwareVersion": "2.5.1"
}Si el backend responde valid: true, el cliente valida localmente la licencia guardada antes de devolver exito.
Validacion offline
const result = await client.validateOffline();Esta validacion no llama al backend. Usa la licencia almacenada localmente y verifica firma, expiracion, software, version y fingerprint.
Flujo offline
Cuando una maquina no puede activar online, genera una solicitud offline:
const { path, request } = await client.generateOfflineRequest("PHOTO-TEST-0001-AAAA");
console.log("Solicitud generada en:", path);
console.log(request.toJSON());La solicitud incluye:
licenseKeyfingerprinttimestampdeviceNamesoftwareNamesoftwareVersion
Luego se importa una licencia emitida por el backend:
const license = await client.importOfflineLicense("/ruta/a/license.dat");
console.log("Licencia importada", license);Despues de importar, se recomienda ejecutar:
const validation = await client.validateOffline();Watchdog manual
El watchdog no se inicia automaticamente. Debe activarse manualmente cuando la aplicacion ya decidio que corresponde monitorear la integridad de la licencia.
client.startWatchdog({
intervalMs: 60 * 1000,
runImmediately: true,
onValidation: (result) => {
console.log("Watchdog:", result.valid, result.reason);
},
onInvalid: (result) => {
console.error("Licencia invalida durante ejecucion:", result.reason);
process.exit(1);
},
onError: (error) => {
console.error("Error de watchdog:", error.code, error.message);
}
});Para detenerlo:
client.stopWatchdog();El watchdog usa ConnectivityService para detectar internet con:
https://www.google.com/generate_204Si hay conexion, valida online y luego localmente. Si no hay conexion, valida offline. El watchdog evita validaciones solapadas si una ejecucion tarda mas que el intervalo.
Almacenamiento local
FileStorage guarda los archivos en una carpeta dependiente del sistema operativo:
| Sistema | Ruta base |
| --- | --- |
| Windows | %APPDATA%/MyLicenseApp |
| macOS | ~/Library/Application Support/MyLicenseApp |
| Linux | ~/.mylicenseapp |
Archivos usados:
| Archivo | Uso |
| --- | --- |
| license/license.dat | Licencia activa |
| license/request.dat | Solicitud offline |
| license/validation.json | Cache de validacion, disponible en storage |
API principal
| Metodo | Descripcion |
| --- | --- |
| activateOnline(licenseKey) | Activa online y guarda la licencia solo si pasa todas las verificaciones locales. |
| validate() | Intenta validar online y cae a offline cuando corresponde. |
| validateOnline() | Valida contra el backend y luego valida localmente la licencia guardada. |
| validateOffline() | Valida localmente sin llamar al backend. |
| generateOfflineRequest(licenseKey) | Genera request.dat para activacion offline. |
| importOfflineLicense(filePath) | Importa una licencia desde archivo. |
| getFingerprint() | Devuelve el fingerprint del dispositivo. |
| getLicense() | Devuelve la licencia guardada o null. |
| isActivated() | Indica si existe una licencia local. |
| clearLicense() | Elimina la licencia local. |
| startWatchdog(options) | Inicia manualmente el watchdog. |
| stopWatchdog() | Detiene el watchdog. |
| runWatchdogValidation() | Ejecuta una validacion de watchdog una sola vez. |
Resultado de validacion
Las validaciones devuelven un objeto compatible con:
{
valid: true,
reason: null,
status: "VALID",
license: {
payload: {},
signature: "..."
}
}Cuando falla:
{
valid: false,
reason: "INVALID_SOFTWARE",
status: "INVALID",
license: null
}Errores y codigos
Errores de licencia
Estos codigos aparecen normalmente como ValidationResult.reason. Tambien pueden preservarse como error.code cuando el backend los devuelve en el body.
| Codigo | Significado | Causa comun | Accion sugerida |
| --- | --- | --- | --- |
| VALID | Licencia valida. | Todas las verificaciones pasaron. | Permitir ejecucion. |
| INVALID | Licencia invalida sin razon especifica. | Falla generica o respuesta incompleta. | Reintentar validacion o pedir reactivacion. |
| NO_LICENSE | No existe licencia local. | license.dat no fue creado o fue eliminado. | Activar online, generar request offline o importar licencia. |
| INVALID_SIGNATURE | La firma digital no corresponde al payload. | Licencia alterada, corrupta o firmada con otra llave privada. | Rechazar ejecucion y solicitar una licencia nueva. |
| EXPIRED | La licencia expiro. | payload.expiresAt es anterior a la fecha actual. | Renovar licencia. |
| INVALID_DEVICE | La licencia pertenece a otro dispositivo. | El fingerprint actual no coincide con payload.fingerprint. | Reemitir licencia para este equipo. |
| INVALID_SOFTWARE | La licencia pertenece a otro software o version incompatible. | softwareName no coincide o softwareVersion no cumple el patron. | Usar una licencia emitida para este producto/version. |
Errores de request
ApiClient normaliza errores HTTP, red y timeout. Todos extienden RequestError y exponen name, code, status, message, details y toJSON().
| Clase | Codigo por defecto | HTTP | Significado | Accion sugerida |
| --- | --- | --- | --- | --- |
| BadRequestError | BAD_REQUEST | 400 | Request mal formado. | Revisar payload enviado al backend. |
| UnauthorizedError | UNAUTHORIZED | 401 | No autorizado. | Revisar credenciales, tokens o configuracion del backend. |
| ForbiddenError | FORBIDDEN | 403 | El backend rechazo la operacion. | Revisar details; puede contener un codigo de negocio como INVALID_SOFTWARE. |
| NotFoundError | NOT_FOUND | 404 | Endpoint o recurso no encontrado. | Revisar baseURL y rutas del backend. |
| ConflictError | CONFLICT | 409 | Conflicto de estado. | Revisar si la licencia ya esta activada o asignada. |
| UnprocessableEntityError | UNPROCESSABLE_ENTITY | 422 | El backend entiende el request, pero no lo puede procesar. | Mostrar el mensaje del backend y corregir datos. |
| ServerError | SERVER_ERROR | 5xx | Error del servidor. | Reintentar luego o revisar logs del backend. |
| RequestError | REQUEST_ERROR | Otros | Error HTTP no clasificado. | Revisar status y details. |
| NetworkError | NETWORK_ERROR | N/A | No hubo respuesta HTTP. | Verificar conexion, DNS, firewall o backend apagado. |
| TimeoutError | REQUEST_TIMEOUT | N/A | El request excedio el timeout. | Reintentar, aumentar timeout o revisar latencia. |
Codigos preservados desde el backend
Si el backend responde con esta forma:
{
"success": false,
"error": {
"code": "INVALID_SOFTWARE",
"message": "Invalid software"
}
}El cliente preserva el codigo de negocio como error.code, incluso si el HTTP status es, por ejemplo, 403.
try {
await client.activateOnline("PHOTO-TEST-0001-AAAA");
} catch (error) {
console.error(error.name); // ForbiddenError
console.error(error.code); // INVALID_SOFTWARE
console.error(error.status); // 403
console.error(error.message); // Invalid software
console.error(error.details); // Respuesta completa del backend
}Contrato esperado del backend
POST /activate
Request:
{
"licenseKey": "PHOTO-TEST-0001-AAAA",
"fingerprint": "...",
"softwareName": "PhotoEditor",
"softwareVersion": "2.5.1"
}Respuesta exitosa:
{
"success": true,
"data": {
"payload": {
"licenseId": 1,
"licenseKey": "PHOTO-TEST-0001-AAAA",
"fingerprint": "...",
"expiresAt": "2027-12-31",
"softwareName": "PhotoEditor",
"softwareVersion": "2.5.*"
},
"signature": "..."
}
}POST /validate
Request:
{
"licenseId": 1,
"fingerprint": "...",
"softwareName": "PhotoEditor",
"softwareVersion": "2.5.1"
}Respuesta exitosa:
{
"valid": true
}Respuesta con error de negocio:
{
"success": false,
"error": {
"code": "INVALID_SOFTWARE",
"message": "Invalid software"
}
}Buenas practicas de integracion
- Definir
softwareNameysoftwareVersioncomo constantes de build de la aplicacion. - No guardar licencias recibidas del backend sin pasar por
ActivationService. - Ejecutar
validate()al iniciar la aplicacion. - Iniciar
startWatchdog()manualmente solo cuando la aplicacion ya este lista para monitorear integridad. - En
onWatchdogInvalid, cerrar la aplicacion, bloquear funcionalidades o limpiar sesion segun el modelo de seguridad. - Mostrar al usuario mensajes amigables, pero registrar
error.code,error.statusyerror.detailspara soporte. - Tratar
INVALID_SIGNATURE,INVALID_DEVICEeINVALID_SOFTWAREcomo errores definitivos. - Tratar
NETWORK_ERRORyREQUEST_TIMEOUTcomo errores temporales.
