I've only been using Radzen for a couple of days, so new to me.
I made a demo app that uses SqlServer and Security. (btw the wizard for visual studio has a bug and specifies MySql instead of SqlServer in the Identity code, but Blazor Studio does it properly).
I removed the https from launchsettings.json.
I removed the line app.UseHttpsRedirection() from program.cs
This exact setup works as expected for other similar demo apps using microsoft fluent ui blazor and also blazorise, so if you could point me in an particular direction for troubleshooting, it would be greatly appreciated!
Doesn't help. Is it is a useful clue that after I type the credentials in and then go to http://localhost:5000 it is in fact authenticated and logged in? But the https page remains on the login screen with no feedback.
Thank you, I had used that before, but it has been updated in the past year. The only thing I got from this was to add the UseForwardedHeaders to the middleware first thing after builder.Build():
Still no cigar though. Clearly I must be missing something trivial, surely running a radzen blazor http server behind an NGINX reverse proxy must be super common? What stupid thing am I doing?
I have an identical fluent ui demo that uses the same identity sql server database, also running in http, and it works perfectly as expected, and it didn't have the forwarded headers line either. Thanks for any comments.
I think I know why. It is something I had issues with too. The request comes from "something.com" but in the AccountController.razor the _navigationManager.BaseUri will return "something.com" as the uri. The issue might be that you need to add to your local machine DNS file that the "something.com" resolves to the localhost:5000.
Looking at the post it seems it should work, but check out the breakpoints in the AccountController. For me it didn't through any error in the console and I could find it out by going through the AccountController.razor and the UserService calls.
This problem usually occurs when nginx does not redirect HTTP POST requests to HTTPS (the sercurity service makes a POST request to get the current user). Check the linked thread for a modification in the source which uses GET instead of POST. I any case you can add logging in the SecurityService to troubleshoot the deployment issue. There are probably messages logged in the output too (also you can check NGINX's logs for failed HTTP requests).
And to clarify a few points:
The UI framework is not relevant to the issue as it is entirely server-side.
There is no such thing as radzen blazor http server. The server is the ASP.NET Core built-in one.
Thank you, I will do more logging and look more closely into this.
Well of course, but semantics aside my point is that the fluent ui wizard app with identity and authentication works with this nginx setup, the blazorise wizard demo app with added identity also works with this same nginx setup, but unfortunately the Radzen wizard app does not.... So something IS different.
Finally you can check what the actual URL being requested here is:
public async Task<ApplicationAuthenticationState> GetAuthenticationStateAsync()
{
var uri = new Uri($"{navigationManager.BaseUri}Account/CurrentUser");
var response = await httpClient.SendAsync(new HttpRequestMessage(HttpMethod.Post, uri));
return await response.ReadAsync<ApplicationAuthenticationState>();
}
If it is http instead of https and nginx redirects aren't properly configured it would fail.
I did more research and successfully deployed a brand new app created with Radzen Blazor Studio behind nginx.
As I suspected the problem is HTTPS propagation. By default NGINX uses 301 redirects which loses the request method (POST gets converted to a GET). This in turn confuses the SecurityService that Radzen apps use.
The default Microsoft instructions say that this is needed in order to forward http headers when using nginx:
It says that the forwarding middleware supports only proxies that work on 127.0.0.1 by default. The suggested solution from that thread worked as expected.
In short to support nginx deployment with security add this code right below var app = builder.Build(); in Program.cs
var app = builder.Build();
// Start -->
var forwardingOptions = new ForwardedHeadersOptions()
{
ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
};
forwardingOptions.KnownNetworks.Clear();
forwardingOptions.KnownProxies.Clear();
app.UseForwardedHeaders(forwardingOptions);
// <-- End
We can also avoid the forwarding middleware by modifying the code of the app:
Open App.razor and replace <base href="@NavigationManager.BaseUri" /> with <base href="/" />
Open AccountController.cs and delete [HttpPost] before CurrentUser
This option also works but I like the forwarding middleware more.
Thank you for all your assistance. Unfortunately I still have issues:
I put in the forwarding middleware exactly as you specified and also added your 301 redirect to the nginx conf exactly as you have it, in addition to the proxy pass section which is as I posted before, taken from the Microsoft instructions. Putting some breakpoints in GetAuthenticationStateAsync, I get what I think it expected behavior. The uri appears correct.
running locally, https:\localhost (so going through the nginx proxy pass), I get:
uri = https:\localhost/Account/CurrentUser and then a SSL Exception in HttpRequestMessage(HttpMethod.Post, uri)). The inner exception says incorrect certificate, makes sense, the certificate is for "example.c0m".
running remotely, example.c0m:
I get
uri = https:\example.c0m/Account/CurrentUser
And then it hangs in the httpClient.SendAsync call, eventually throwing a timeout exception.
I also tried your previous suggestion of replacing the HttpMethod.Post with Get, and removing the [HttpPost] before CurrentUser. Also tried replacing "@NavigationManager.BaseUri" with "/". Nothing works.
I hear what you're saying, that you have production code working for years with security running behind nginx, so I am at a loss. Running Ubuntu 24.04, nginx/1.18.0, nginx conf exactly as in the microsoft docs with the 301 redirect for http port 80 that you posted above.
Edit: It wouldn't let me post links so I put those goofy back slashes in. Sorry
Let's stick with the forwarding middleware. What is the value rendered for <base href="@NavigationManager.BaseUri" /> that you see when browsing with https://example.com? Does it have https or http? If it is still http then the middleware isn't working properly for some reason.
Did you try adding some form of logging? Adding Console.WriteLine in SecurityService and AccountController would show what code is executed.
In any case here is my test application running on our Ubuntu server: https://next.radzen.com
What is the value rendered for <base href="@NavigationManager.BaseUri" /> that you see when browsing with https://example.com? Does it have https or http?
I downloaded your test application and ran it on my ubuntu server.
Running your EmptyApp code, I put a breakpoint on line 98 in SecurityService.cs, and the value for BaseUri is "https://mydomain.com/". And then it gets hung up in the next line with the httpClient.SendAsync.
The last thing I can suggest is to check what happens in the CurrentUser method. It seems to be properly requested and should return a successful response. You can add logging to see what happens. For example this:
[HttpPost]
public ApplicationAuthenticationState CurrentUser()
{
try
{
Console.WriteLine("Entering CurrentUser");
var result = new ApplicationAuthenticationState
{
IsAuthenticated = User.Identity.IsAuthenticated,
Name = User.Identity.Name,
Claims = User.Claims.Select(c => new ApplicationClaim { Type = c.Type, Value = c.Value })
};
Console.WriteLine(JsonSerializer.Serialize(result, new JsonSerializerOptions { WriteIndented = true }));
return result;
}
catch (Exception ex)
{
Console.WriteLine($"Exception: {ex}");
return null;
}
}