Hi,
I have a Client/Server Blazor project that, among other things, authenticates users using Microsoft.AspNetCore.Identity. The relevant controller uses the SignInManager class from identity.
I am experimenting with migrating this project to Blazor Server Side so that I can improve performance. Calls to a SignInManager object fail however. What I have (unsuccessfully) attempted to do is to make the pre-existing controller a service that is consumed by the App project. Referencing it is fine however I get an exception on calling SignInManager.PasswordSignInAsync. Is this because there is no HttpClient access from the App project, and if so is there a way that I can make this work?
Thank you.
Blazor Server Side has no IOC-registration of System.Net.Http.HttpClient.
ATM, I guess this is by design (look at the example in the default template - the weather forecast also does not fetch data from the server). But it should be possible to use HttpClient also from the server (with all limitations, e.g. being not able to identify a client by it's IP (because it's always from the server))
Thanks. Looks like I won't be able to migrate this project to server side then. Pity, and I hope that this changes in due course!
@blowdart What are your thoughts on how ASP.NET Core Identity and server-side Blazor should be used together?
I don't know what Blazor server-side is, so ... I'm going to guess here from a quick read.
Sign-in needs to be run on the app on the server, for access to databases, signing keys and so on, all things ASP.NET provides. The SignalR communication doesn't flow things like cookies, which asp.net identity expects, so I wouldn't expect this to work at all. An alternative approach would be have an ODIC server, like Identity Server on the backend, talking to ASP.NET identity, then do whatever blazor does for OIDC logins and head down that route, protecting any APIs with JWT tokens issued by the IdentityServer instance.
@simonziegler I don't believe sign in requires an HttpClient at all. But it probably does need to happen in the context of a request. With server-side Blazor your dealing with a SignalR connection, not individual HTTP requests. I think you should be able to use normal Razor Pages/MVC to handle sign in and then once the authentication cookie is established you can use that to authorize the user. Or, as @blowdart suggested you could setup an OIDC server and use JWT tokens to establish the user identity.
I think this answers your question, but let us know if anything is unclear!
@danroth27 is it possible to register a HttpClient when running Blazor on Server?
I'd like to switch between WebAssembly and Server for better debugging on Server and need HttpClient for Backend-Connections.
@danroth27 and do, Thank you for the viewpoints. This is beyond my skill set and not something that I strictly need for my purposes - a demonstration web app pre funding. If I get funded, some real life engineers will deal with all of this from scratch.
I鈥檒l just say again that I鈥檓 in awe of what you folks are doing here. I very much hope that MS decides to make Blazor a supported product, as it is just ideal for the FinTech thing I鈥檓 working on. Keep up the great work!
@danroth27 is it possible to register a HttpClient when running Blazor on Server?
I'd like to switch between WebAssembly and Server for better debugging on Server and need HttpClient for Backend-Connections.
@DNF-SaS Yes, you should be able to add whatever you want to the service collection when running Blazor on the server. For server-side Blazor both the Blazor app and the ASP.NET Core app share the same service provider so you can add the HttpClient in the Server project. When running client-side the Blazor app will use the browser friendly HttpClient.
I want to use Microsoft.AspNetCore.Identity.SignInManager for authentication but i can't use it. please give me some demo using that. thanks
Using an HttpClient is not useful Server-side. Because the client is made on the server and it is different from clients browser, say Chrome. HttpClient on server is something like a virtual-Chrome on the server machine, not client machine. And after you login and set auth cookies, they are set to that virtual browser.
@tanngocIT I tried tons of idea to work around this. I ended up with two solutions:
SOLUTION 1:
Putting a form in my Razor Component and also handling post request in a Controller. And it works like a charm. My code like this:
SignInController.cs
public class SignInController : ControllerBase
{
private readonly SignInManager<User> SignInManager;
public SignInController(SignInManager<User> signInManager)
{
this.SignInManager = signInManager;
}
[HttpPost("/signin")]
public async Task<IActionResult> Index([FromForm]string username, [FromForm] string password)
{
var result = await SignInManager.PasswordSignInAsync(username, password, true, lockoutOnFailure: true);
return Redirect("/");
}
}
Login.razor
<form method="post" action="/signin">
<input type="text" id="username" name="username" />
<input type="password" id="password" name="password" />
<input type="submit" value="Login" />
</form>
Startup.cs:
app.UseRouting(routes =>
{
routes.MapRazorPages();
routes.MapComponentHub<App>("app");
routes.MapControllers(); // Add this.
});
SOLUTION 2:
The idea is to create auth cookie on the server (manually, not using SignInManager) and send it to the client in base64 format with SignalR. The client then calls a javascript through Js-Interop and sets asp.net's Cookie.
This solution also worked for me and i assume HTTPS and WSS are secure enough. But i dont know if browser extensions can capture interop calls and steal the cookie and what are the risks.
CONCLUSION:
In solution 2, when the cookie is set the client needs to do a full refresh of the page and cause prerendering again. While in solution 1 this job is done automatically.
@xclud thanks for your sharing.
In solution 1: you add code in Server project or App project ??
i try to do that but it not working :((
@tanngocIT Update to latest nightly SDK and create a new Razor Component project.
There is no longer Server and App projects. There will be only one project and the structure looks like other asp.net projects. You will find it straight forward.
Edit: https://github.com/dotnet/core-sdk#installers-and-binaries
Trying to get Blazor server-side login to work like rest of the blazor app, to have consistent SPA experience. Currently, the supplied identity sample uses razor page for identity.
I've successfully ported most of the pages. However, I am running into issue on login. The error is
System.InvalidOperationException: The response headers cannot be modified because the response has already started.
It originated from this call:
var result = await _signInManager.PasswordSignInAsync(Input.Email, Input.Password, Input.RememberMe, lockoutOnFailure: true);
My implementation is have server side page to handle the UI and created a service that does the back-end identity. This design pattern works most of the time, except in the call above, it seems that it is trying to set cookie for the response. On blazor, to set cookie, we need to use jsinterop. So there's no directly way for PasswordSignInAsync to set cookie.
Update: I experimented by calling PasswordSignInAsync directly from server side page. It also failed. So technically there's a limitation here that Microsoft needs to address to enable server side login (for cookie authentication from the default identity sample).
Update: I'll use SignInManager.SigninAsync() instead.
Update: SignInManager.SigninAsync() also doesn't work as it also tries to set cookie directly... Looks like the issue is still open
From Microsoft documentation: SignInAsync creates an encrypted cookie and adds it to the current response. If AuthenticationScheme isn't specified, the default scheme is used. I think what we need here is a method to handle signinasync, create the cookie without adding it directly. and the developer can use jsinterop to update the cookie.
I think what we need here is a method to handle signinasync, create the cookie without adding it directly. and the developer can use jsinterop to update the cookie.
@danroth27 any thoughts about this suggestion?
I like what William suggested. By the way, I posted this on a closed issue. I wonder if people will follow up on this. Or should we open a new one?
Also, SignOutAsync have similar issue as SigninAsync.
For anyone running into similar issue, temporary I use razor page for Login, logoff/logOut, Relogin.
I ran into the same issue. But it makes sense why you should use Razor pages for login and logout.
When you set a cookie with Javascript (jsinterop) the cookie will always be visible via any Javascript. This creates a XSS safety issue. That's why you want to use HttpOnly cookies only.
Thomas is correct! I forgot to comment on the httpOnly part.