Imports System.Configuration Imports System.Net.Http Imports System.Threading.Tasks Imports System.Web Imports System.Web.Configuration Imports Newtonsoft.Json ''' ''' VB.NET Client Library for 602TechSec API v2 Integration ''' Provides easy integration for verification calls in web applications ''' Public Class TechSec602Client Private ReadOnly _httpClient As HttpClient Private ReadOnly _config As TechSec602Config Public Sub New(config As TechSec602Config) _config = config _httpClient = New HttpClient() With { .Timeout = TimeSpan.FromMilliseconds(_config.TimeoutMs) } If Not String.IsNullOrEmpty(_config.ApiKey) Then _httpClient.DefaultRequestHeaders.Add("X-API-Key", _config.ApiKey) End If End Sub Public Sub New() Me.New(TechSec602Config.FromAppSettings()) End Sub ''' ''' 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 Function VerifyRequestAsync(url As String, Optional ipAddress As String = Nothing, Optional forwardedForIp As String = Nothing, Optional apiKey As String = Nothing) As Task(Of VerificationResult) Try Dim requestApiKey = If(apiKey, _config.ApiKey) Dim clientIp = If(ipAddress, GetClientIpAddress()) Dim formData = New List(Of KeyValuePair(Of String, String)) From { New KeyValuePair(Of String, String)("url", url) } If Not String.IsNullOrEmpty(requestApiKey) Then formData.Add(New KeyValuePair(Of String, String)("apiKey", requestApiKey)) End If If Not String.IsNullOrEmpty(clientIp) Then formData.Add(New KeyValuePair(Of String, String)("ipAddress", clientIp)) End If ' Add forwarded IP for proxy/load balancer scenarios ' When provided, the API will check BOTH the direct IP and forwarded IP If Not String.IsNullOrEmpty(forwardedForIp) Then formData.Add(New KeyValuePair(Of String, String)("forwardedForIp", forwardedForIp)) End If Dim content = New FormUrlEncodedContent(formData) Dim response = Await _httpClient.PostAsync($"{_config.BaseUrl}/api/verify", content) If response.IsSuccessStatusCode Then Dim jsonResponse = Await response.Content.ReadAsStringAsync() Dim apiResult = JsonConvert.DeserializeObject(Of ApiVerificationResponse)(jsonResponse) Return New VerificationResult With { .IsAllowed = apiResult.IsAllowed, .Reason = apiResult.Reason, .ProcessingTimeMs = apiResult.ProcessingTimeMs, .Success = True } Else Return New VerificationResult With { .IsAllowed = _config.FailOpen, .Reason = $"HTTP {response.StatusCode}: {response.ReasonPhrase}", .Success = False } End If Catch ex As TaskCanceledException ' Timeout occurred Return New VerificationResult With { .IsAllowed = _config.FailOpen, .Reason = "Request timeout", .Success = False } Catch ex As Exception ' Network or other error Return New VerificationResult With { .IsAllowed = _config.FailOpen, .Reason = ex.Message, .Success = False } End Try End Function ''' ''' 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 Function VerifyRequest(url As String, Optional ipAddress As String = Nothing, Optional forwardedForIp As String = Nothing, Optional apiKey As String = Nothing) As VerificationResult Return VerifyRequestAsync(url, ipAddress, forwardedForIp, apiKey).Result End Function ''' ''' Quick verification for current HTTP context. ''' Automatically extracts both the direct IP and X-Forwarded-For IP if present. ''' Public Async Function VerifyCurrentRequestAsync() As Task(Of VerificationResult) If HttpContext.Current Is Nothing Then Return New VerificationResult With {.IsAllowed = _config.FailOpen, .Reason = "No HTTP context", .Success = False} End If Dim request = HttpContext.Current.Request Dim url = request.Url.ToString() Dim ipAddress = request.UserHostAddress ' Extract X-Forwarded-For IP separately for dual-IP verification Dim forwardedForIp = GetForwardedForIp() Return Await VerifyRequestAsync(url, ipAddress, forwardedForIp).ConfigureAwait(False) End Function ''' ''' Gets the X-Forwarded-For IP address from the request headers. ''' Returns the first IP in the chain (client IP). ''' Private Function GetForwardedForIp() As String If HttpContext.Current Is Nothing Then Return Nothing Dim forwarded = HttpContext.Current.Request.Headers("X-Forwarded-For") If Not String.IsNullOrEmpty(forwarded) Then Return forwarded.Split(","c)(0).Trim() End If ' Also check X-Real-IP as an alternative Dim realIp = HttpContext.Current.Request.Headers("X-Real-IP") If Not String.IsNullOrEmpty(realIp) Then Return realIp End If Return Nothing End Function ''' ''' Health check for the verification service ''' Public Async Function HealthCheckAsync() As Task(Of Boolean) Try Dim response = Await _httpClient.GetAsync($"{_config.BaseUrl}/api/verify/health") Return response.IsSuccessStatusCode Catch Return False End Try End Function ''' ''' Get API information and capabilities ''' Public Async Function GetApiInfoAsync() As Task(Of Object) Try Dim response = Await _httpClient.GetAsync($"{_config.BaseUrl}/api/verify/info") If response.IsSuccessStatusCode Then Dim json = Await response.Content.ReadAsStringAsync() Return JsonConvert.DeserializeObject(json) End If Catch ' Ignore errors End Try Return Nothing End Function ''' ''' Gets the client IP address - returns direct connection IP (UserHostAddress). ''' For proxy scenarios, use GetForwardedForIp() separately. ''' Private Function GetClientIpAddress() As String If HttpContext.Current Is Nothing Then Return Nothing Return HttpContext.Current.Request.UserHostAddress End Function Public Sub Dispose() _httpClient?.Dispose() End Sub End Class ''' ''' Configuration class for TechSec602Client ''' Public Class TechSec602Config Public Property BaseUrl As String = "https://sec.602.tech" Public Property ApiKey As String = "" Public Property TimeoutMs As Integer = 5000 Public Property FailOpen As Boolean = True ' Allow requests when service is unavailable Public Property EnableLogging As Boolean = False Public Property CacheResults As Boolean = True Public Property CacheTimeoutMinutes As Integer = 5 Public Shared Function FromAppSettings() As TechSec602Config Return New TechSec602Config With { .BaseUrl = ConfigurationManager.AppSettings("TechSec602.BaseUrl") ?: "https://sec.602.tech", .ApiKey = ConfigurationManager.AppSettings("TechSec602.ApiKey") ?: "", .TimeoutMs = Integer.Parse(ConfigurationManager.AppSettings("TechSec602.TimeoutMs") ?: "5000"), .FailOpen = Boolean.Parse(ConfigurationManager.AppSettings("TechSec602.FailOpen") ?: "true"), .EnableLogging = Boolean.Parse(ConfigurationManager.AppSettings("TechSec602.EnableLogging") ?: "false"), .CacheResults = Boolean.Parse(ConfigurationManager.AppSettings("TechSec602.CacheResults") ?: "true"), .CacheTimeoutMinutes = Integer.Parse(ConfigurationManager.AppSettings("TechSec602.CacheTimeoutMinutes") ?: "5") } End Function End Class ''' ''' Result of verification request ''' Public Class VerificationResult Public Property IsAllowed As Boolean Public Property Reason As String Public Property ProcessingTimeMs As Double? Public Property Success As Boolean End Class ''' ''' API response model ''' Friend Class ApiVerificationResponse Public Property IsAllowed As Boolean Public Property Reason As String Public Property ProcessingTimeMs As Double End Class