Aspnetcore: Authentication in server side blazor

Created on 19 Feb 2020  路  16Comments  路  Source: dotnet/aspnetcore

Hello, I actually implement authentication in my blazor app.
I try my app work as well in client and server side.
In client side i don't have any problem by using SignInAsync, but in server side i have an exception "The response headers cannot be modified because the response has already started.'"

A i understand it's because the connection is mode over signal-r.

When i check an empty server-side project with authentication activated, was i understood is i have to manage my authentication with classic mvc page.

I would prefer stay on my spa app, there is any workaround to make my authentication work like the webassembly mode?

thanks !

area-blazor question

Most helpful comment

Hi JulienGrd, No, I haven't been completely successful. I'll summarize my findings:

  1. Blazor does work with MS Identity scaffold during project creation.
    1.1 https://docs.microsoft.com/en-us/aspnet/core/security/blazor/?view=aspnetcore-3.1&tabs=visual-studio
    1.2 But you don't get access to the pages.
  2. You can scaffold after creation and override the pages you want (e.g. Login, Logout, Register, etc.)
    2.1 But these are not Blazor/razor pages, but .cshtml/.cs
    2.2 They don't have the same look and feel as the rest of a Blazor app.
    2.3 You can rewrite all of these into Blazor pages, except Login
    2.4 Once logged in, all of the Authorization mechanisms work fine in Blazor. The rub, is getting a user logged in.
  3. SignInManager inside Login.razor does NOT work with Blazor:
    3.1 This is because Blazor is using SignalR underneath and isn't really sending HTTP messages. If someone else knows better and a way around this problem, please join the conversation.
    3.2 The error returned is "The response headers cannot be modified because the response has already started."
    3.3 I have found no work-around for SignInManager, except using the Asp.Net core pages, which have the different look and feel.
    3.4 This is disastrous, because Blazor is just one call away from fully utilizing Asp.Net Core Identity. But because of the incompatibility with SignalR, we are required to do 4 times as much work to get A&A working.
  4. Others have successfully gone an API route for the signin.
    4.1 https://github.com/stavroskasidis/BlazorWithIdentity
    4.2 https://github.com/enkodellc/blazorboilerplate
    4.3 These work, work well. It just seems strange for a Server to make an API call to itself, to log in a user, and then not need the API for any further A&A work.
  5. A different route is doing A&A in SignalR that Blazor is riding atop.
    5.1 https://docs.microsoft.com/en-us/aspnet/core/signalr/authn-and-authz?view=aspnetcore-3.1
  6. In SignalR, there is Cookie authentication (simple and limited) and Bearer token Authentication (complex but comprehensive).
  7. There is a very comprehensive sample at https://github.com/dotnet/AspNetCore.Docs/tree/master/aspnetcore/signalr/authn-and-authz/sample/.
  8. The sample is not a tutorial, and has several fatal flaws.
    8.1 It elects to do the bearer token instead of the simpler Cookie authentication. I have not found a cookie sample. Bearer token is a better A&A solution, but has a steeper learner curve. It is at least 4 times as much work to implement, minimum, to get it up and running.
    8.2 In Startup.cs it was decided not include an Identity Authority:
    // Configure the Authority to the expected value for your authentication provider
    // This ensures the token is appropriately validated
    options.Authority = /* TODO: Insert Authority URL here */;
    The sample won't compile. See BlazorBoilerPlate for an example of using IdentityServer4 as a Token Authority.
    8.3 After all of this work just to get around the SignInManager Chasm, there is this line in Index.cshtml:

    There is no example on doing the Login. LMAO! You are back to square on figuring out how to do the login because no workaround for SignInManager is available. Or at least I haven't found one.

Conclusiion:.
--If you have the time and energy, the JWT Identity approach as described for SignalR looks to be best practice. But you will have to figure out the Login steps.
--If you are looking for something for testing, educational purpose, just scaffold Identity and use the Asp pages.
--The API approach looks good, is using a bearer token and is closely related to the SignalR approach, but I don't know if it is vulnerable or not.

