using System;
using System.Collections.Generic;
using System.Configuration;
using System.Net.Http;
using System.Threading.Tasks;
using System.Web;
using Newtonsoft.Json;
namespace TechSec602
{
///
/// C# Client Library for 602TechSec API v2 Integration
/// Provides easy integration for verification calls in web applications
///
public class TechSec602Client : IDisposable
{
private readonly HttpClient _httpClient;
private readonly TechSec602Config _config;
public TechSec602Client(TechSec602Config config)
{
_config = config;
_httpClient = new HttpClient()
{
Timeout = TimeSpan.FromMilliseconds(_config.TimeoutMs)
};
if (!string.IsNullOrEmpty(_config.ApiKey))
{
_httpClient.DefaultRequestHeaders.Add("X-API-Key", _config.ApiKey);
}
}
public TechSec602Client() : this(TechSec602Config.FromAppSettings())
{
}
///
/// Primary verification method using v2 API format (URL-based)
///
/// The URL to verify
/// The client IP address (direct connection)
/// Optional X-Forwarded-For IP for proxy scenarios. When provided, BOTH IPs are checked.
/// Optional API key override
public async Task VerifyRequestAsync(string url, string ipAddress = null, string forwardedForIp = null, string apiKey = null)
{
try
{
var requestApiKey = apiKey ?? _config.ApiKey;
var clientIp = ipAddress ?? GetClientIpAddress();
var formData = new List>
{
new KeyValuePair("url", url)
};
if (!string.IsNullOrEmpty(requestApiKey))
{
formData.Add(new KeyValuePair("apiKey", requestApiKey));
}
if (!string.IsNullOrEmpty(clientIp))
{
formData.Add(new KeyValuePair("ipAddress", clientIp));
}
// Add forwarded IP for proxy/load balancer scenarios
// When provided, the API will check BOTH the direct IP and forwarded IP
if (!string.IsNullOrEmpty(forwardedForIp))
{
formData.Add(new KeyValuePair("forwardedForIp", forwardedForIp));
}
var content = new FormUrlEncodedContent(formData);
var response = await _httpClient.PostAsync($"{_config.BaseUrl}/api/verify", content);
if (response.IsSuccessStatusCode)
{
var jsonResponse = await response.Content.ReadAsStringAsync();
var apiResult = JsonConvert.DeserializeObject(jsonResponse);
return new VerificationResult
{
IsAllowed = apiResult.IsAllowed,
Reason = apiResult.Reason,
ProcessingTimeMs = apiResult.ProcessingTimeMs,
Success = true
};
}
else
{
return new VerificationResult
{
IsAllowed = _config.FailOpen,
Reason = $"HTTP {response.StatusCode}: {response.ReasonPhrase}",
Success = false
};
}
}
catch (TaskCanceledException)
{
// Timeout occurred
return new VerificationResult
{
IsAllowed = _config.FailOpen,
Reason = "Request timeout",
Success = false
};
}
catch (Exception ex)
{
// Network or other error
return new VerificationResult
{
IsAllowed = _config.FailOpen,
Reason = ex.Message,
Success = false
};
}
}
///
/// Synchronous verification method (not recommended for production)
///
/// The URL to verify
/// The client IP address (direct connection)
/// Optional X-Forwarded-For IP for proxy scenarios. When provided, BOTH IPs are checked.
/// Optional API key override
public VerificationResult VerifyRequest(string url, string ipAddress = null, string forwardedForIp = null, string apiKey = null)
{
return VerifyRequestAsync(url, ipAddress, forwardedForIp, apiKey).Result;
}
///
/// Quick verification for current HTTP context.
/// Automatically extracts both the direct IP and X-Forwarded-For IP if present.
///
public async Task VerifyCurrentRequestAsync()
{
if (HttpContext.Current == null)
{
return new VerificationResult { IsAllowed = _config.FailOpen, Reason = "No HTTP context", Success = false };
}
var request = HttpContext.Current.Request;
var url = request.Url.ToString();
var ipAddress = request.UserHostAddress;
// Extract X-Forwarded-For IP separately for dual-IP verification
var forwardedForIp = GetForwardedForIp();
return await VerifyRequestAsync(url, ipAddress, forwardedForIp);
}
///
/// Gets the X-Forwarded-For IP address from the request headers.
/// Returns the first IP in the chain (client IP).
///
private string GetForwardedForIp()
{
if (HttpContext.Current == null) return null;
var forwarded = HttpContext.Current.Request.Headers["X-Forwarded-For"];
if (!string.IsNullOrEmpty(forwarded))
{
return forwarded.Split(',')[0].Trim();
}
// Also check X-Real-IP as an alternative
var realIp = HttpContext.Current.Request.Headers["X-Real-IP"];
if (!string.IsNullOrEmpty(realIp))
{
return realIp;
}
return null;
}
///
/// Health check for the verification service
///
public async Task HealthCheckAsync()
{
try
{
var response = await _httpClient.GetAsync($"{_config.BaseUrl}/api/verify/health");
return response.IsSuccessStatusCode;
}
catch
{
return false;
}
}
///
/// Get API information and capabilities
///
public async Task