paultechguy
paultechguy

Reputation: 2538

.NET Blazor 9: Capture client ip address

Is anyone able to capture for component use, a client IP address, in Blazor 9 hosted in Azure?

This question has been asked for several years, but for previous Blazor versions; IMHO, there isn't a satisfactory answer that applies to .NET 9 & Blazor. It can't be this difficult.

When running a Blazor interactive server app in Azure, with rendermode of InteractiveServer, it is very difficult to capture the client's IP address.

The first time OnInitialize is hit, there is a valid HttpContext. The second time (render), the HttpContext is null. A HttpContext value captured such as remoteipaddress, even in a scoped service (which is really a singleton in interactive server mode), is gone. HttpContext is missing. Scoped services are reinitialized.

I disabled prerendering, but then the only call to OnInitialized, the HttpContext is null. I get that HttpContext is shady in interactive mode.

Things run stellar in localhost, but once deployed to Azure, the second OnInitialize everything is reinitalized.

Upvotes: 0

Views: 182

Answers (2)

Kristian Bleken
Kristian Bleken

Reputation: 21

I get the remote IP address like this. This works for me on Azure, however we're just on .NET8 for now. I can't see any reason this will not work in .NET9?

In App.razor, I get the IP address, and forward it to the "Routes.razor" component.

@{
    string? remoteIpAddress = null;

    // Get IP
    httpContext.Request.Headers.TryGetValue("X-Forwarded-For", out var forwardedFor);
    if (forwardedFor.Count > 0)
    {
        remoteIpAddress = forwardedFor[0];
    }
    if (remoteIpAddress is null)
    {
        remoteIpAddress = httpContext.Connection.RemoteIpAddress?.ToString();
    }
}

<Routes RemoteIpAddress="@remoteIpAddress" @rendermode="@(new InteractiveServerRenderMode(false))" />

And in Routes.razor, I store it in the authentication provider.

[Parameter] public string? RemoteIpAddress { get; set; }

protected override void OnInitialized()
{
    try
    {
        var ip = System.Net.IPEndPoint.Parse(RemoteIpAddress ?? "").Address;
        AuthProvider.IpAddress = ip;
    }
    catch (Exception)
    {
        // TODO log
    }
}

Hope this helps and works for you!

Upvotes: 0

Yousha Aleayoub
Yousha Aleayoub

Reputation: 5703

Notes for Azure:

1- In Blazor Server mode, in enabled prerendering HttpContext will be unavailable during the initial lifecycle.

2- in Azure, since client IP is behind a load balancer or app gateway, X-Forwarded-For header field is best way to get the actual client IP.

Here's a solution(not sure if it 100% work in all situations)

Create a middleware to capture the client IP address

public class ClientIpAddressMiddleware
{
    private RequestDelegate _next;

    public ClientIpAddressMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        var _clientIp = context.Connection.RemoteIpAddress?.ToString();

        if (context.Request.Headers.TryGetValue("X-Forwarded-For", out var forwardedFor))
        {
            _clientIp = forwardedFor.FirstOrDefault()?.Split(',')[0].Trim();
        }


        context.Items["ClientIp"] = _clientIp ;
        await _next(context);
    }
}

Then register the middleware before other middleware:

app.UseMiddleware<ClientIpMiddleware>();

More details: https://learn.microsoft.com/en-us/azure/application-gateway/how-application-gateway-works https://learn.microsoft.com/en-us/aspnet/core/host-and-deploy/proxy-load-balancer?view=aspnetcore-7.0

Upvotes: 0

Related Questions