(Soap box on) Authentication and Authorization are fundamental to any system, framework, etc. To state there is a ready A&A system for Blazor, and then pass responsibility to SignalR, while teasing this community that Blazor is Production Ready, is very misleading. The fact that SignInManager will throw and exception, and this fact is OMITTED from the documentation, leaves one to wonder why? There are no good answers to this question. And I see nothing on the horizon to change the situation. I want to write in Blazor, not Asp.Net Core. I don't want to build a rube goldberg software contraption that maybe leaves my system vulnerable. I want Good, Better, Best A&A systems that are "best practices" and strong guidance on implementation. Security/A&A is too important for "well, it works." or "it is good enough." It has to be right. (Soap box off).

For my purposes, I'm rewriting the Asp pages into Blazor, except Login. Your mileage may vary.

All 16 comments

Where is the MS guidance on Blazor Server Authentication without MVC? Is there a 100% Blazor solution?

@julienGrd thanks for contacting us.

We don't offer a Blazor based UI for Identity, but there should be nothing preventing you from implementing it yourself. With regards to authentication Blazor Server applications are considered traditional web applications, the identity is established when the app starts and normally preserved for the entirety of the connection in usual cases.

That said, nothing prevents you from implementing your own authentication with your own authenticationprovider, although we recommend staying on the options we provide.

Thanks for the quick response. Your explanation had me stumped for a bit. It seemed we were talking about two different things. I went back and reviewed https://docs.microsoft.com/en-us/aspnet/core/security/blazor/?view=aspnetcore-3.1&tabs=visual-studio. Create a Blazor app, scaffold MS Identity into it, use the Asp.Net Core pages to Login, Logout, etc. All works fine, except the pages aren't Blazor, and don't have the right look and feel. Try to create Login.razor, and at _SignInManager.SignInAsync() the error that occurs is "The response headers cannot be modified because the response has already started." Well, yes, because of the Middleware Pipeline and Blazor being a real-time connection, of course signin is not going to work. Some have used APIs to workaround this issue, but passing credentials from the browser to the server just to make an API call back to server itself seems a little circuitous.

I re-read the aforementioned page, and there was the answer: _Blazor Server apps operate over a real-time connection that's created using SignalR. Authentication in SignalR-based apps is handled when the connection is established. Authentication can be based on a cookie or some other bearer token._
I've been looking for Blazor Authentication, and should have been looking at SignalR Authentication. I can't count how many times I looked at that page, and skipped over those 2 sentences, because I wasn't interested in SignlalR (at that moment :-) )

For anyone else passing this way, to find Blazor Authentication, look down the SignalR Authentication rabbit hole. I'll head off that way now myself. Thank you for getting me pointed in the right direction.

@bdnts as i understand we have to connect identity to the signal-r circuit ? did you achieve this or its just in theory ?
I check this page https://docs.microsoft.com/en-us/aspnet/core/signalr/authn-and-authz?view=aspnetcore-3.1 but i don't really understand how to connect the authorization to the existing signal-r connection

It can be niece to have a github project which validate and demonstrate this.

