ci-validation
Version:
🇺🇾 Complete TypeScript/JavaScript library for validating Uruguayan CI (Cédula de Identidad) with official algorithm and government service integration
428 lines • 19.4 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.LaEspanolaService = void 0;
const axios_1 = __importDefault(require("axios"));
const crypto = __importStar(require("crypto"));
/**
* Service for checking user existence in La Española (ASESP) system
* Uses the autogestion portal API to verify CI registration
*/
class LaEspanolaService {
constructor() {
this.baseUrl = "https://autogestion.asesp.com.uy";
this.timeout = 30000; // 30 seconds
this.sessionData = { cookies: "" };
}
/**
* Initialize session by getting the homepage and extracting necessary tokens
* @returns Session data with cookies and tokens
*/
async initializeSession() {
try {
console.log("🔍 Initializing La Española session...");
// First, get the login page to establish session
const loginPageUrl = `${this.baseUrl}/Autogestion/`;
let initialResponse = await axios_1.default.get(loginPageUrl, {
timeout: this.timeout,
maxRedirects: 5,
headers: {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36",
Accept: "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8",
"Accept-Language": "es-ES,es;q=0.9",
"Accept-Encoding": "gzip, deflate, br",
Connection: "keep-alive",
"Upgrade-Insecure-Requests": "1",
"Sec-Fetch-Dest": "document",
"Sec-Fetch-Mode": "navigate",
"Sec-Fetch-Site": "none",
"Sec-Fetch-User": "?1",
},
});
console.log(`📡 Initial response status: ${initialResponse.status}`);
console.log(`📄 Response headers:`, initialResponse.headers);
// Extract cookies from response headers
const cookies = this.extractCookiesFromResponse(initialResponse);
console.log(`🍪 Extracted cookies:`, cookies);
// Store cookies in session
this.sessionData.cookies = this.formatCookiesString(cookies);
// Extract tokens from HTML content
initialResponse = await axios_1.default.get(loginPageUrl, {
timeout: this.timeout,
maxRedirects: 5,
headers: {
Cookie: this.sessionData.cookies,
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36",
Accept: "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8",
"Accept-Language": "es-ES,es;q=0.9",
"Accept-Encoding": "gzip, deflate, br",
Connection: "keep-alive",
"Upgrade-Insecure-Requests": "1",
"Sec-Fetch-Dest": "document",
"Sec-Fetch-Mode": "navigate",
"Sec-Fetch-Site": "none",
"Sec-Fetch-User": "?1",
},
});
const tokens = this.extractTokensFromHTML(initialResponse.data);
console.log(`🔑 Extracted tokens:`, tokens);
// Update session data with tokens
this.sessionData.ajaxSecurityToken = tokens.ajaxSecurityToken;
this.sessionData.gxAuthToken = tokens.gxAuthToken;
console.log("✅ Session initialized successfully");
console.log(`📋 Session summary:`);
console.log(` Cookies: ${this.sessionData.cookies}`);
console.log(` AJAX_SECURITY_TOKEN: ${this.sessionData.ajaxSecurityToken}`);
console.log(` X-GXAUTH-TOKEN: ${this.sessionData.gxAuthToken}`);
return true;
}
catch (error) {
console.error("❌ Error initializing session:", error);
throw new Error("No se pudo inicializar la sesión con La Española");
}
}
/**
* Extract cookies from HTTP response headers
* @param response - Axios response object
* @returns Object containing extracted cookies
*/
extractCookiesFromResponse(response) {
const cookies = {};
if (response.headers["set-cookie"]) {
response.headers["set-cookie"].forEach((cookie) => {
const [nameValue] = cookie.split(";");
const [name, value] = nameValue.split("=");
if (name && value) {
cookies[name.trim()] = value.trim();
}
});
}
return cookies;
}
/**
* Format cookies object into a cookie string for HTTP headers
* @param cookies - Object containing cookie name-value pairs
* @returns Formatted cookie string
*/
formatCookiesString(cookies) {
return Object.entries(cookies)
.map(([name, value]) => `${name}=${value}`)
.join("; ");
}
/**
* Extract authentication tokens from HTML response
* @param htmlContent - HTML content from the response
* @returns Object with extracted tokens
*/
extractTokensFromHTML(htmlContent) {
try {
// Extract AJAX_SECURITY_TOKEN
const ajaxTokenMatch = htmlContent.match(/AJAX_SECURITY_TOKEN['"]\s*:\s*['"]([^'"]+)['"]/);
if (ajaxTokenMatch) {
this.sessionData.ajaxSecurityToken = ajaxTokenMatch[1];
}
// Extract X-GXAUTH-TOKEN
const gxAuthTokenMatch = htmlContent.match(/X-GXAUTH-TOKEN['"]\s*:\s*['"]([^'"]+)['"]/);
if (gxAuthTokenMatch) {
this.sessionData.gxAuthToken = gxAuthTokenMatch[1];
}
// If tokens not found in HTML, use sample tokens (they might be generated dynamically)
if (!this.sessionData.ajaxSecurityToken) {
this.sessionData.ajaxSecurityToken = this.generateSecurityToken();
}
if (!this.sessionData.gxAuthToken) {
this.sessionData.gxAuthToken = this.generateAuthToken();
}
return {
ajaxSecurityToken: this.sessionData.ajaxSecurityToken,
gxAuthToken: this.sessionData.gxAuthToken,
};
}
catch (error) {
console.error("Error extracting tokens from HTML:", error);
return {
ajaxSecurityToken: "",
gxAuthToken: "",
};
}
}
/**
* Generate a security token similar to the format used by La Española
* @returns Generated security token
*/
generateSecurityToken() {
const timestamp = Date.now().toString();
const randomData = Math.random().toString(36).substring(2);
const combined = timestamp + randomData;
return crypto.createHash("md5").update(combined).digest("hex");
}
/**
* Generate an auth token similar to the format used by La Española
* @returns Generated auth token
*/
generateAuthToken() {
const timestamp = Date.now().toString(16);
return timestamp.substring(0, 8);
}
/**
* Generate URL hash parameter based on session and timestamp
* @returns Generated hash parameter
*/
generateUrlHash() {
const timestamp = Date.now();
// Use a simpler hash generation approach similar to the working curl example
const baseString = `${this.sessionData.ajaxSecurityToken}${timestamp}`;
const hash = crypto.createHash("md5").update(baseString).digest("hex");
return `${hash.substring(0, 8)},gx-no-cache=${timestamp}`;
}
/**
* Check if a user exists in La Española system
* @param request - Request containing CI to check
* @returns Promise with user verification result
*/
async checkUser(request) {
const startTime = Date.now();
try {
// Initialize session if needed
if (!this.sessionData.cookies) {
const sessionInitialized = await this.initializeSession();
if (!sessionInitialized) {
return {
success: false,
hasUser: false,
error: "Failed to initialize session with La Española",
};
}
}
// Prepare the request payload
const payload = {
MPage: false,
cmpCtx: "",
parms: [request.ci, false, "", "", "", "IdentityProvider.GAMRemoteLogin", { Success: false }, 6],
hsh: ["80ba4750"],
objClass: "identityprovider.gamremotelogin",
pkgName: "uy.com.asesp",
events: ["ENTER"],
grids: {},
};
// Generate URL hash
const urlHash = this.generateUrlHash();
const requestUrl = `${this.baseUrl}/Autogestion/?${urlHash}`;
console.log("Making La Española request to:", requestUrl);
console.log("Request payload:", JSON.stringify(payload, null, 2));
// Make the API request
const response = await axios_1.default.post(requestUrl, payload, {
headers: {
AJAX_SECURITY_TOKEN: this.sessionData.ajaxSecurityToken,
Accept: "*/*",
"Accept-Language": "es-ES,es;q=0.9,bg;q=0.8",
"Cache-Control": "no-cache",
Connection: "keep-alive",
"Content-Type": "application/json",
Cookie: this.sessionData.cookies,
GxAjaxRequest: "1",
Origin: "https://autogestion.asesp.com.uy",
Pragma: "no-cache",
Referer: "https://autogestion.asesp.com.uy/Autogestion/",
"Sec-Fetch-Dest": "empty",
"Sec-Fetch-Mode": "cors",
"Sec-Fetch-Site": "same-origin",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36",
"X-GXAUTH-TOKEN": this.sessionData.gxAuthToken,
"sec-ch-ua": '"Not;A=Brand";v="99", "Google Chrome";v="139", "Chromium";v="139"',
"sec-ch-ua-mobile": "?0",
"sec-ch-ua-platform": '"Windows"',
},
timeout: 30000,
});
const executionTime = Date.now() - startTime;
console.log("La Española response status:", response.status);
return this.parseResponse(response.data, request.ci, executionTime);
}
catch (error) {
const executionTime = Date.now() - startTime;
if (error.response) {
console.error("Error response status:", error.response.status);
if (error.response.status === 401 || error.response.status === 403) {
return {
success: false,
hasUser: false,
error: "Acceso denegado: API requiere autenticación válida",
};
}
else if (error.response.status >= 500) {
return {
success: false,
hasUser: false,
error: "Error del servidor La Española",
};
}
}
return {
success: false,
hasUser: false,
error: error instanceof Error ? error.message : "Error desconocido al consultar La Española",
};
}
}
/**
* Parse the raw API response into our standardized format
* @param apiResponse - Raw response from La Española API
* @param ci - The CI number that was queried
* @param executionTime - Time taken for the request
* @returns Parsed LaEspanolaResponse
*/
parseResponse(apiResponse, ci, executionTime) {
try {
// Check if response contains user data
if (apiResponse.gxValues && Array.isArray(apiResponse.gxValues) && apiResponse.gxValues.length > 0) {
const userData = apiResponse.gxValues[0];
// Check for successful authentication or user found
if (userData.AV117K2BContext && (userData.AV117K2BContext.UserCode || userData.AV117K2BContext.UserUUID || userData.AV117K2BContext.UsuarioDocumento)) {
const k2bContext = userData.AV117K2BContext;
const extractedUserData = {
isValidUser: true,
userCode: k2bContext.UserCode || undefined,
userUUID: k2bContext.UserUUID || undefined,
firstName: k2bContext.UserFirstName || undefined,
lastName: k2bContext.UserLastName || undefined,
matricula: k2bContext.UsuarioMatricula || undefined,
aeId: k2bContext.UsuarioAEId || undefined,
tipoDocumento: k2bContext.UsuarioTipoDocumentoCodigo || undefined,
paisCodigo: k2bContext.UsuarioPaisCodigo || undefined,
documento: k2bContext.UsuarioDocumento || undefined,
};
return {
success: true,
hasUser: true,
member: {
userData: extractedUserData,
ci,
status: "registered",
executionTime,
},
};
}
}
// Check for error messages
if (apiResponse.gxMessages && apiResponse.gxMessages.MAIN && Array.isArray(apiResponse.gxMessages.MAIN)) {
const messages = apiResponse.gxMessages.MAIN;
for (const message of messages) {
if (message.text) {
const messageText = message.text.toLowerCase();
// Common error messages indicating user not found
if (messageText.includes("usuario o contraseña incorrecta") || messageText.includes("no encontrado") || messageText.includes("no existe") || messageText.includes("inválido") || messageText.includes("no registrado")) {
return {
success: true,
hasUser: false,
error: message.text,
};
}
}
}
}
// Check for result SDT errors
if (apiResponse.gxValues && apiResponse.gxValues[0] && apiResponse.gxValues[0].AV93ResultadoSDT) {
const resultado = apiResponse.gxValues[0].AV93ResultadoSDT;
if (!resultado.Success && resultado.Detalle) {
// Check if error indicates user not found vs authentication error
const descripcion = resultado.Detalle.Descripcion || "";
if (descripcion.includes("Usuario o contraseña incorrecta")) {
return {
success: true,
hasUser: false,
error: "no_user",
};
}
return {
success: false,
hasUser: false,
error: descripcion,
};
}
}
// Check for CAPTCHA requirement
if (apiResponse.gxProps && Array.isArray(apiResponse.gxProps) && apiResponse.gxProps.length > 0) {
const props = apiResponse.gxProps[0];
if (props.vCAPTCHAIMAGE && props.vCAPTCHAIMAGE.Visible === "1") {
return {
success: false,
hasUser: false,
error: "CAPTCHA requerido para continuar",
};
}
}
// Default case - could not determine user status
return {
success: true,
hasUser: false,
error: "no_user",
};
}
catch (parseError) {
console.error("Error parsing La Española response:", parseError);
return {
success: false,
hasUser: false,
error: "Error al procesar la respuesta de La Española",
};
}
}
/**
* Check if the service is available
* @returns Promise indicating service availability
*/
async isServiceAvailable() {
try {
const response = await axios_1.default.get(`${this.baseUrl}/Autogestion/`, {
timeout: 10000,
headers: {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36",
},
});
return response.status === 200;
}
catch (error) {
console.error("La Española service availability check failed:", error);
return false;
}
}
}
exports.LaEspanolaService = LaEspanolaService;
exports.default = LaEspanolaService;
//# sourceMappingURL=LaEspanola.js.map