feat: enhance login process with IP capture and improved error handling
- Implemented an internal mutation for login that captures the user's IP address and user agent for better security and tracking. - Enhanced the HTTP login endpoint to extract and log client IP, improving the overall authentication process. - Added validation for IP addresses to ensure only valid formats are recorded, enhancing data integrity. - Updated the login mutation to handle rate limiting and user status checks more effectively, providing clearer feedback on login attempts.
This commit is contained in:
@@ -14,7 +14,64 @@ export function getUserAgent(): string {
|
||||
}
|
||||
|
||||
/**
|
||||
* Tenta obter o IP local usando WebRTC
|
||||
* Valida se uma string tem formato de IP válido
|
||||
*/
|
||||
function isValidIPFormat(ip: string): boolean {
|
||||
if (!ip || ip.length < 7) return false; // IP mínimo: "1.1.1.1" = 7 chars
|
||||
|
||||
// Validar IPv4
|
||||
const ipv4Regex = /^(\d{1,3}\.){3}\d{1,3}$/;
|
||||
if (ipv4Regex.test(ip)) {
|
||||
const parts = ip.split('.');
|
||||
return parts.length === 4 && parts.every(part => {
|
||||
const num = parseInt(part, 10);
|
||||
return !isNaN(num) && num >= 0 && num <= 255;
|
||||
});
|
||||
}
|
||||
|
||||
// Validar IPv6 básico (formato simplificado)
|
||||
const ipv6Regex = /^([0-9a-fA-F]{0,4}:){2,7}[0-9a-fA-F]{0,4}$|^::[0-9a-fA-F]{0,4}(:[0-9a-fA-F]{0,4}){0,6}$|^[0-9a-fA-F]{0,4}::[0-9a-fA-F]{0,4}(:[0-9a-fA-F]{0,4}){0,5}$/;
|
||||
if (ipv6Regex.test(ip)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifica se um IP é local/privado
|
||||
*/
|
||||
function isLocalIP(ip: string): boolean {
|
||||
// IPs locais/privados
|
||||
return (
|
||||
ip.startsWith('127.') ||
|
||||
ip.startsWith('192.168.') ||
|
||||
ip.startsWith('10.') ||
|
||||
ip.startsWith('172.16.') ||
|
||||
ip.startsWith('172.17.') ||
|
||||
ip.startsWith('172.18.') ||
|
||||
ip.startsWith('172.19.') ||
|
||||
ip.startsWith('172.20.') ||
|
||||
ip.startsWith('172.21.') ||
|
||||
ip.startsWith('172.22.') ||
|
||||
ip.startsWith('172.23.') ||
|
||||
ip.startsWith('172.24.') ||
|
||||
ip.startsWith('172.25.') ||
|
||||
ip.startsWith('172.26.') ||
|
||||
ip.startsWith('172.27.') ||
|
||||
ip.startsWith('172.28.') ||
|
||||
ip.startsWith('172.29.') ||
|
||||
ip.startsWith('172.30.') ||
|
||||
ip.startsWith('172.31.') ||
|
||||
ip.startsWith('169.254.') || // Link-local
|
||||
ip === '::1' ||
|
||||
ip.startsWith('fe80:') // IPv6 link-local
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tenta obter o IP usando WebRTC
|
||||
* Prioriza IP público, mas retorna IP local se não encontrar
|
||||
* Esta função não usa API externa, mas pode falhar em alguns navegadores
|
||||
* Retorna undefined se não conseguir obter
|
||||
*/
|
||||
@@ -32,31 +89,87 @@ export async function getLocalIP(): Promise<string | undefined> {
|
||||
});
|
||||
|
||||
let resolved = false;
|
||||
let foundIPs: string[] = [];
|
||||
let publicIP: string | undefined = undefined;
|
||||
let localIP: string | undefined = undefined;
|
||||
|
||||
const timeout = setTimeout(() => {
|
||||
if (!resolved) {
|
||||
resolved = true;
|
||||
pc.close();
|
||||
resolve(undefined);
|
||||
// Priorizar IP público, mas retornar local se não houver
|
||||
resolve(publicIP || localIP || undefined);
|
||||
}
|
||||
}, 3000);
|
||||
}, 5000); // Aumentar timeout para 5 segundos
|
||||
|
||||
pc.onicecandidate = (event) => {
|
||||
if (event.candidate && !resolved) {
|
||||
const candidate = event.candidate.candidate;
|
||||
// Regex para extrair IP local (IPv4)
|
||||
const ipMatch = candidate.match(/([0-9]{1,3}(\.[0-9]{1,3}){3})/);
|
||||
if (ipMatch && ipMatch[1]) {
|
||||
const ip = ipMatch[1];
|
||||
// Verificar se não é IP localhost (127.0.0.1 ou ::1)
|
||||
if (!ip.startsWith('127.') && !ip.startsWith('::1')) {
|
||||
if (!resolved) {
|
||||
resolved = true;
|
||||
clearTimeout(timeout);
|
||||
pc.close();
|
||||
resolve(ip);
|
||||
|
||||
// Regex mais rigorosa para IPv4 - deve ser um IP completo e válido
|
||||
// Formato: X.X.X.X onde X é 0-255
|
||||
const ipv4Match = candidate.match(/\b([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})\b/);
|
||||
|
||||
// Regex para IPv6 - mais específica
|
||||
const ipv6Match = candidate.match(/\b([0-9a-fA-F]{1,4}(?::[0-9a-fA-F]{1,4}){2,7}|::[0-9a-fA-F]{1,4}(?::[0-9a-fA-F]{1,4}){0,6}|[0-9a-fA-F]{1,4}::[0-9a-fA-F]{1,4}(?::[0-9a-fA-F]{1,4}){0,5})\b/);
|
||||
|
||||
let ip: string | undefined = undefined;
|
||||
|
||||
if (ipv4Match && ipv4Match[1]) {
|
||||
const candidateIP = ipv4Match[1];
|
||||
// Validar se cada octeto está entre 0-255
|
||||
const parts = candidateIP.split('.');
|
||||
if (parts.length === 4 && parts.every(part => {
|
||||
const num = parseInt(part, 10);
|
||||
return !isNaN(num) && num >= 0 && num <= 255;
|
||||
})) {
|
||||
ip = candidateIP;
|
||||
}
|
||||
} else if (ipv6Match && ipv6Match[1]) {
|
||||
// Validar formato básico de IPv6
|
||||
const candidateIP = ipv6Match[1];
|
||||
if (candidateIP.includes(':') && candidateIP.length >= 3) {
|
||||
ip = candidateIP;
|
||||
}
|
||||
}
|
||||
|
||||
// Validar se o IP é válido antes de processar
|
||||
if (ip && isValidIPFormat(ip) && !foundIPs.includes(ip)) {
|
||||
foundIPs.push(ip);
|
||||
|
||||
// Ignorar localhost
|
||||
if (ip.startsWith('127.') || ip === '::1') {
|
||||
return;
|
||||
}
|
||||
|
||||
// Separar IPs públicos e locais
|
||||
if (isLocalIP(ip)) {
|
||||
if (!localIP) {
|
||||
localIP = ip;
|
||||
}
|
||||
} else {
|
||||
// IP público encontrado!
|
||||
if (!publicIP) {
|
||||
publicIP = ip;
|
||||
// Se encontrou IP público, podemos resolver mais cedo
|
||||
if (!resolved) {
|
||||
resolved = true;
|
||||
clearTimeout(timeout);
|
||||
pc.close();
|
||||
resolve(publicIP);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (event.candidate === null) {
|
||||
// No more candidates
|
||||
if (!resolved) {
|
||||
resolved = true;
|
||||
clearTimeout(timeout);
|
||||
pc.close();
|
||||
// Retornar IP público se encontrou, senão local
|
||||
resolve(publicIP || localIP || undefined);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -69,10 +182,11 @@ export async function getLocalIP(): Promise<string | undefined> {
|
||||
resolved = true;
|
||||
clearTimeout(timeout);
|
||||
pc.close();
|
||||
resolve(undefined);
|
||||
resolve(publicIP || localIP || undefined);
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
console.warn("Erro ao obter IP via WebRTC:", error);
|
||||
resolve(undefined);
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user