Hi JulienGrd, No, I haven't been completely successful. I'll summarize my findings:

  1. Blazor does work with MS Identity scaffold during project creation.
    1.1 https://docs.microsoft.com/en-us/aspnet/core/security/blazor/?view=aspnetcore-3.1&tabs=visual-studio
    1.2 But you don't get access to the pages.
  2. You can scaffold after creation and override the pages you want (e.g. Login, Logout, Register, etc.)
    2.1 But these are not Blazor/razor pages, but .cshtml/.cs
    2.2 They don't have the same look and feel as the rest of a Blazor app.
    2.3 You can rewrite all of these into Blazor pages, except Login
    2.4 Once logged in, all of the Authorization mechanisms work fine in Blazor. The rub, is getting a user logged in.
  3. SignInManager inside Login.razor does NOT work with Blazor:
    3.1 This is because Blazor is using SignalR underneath and isn't really sending HTTP messages. If someone else knows better and a way around this problem, please join the conversation.
    3.2 The error returned is "The response headers cannot be modified because the response has already started."
    3.3 I have found no work-around for SignInManager, except using the Asp.Net core pages, which have the different look and feel.
    3.4 This is disastrous, because Blazor is just one call away from fully utilizing Asp.Net Core Identity. But because of the incompatibility with SignalR, we are required to do 4 times as much work to get A&A working.
  4. Others have successfully gone an API route for the signin.
    4.1 https://github.com/stavroskasidis/BlazorWithIdentity
    4.2 https://github.com/enkodellc/blazorboilerplate
    4.3 These work, work well. It just seems strange for a Server to make an API call to itself, to log in a user, and then not need the API for any further A&A work.
  5. A different route is doing A&A in SignalR that Blazor is riding atop.
    5.1 https://docs.microsoft.com/en-us/aspnet/core/signalr/authn-and-authz?view=aspnetcore-3.1
  6. In SignalR, there is Cookie authentication (simple and limited) and Bearer token Authentication (complex but comprehensive).
  7. There is a very comprehensive sample at https://github.com/dotnet/AspNetCore.Docs/tree/master/aspnetcore/signalr/authn-and-authz/sample/.
  8. The sample is not a tutorial, and has several fatal flaws.
    8.1 It elects to do the bearer token instead of the simpler Cookie authentication. I have not found a cookie sample. Bearer token is a better A&A solution, but has a steeper learner curve. It is at least 4 times as much work to implement, minimum, to get it up and running.
    8.2 In Startup.cs it was decided not include an Identity Authority:
    // Configure the Authority to the expected value for your authentication provider
    // This ensures the token is appropriately validated
    options.Authority = /* TODO: Insert Authority URL here */;
    The sample won't compile. See BlazorBoilerPlate for an example of using IdentityServer4 as a Token Authority.
    8.3 After all of this work just to get around the SignInManager Chasm, there is this line in Index.cshtml:

    There is no example on doing the Login. LMAO! You are back to square on figuring out how to do the login because no workaround for SignInManager is available. Or at least I haven't found one.

Conclusiion:.
--If you have the time and energy, the JWT Identity approach as described for SignalR looks to be best practice. But you will have to figure out the Login steps.
--If you are looking for something for testing, educational purpose, just scaffold Identity and use the Asp pages.
--The API approach looks good, is using a bearer token and is closely related to the SignalR approach, but I don't know if it is vulnerable or not.

(Soap box on) Authentication and Authorization are fundamental to any system, framework, etc. To state there is a ready A&A system for Blazor, and then pass responsibility to SignalR, while teasing this community that Blazor is Production Ready, is very misleading. The fact that SignInManager will throw and exception, and this fact is OMITTED from the documentation, leaves one to wonder why? There are no good answers to this question. And I see nothing on the horizon to change the situation. I want to write in Blazor, not Asp.Net Core. I don't want to build a rube goldberg software contraption that maybe leaves my system vulnerable. I want Good, Better, Best A&A systems that are "best practices" and strong guidance on implementation. Security/A&A is too important for "well, it works." or "it is good enough." It has to be right. (Soap box off).

For my purposes, I'm rewriting the Asp pages into Blazor, except Login. Your mileage may vary.

Found another interesting Tutorial - Enable your Web Apps to sign-in users and call APIs with the Microsoft identity platform for developers:
https://docs.microsoft.com/en-us/samples/azure-samples/active-directory-aspnetcore-webapp-openidconnect-v2/enable-webapp-signin/
It appears to be comprehensive tutorial on Authentication for all MS platforms.

@javiercn

With regards to authentication Blazor Server applications are considered traditional web applications...

That makes me a sad panda :(

... nothing prevents you from implementing your own authentication...

Nothing, except we don't know how to do this and the default solution is ugly and hackish.

Take a look at how we implemented custom cookie-based authentication using Blazor login views in Xomega. Since the login view, just as all other views, is generated from the Xomega model as a Blazor component, and inherits from a base class in the Xomega.Framework.Blazor package, you don't want to replicate the same code in a separate Razor page.

Essentially, once you run your authentication logic and construct a ClaimsPrincipal in the login Blazor component, you can call the injected SignInManager from the Xomega.Framework.Blazor package, and get an encrypted authentication ticket that expires in 1 minute by default. Then you pass that ticket to a very simple SignIn razor page like this.

The SignIn.cshtml razor page is then able to use that ticket to sign in the user and redirect to the url provided in the redirectUri parameter, all in just one line of code, thanks again to the injected SignInManager from the Xomega.Framework.Blazor package.

You can check out our entire working solution based on the sample AdventureWorks database that demonstrates Xomega Framework and Xomega-generated apps using Blazor Server and other .Net technologies here: https://github.com/Xomega-Net/Xomega.Examples

I've just stumbled onto this minefield myself. I have an ASP.NET Core 3.1 app that uses Identity Core - controllers, views and pages all work correctly with authentication.

I tried to retrofit a Blazor WASM client into this so we can start the migration process to Blazor with a hybrid Blazor-RazorPages-MVC application and gradually move functionality into Blazor components - this is likely to be a common scenario out there for anyone with an existing application.

I used the RC1 template for an ASP.NET Core hosted Blazor WASM client to understand the changes I needed to make to the Server to make it work, and I now have a Blazor client using Identity server correctly but has broken all the MVC authentication - any MVC returns a 401 unauthorised response instead of redirecting to login.

I've almost certainly done something wrong somewhere but with no official guidance on retrofitting Blazor into an existing app it's hard to figure out which of the many services.AddXX or app.UseXX statements and settings to change.

I tried going back to basics and created a basic RC1 ASPNET hosted template app. It routes to the controller, any request to an action marked [Authorize] results in 401. I can see that when Blazor calls a WebAPI controller a 401 response is the correct result, but I'm not able to figure out how to get regular controllers to redirect to the login page.

Hi, I think you must change your view - separate the client-side part from the WebApi. WebApi answers with 401 when the action require authentication/authorization and your request doesn't meet this requirement (e.g. does not contain a valid token). Then on the client-side you must react to such a response from the API, either informing the user about the lack of permissions, or redirecting to the login page.

Read more about CascadingAuthenticationState, AuthorizeView and AuthenticationStateProvider in Client-side Blazor applications, you can start with that:
https://gist.github.com/SteveSandersonMS/175a08dcdccb384a52ba760122cd2eda
https://docs.microsoft.com/en-us/aspnet/core/security/blazor/?view=aspnetcore-3.1

Thanks @askolniak I do indeed want to keep the WebAPI 401 behaviour, but I need normal views/controllers in the app to redirect to Login. I fully understand the CascadingAuthenticationState etc. this isn't about them, it's regular MVC controllers since this is an existing application.

Hi, My experience, which is by no means exhaustive, if you are in a mixed environment, this should be workable. If I were to put this in to a matrix, there would be Blazor Server and Blazor Server/WASM across the top. Then down the side is 100% Blazor, and then MVC/Blazor. The 4 combinations have slightly different solutions because of the underlying tech. We can add a third dimension to our matrix regarding Cookies and Tokens. And for more fun, a 4th dimension regarding providers: Local, Remote(API). I'll stick with the 2d matrix for now. Two tech issues before getting to the matrix.

My understanding is that MVC pages access HTTPContext to constantly retrieve Identity Info. With Blazor, HTTPContext is set at startup, and then never updated. This is the major reason for issues. This is not a bug, or any kind of mistake, just two vastly different techs at work. Sort of like the difference between snail mail (Addressee, Return Address, Postmark) and a telephone call (phone number, caller id). They are fundamentally different. Again, my understanding is this is why SignInManager, and UserManager (which actually does kinda work) don't work in Blazor (HTTPContext.)

For a cookie A&A scheme to work, the authentication cookie has to get INTO THE BROWSER. It is not enough to just make an API call back to the server, authenticate using SignInManager (which is in MVC space), and return a response. The authentication cookie won't be in the browser. Using DevTools, you can see the auth cookie go into the browser in a MVC app, and not in Blazor if just using API.

BS - MVC/Blazor: This is the default you get after creating a Blazor Server app, and then scaffolding in Identity (overriding default pages.) You use the MVC pages for Login/Logout, and can use the remaining MVC pages, or go to Blazor. This works because MVC pages have access to HTTPContext, and the cookie goes into the browser. If converting an existing app, this does work pretty much out of the box. If converting an application, this is the combo you want. Create a test application and build out the infrastructure to mimic your real app.

BS/WASM - 100% Blazor & BS/WASM - MVC/Blazor: I haven't spent much time on this combo, but others have. Look for BlazorIdentity and BlazorBoilerPlate on GitHub. They set up an API controller on the server, and the WASM client calls back for Authentication. BlazorIdentity uses a cookie, and works great. But when I built my own, the cookie wasn't going into the Browser. BlazorBoilerPlate uses IdentityServer4 and tokens. It is real bulletproof. Both of these use a 3.2 preview library that is the key to success. (I can only use GA products, hence why I haven't dived into the WASM end of the pool.)

Blazor Server - 100% Blazor: This is the most difficult combo as there are multiple dead ends down every path. As already discussed, MVC components like SignInManager will not work ("the response headers cannot be modified..." error). If you switch to an API, getting the cookie into the browser is a problem. Bearer tokens work better than cookies in this scenario (and are the recommended approach). But now you have to spin up a token server or service--Azure AD and Azure B2C are good if staying 100% BS. If splitting to WASM, IdentityServer4 is good. Upside of Azure AD & B2C is they can be working in a day or so, and scale monstrously. Downside is the UI/UX is as MS defines. If you want to make changes (e.g. a UserId instead of Email as unique identifier), it is a full on customization. Not for the timid.

I have come across something interesting called RevalidatingIdentityAuthenticationStateProvider If you create a BS app and added Identity during creation (not afterwards as scaffolding), this is created as the new AuthProvider. It inherits from ServerAuthenticationStateProvider, which inherits from AuthStateProvider. SASP definition says "An Microsoft.AspNetCore.Components.Authorization.AuthenticationStateProvider intended for use in server-side Blazor." WHOA, where was this all my life? I found a note by Steve Sanderson (https://github.com/dotnet/aspnetcore/issues/12692) asking to have local authenticatoin hook into this mechanism. There are only a handful of pages on the web about this. No docs of relevance I could find. Basically, this enables Host based Authentication into Blazor. I'm not done, but this has been nearly 100% dead on, until yesterday. I ran into a gotcha about DBcontext threading issues. (https://docs.microsoft.com/en-us/ef/core/miscellaneous/configuring-dbcontext#avoiding-dbcontext-threading-issues.) The recommended solutions were to have DBContext with a scoped lifetime and/or create contexts through IServiceScopeFactory. I already had that to begin with, and tried multiple variations, with no joy. (different topic.)

That is kind of my map of the landscape around Blazor A&A. One more note: You really want to stick with the MS facilities (https://docs.microsoft.com/en-us/aspnet/core/security/blazor/?view=aspnetcore-3.1) Using component and all the other facilities is great. The trick is how to implement the underlying system.

There is a non-Blazor MS Identity tutorial at (https://docs.microsoft.com/en-us/samples/azure-samples/active-directory-aspnetcore-webapp-openidconnect-v2/enable-webapp-signin/) It is a good reference.

Hope this has been helpful.

Awesome reply @bdnts - I'm still experimenting with retrofitting Blazor into MVC apps. Will test at weekend if my wife lets me :)

I'm not sure if this will help you guys, but I created a minimal example of a Blazor Server App with Azure AD Authentication, that calls the Microsoft Graph API on-behalf of the signed-in user here.
https://github.com/wmgdev/BlazorGraphApi
This uses Microsoft.Identity.Web

Microsoft.Identity.Web has plenty of other options and examples, razor friendly and the app above could be adapted for other scenarios. It may at least be something else you could look at.

Thank you for contacting us. Due to a lack of activity on this discussion issue we're closing it in an effort to keep our backlog clean. If you believe there is a concern related to the ASP.NET Core framework, which hasn't been addressed yet, please file a new issue.

This issue will be locked after 30 more days of inactivity. If you still wish to discuss this subject after then, please create a new issue!

The new Blazor code in Microsoft.Idenity.Web is awesome
I've updated https://github.com/wmgdev/BlazorGraphApi to use this

Was this page helpful?
0 / 5 - 0 ratings

Related issues

githubgitgit picture githubgitgit  路  3Comments

ermithun picture ermithun  路  3Comments

aurokk picture aurokk  路  3Comments

guardrex picture guardrex  路  3Comments

TanvirArjel picture TanvirArjel  路  3Comments