Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve documentation for blazor authentication #47490

Closed
1 task done
MarvinKlein1508 opened this issue Mar 30, 2023 · 9 comments
Closed
1 task done

Improve documentation for blazor authentication #47490

MarvinKlein1508 opened this issue Mar 30, 2023 · 9 comments
Labels
area-blazor Includes: Blazor, Razor Components ✔️ Resolution: Answered Resolved because the question asked by the original author has been answered. Status: Resolved

Comments

@MarvinKlein1508
Copy link

MarvinKlein1508 commented Mar 30, 2023

Is there an existing issue for this?

  • I have searched the existing issues

Is your feature request related to a problem? Please describe the problem.

I find questions regarding the authentication progress in blazor apps quite often all over the internet. At least twice a week I see people asking about this stuff on StackOverFlow.

The docs for blazor authentication can be found here. But in my opinion those are not very good documented. For example I still have not figured out after 4 years how to refresh my user claims with updated roles.

The documentation should include some basic tutorials on how to implement authentification in both Blazor Server and Blazor WebAssembly.

One common example I find a lot of people asking about is how to check against a local Active Directory. In my use case for example I'll need to check first in database if login credentails of a non AD account has been entered if not then I'm checking for AD. If the user is found, I create it in my database.

That's also the reason why I cannot use ASP.NET Core Identity (maybe I'm missing something here, then feel free to tell my about this magic).

I still don't know if I can do authentication without the usage of Razor Pages or MVC.

Describe the solution you'd like

I would highly appreciate if you can create simple demo projects showcasing all types of authentification in both Blazor Server and Blazor WebAssembly

  • Claims authentication
  • LDAP authentication
  • authentication with two mechanismn
  • ASP.NET Core Identity authentication
  • Refreshing roles for those authentifications
  • More?

Additional context

No response

@dotnet-issue-labeler dotnet-issue-labeler bot added the area-blazor Includes: Blazor, Razor Components label Mar 30, 2023
@javiercn
Copy link
Member

@MarvinKlein1508 thanks for contacting us.

Claims authentication

Can you clarify what you mean by this?

LDAP authentication

With regards to Blazor Server, authentication happens "before" the app starts, so using LDAP is similar to how you would do it for an MVC / Razor Pages app.

If you need to refresh claims/roles, then you need to provide your own AuthenticationStateProvider

The mechanism you use to get "refreshed" credentials depends on the type of auth you are using, either your auth system gives you an API to retrieve them, or they come in some other way (connection or a cookie), in which case, you need an extra "session" endpoint defined in the app that the browser can ping regularly to push a notification into the circuit.

You can keep a registry of available circuits with a circuit handler and a singleton service to "dispatch" a notification to a given circuit.

authentication with two mechanismn

I am assuming this means two different authentication schemes, like Microsoft accounts and something else. Neither of this is dealt with at the Blazor level.

  • For Blazor Server apps, auth happens before the app starts, but you need to have already authenticated the user when that happens.
  • For Blazor Webassembly apps, we don't have the concept of multiple authentication schemes within the Webassembly app itself, as it greatly complicates the implementation, and the same result can be achieved via Identity and social logins.

Refreshing roles for those authentifications

  • We have docs for webassembly on this here for AAD
  • For our built-in Identity support we have docs here
  • For Identity we have info on how to configure roles here

With that said, the team is making investments in this area for .NET 8.0, so I am going to let other folks chime in.

@MarvinKlein1508
Copy link
Author

@MarvinKlein1508 thanks for contacting us.

Claims authentication

Can you clarify what you mean by this?

Sure. I'm doing this right now.
https://github.com/MarvinKlein1508/FormPortal/blob/d0f47273cc5accee1feacd8e99655ef9007637be/FormPortal/Pages/Account/Login.cshtml.cs#L48

As you can see I'm authenticating the user in a seperate .cshtml file.

LDAP authentication

With regards to Blazor Server, authentication happens "before" the app starts, so using LDAP is similar to how you would do it for an MVC / Razor Pages app.

My main problem here is that I've never worked with MVC before and I only did some basic forms using razor pages. I'm developing apps using Blazor. It's hard for a developer to learn all three concepts just to do the same thing in one framework. That's why I'm thinking that you should provide detailed tutorials in the docs for the Blazor framework itself.

Unlucky for me, I didn't get paid to spent hours of hours learning new concepts. I think when I choose a framework, I should be able to do everything with it without learning another framework. Don't get me wrong, it's obviously good that I can mix multiple frameworks together but it makes developing with the framework much harder for new people.

I'm working for 5 years now with Blazor Server and outside of the login mechanismn I've never needed anything else besides of my Blazor components.

If you need to refresh claims/roles, then you need to provide your own AuthenticationStateProvider

I've tried this before but I couldn't figure out how to access my set claims here in order to receive my user form the database. Is it also possible to check if the users got deleted in the database and delete all claims within this approach?

The mechanism you use to get "refreshed" credentials depends on the type of auth you are using, either your auth system gives you an API to retrieve them, or they come in some other way (connection or a cookie), in which case, you need an extra "session" endpoint defined in the app that the browser can ping regularly to push a notification into the circuit.

In my case all necessary data is stored within the database. I can fetch all I'll need by using my UserService.GetAsync(int userId) method.

You can keep a registry of available circuits with a circuit handler and a singleton service to "dispatch" a notification to a given circuit.
I have no idea how this could help me. Could you provide me more information on what this is good for?

authentication with two mechanismn

I am assuming this means two different authentication schemes, like Microsoft accounts and something else. Neither of this is dealt with at the Blazor level.

What I mean by this are two ways of authentication. For example:

  1. provider checks for accounts in db
  2. provider checks against LDAP

As you can see in my previously mentioned file, I'm doin this right now in a POST request from a razor page all in once. I'm pretty sure there is a much easier way to achive this. The implementation I'm having now only exists because I've copied it from the scaffolding templates. I didn't find anything near this in the docs.

* For Blazor Server apps, auth happens before the app starts, but you need to have already authenticated the user when that happens.

* For Blazor Webassembly apps, we don't have the concept of multiple authentication schemes within the Webassembly app itself, as it greatly complicates the implementation, and the same result can be achieved via Identity and social logins.

Refreshing roles for those authentifications

* We have docs for webassembly on this for AAD
* For our built-in Identity support we have docs 
* For Identity we have info on how to configure roles 

Yes I've seen this docs. But I'm not not using neither Identity nor AAD. Many small companies want to check against a small database or their local LDAP. You wouldn't believe how many companies in Europe don't use cloud services.

My main problem why I can't use Identity is the lack of local LDAP support.

@javiercn could you also tell me if it's possible within Blazor server to create a login form and login the user just with using blazor components?

Thanks for all your explanations and time! :)

@MarvinKlein1508 MarvinKlein1508 changed the title Improve documentation for razor authentication Improve documentation for blazor authentication Mar 30, 2023
@javiercn
Copy link
Member

javiercn commented Mar 30, 2023

My main problem here is that I've never worked with MVC before and I only did some basic forms using razor pages. I'm developing apps using Blazor. It's hard for a developer to learn all three concepts just to do the same thing in one framework. That's why I'm thinking that you should provide detailed tutorials in the docs for the Blazor framework itself.

Unlucky for me, I didn't get paid to spent hours of hours learning new concepts. I think when I choose a framework, I should be able to do everything with it without learning another framework. Don't get me wrong, it's obviously good that I can mix multiple frameworks together but it makes developing with the framework much harder for new people.

I'm working for 5 years now with Blazor Server and outside of the login mechanismn I've never needed anything else besides of my Blazor components.

So, if I understood correctly, what you want is to build an Auth system completely using Blazor Server, without MVC/Razor pages in the picture?

The challenge here comes down to "remembering" whether the user has a valid session in the system. That's normally done with an HTTP only cookie in the browser. Otherwise, an XSS attack could steal your cookie. Unfortunately, Blazor Server can't set cookies because it is not a web app in the traditional sense, which is why auth is handled "outside" the Blazor app.

I've tried this before but I couldn't figure out how to access my set claims here in order to receive my user form the database. Is it also possible to check if the users got deleted in the database and delete all claims within this approach?

If you have an identifier for your currently logged-in user (provided that the ID is in the ClaimsPrincipal), and you can query a database, then you should be able to write a custom AuthenticationStateProvider to achieve this. You can write an AuthorizationPolicy and do a check for whether the data is up to date before you authorize a user, or you can periodically check against the database and update the current user.

You can use RevalidatingAuthenticationStateProvider to do it. There are more details here

@javiercn
Copy link
Member

In my case all necessary data is stored within the database. I can fetch all I'll need by using my UserService.GetAsync(int userId) method.

What I mean by this are two ways of authentication. For example:

  • provider checks for accounts in db
  • provider checks against LDAP

If you do not care about the user being "logged-in" after they come back from closing the tab, then you can do everything with Blazor Server and a custom AuthenticationStateProvider.

The only challenge for Blazor Server is to setup a cookie to "remember" that the user is logged in, which is what the approach I mentioned above was for. The facts are, that approach is possible, but complicated (from a security point of view), hence why we recommend you authenticate and establish the session in a traditional way, as that is a test and tried path.

@MarvinKlein1508
Copy link
Author

So, if I understood correctly, what you want is to build an Auth system completely using Blazor Server, without MVC/Razor pages in the picture?

Yes I don't want to switch between frameworks to achive this. Also it is not clear for me from the docs that I'll need to do this using MVC and razor pages.

The challenge here comes down to "remembering" whether the user has a valid session in the system. That's normally done with an HTTP only cookie in the browser. Otherwise, an XSS attack could steal your cookie. Unfortunately, Blazor Server can't set cookies because it is not a web app in the traditional sense, which is why auth is handled "outside" the Blazor app.

I don't know much about the security aspect of this. But couldn't you just set a cookie using JavaScript?

If you have an identifier for your currently logged-in user (provided that the ID is in the ClaimsPrincipal),
Yes I have the user ID stored in my Claims. Now I take a look on the docs:

public override Task<AuthenticationState> GetAuthenticationStateAsync()
{
    var identity = new ClaimsIdentity(new[]
    {
        new Claim(ClaimTypes.Name, "mrfibuli"),
    }, "Custom Authentication");

    var user = new ClaimsPrincipal(identity);

    return Task.FromResult(new AuthenticationState(user));
}

I literally have no idea how I can access the claims here. How do I get the UserId from my claim to read data for it?

@MarvinKlein1508
Copy link
Author

MarvinKlein1508 commented Mar 30, 2023

* provider checks for accounts in db
* provider checks against LDAP

Can I do this with two registered providers as well? For example:

  1. Provider checks in the database, it finds no user
  2. Then the second provider is being used

If you do not care about the user being "logged-in" after they come back from closing the tab, then you can do everything with Blazor Server and a custom AuthenticationStateProvider.

I will take a look into it. This one sounds really promising.

The only challenge for Blazor Server is to setup a cookie to "remember" that the user is logged in, which is what the approach I mentioned above was for. The facts are, that approach is possible, but complicated (from a security point of view), hence why we recommend you authenticate and establish the session in a traditional way, as that is a test and tried path.

Just out of curiosity: which data does this cookie hold? Couldn't I manually set a cookie with username and a token and check for it in my custom AuthenticationProvider?

@javiercn
Copy link
Member

I don't know much about the security aspect of this. But couldn't you just set a cookie using JavaScript?

No, the cookie needs to be set by the server as part of an HTTP response.

@javiercn
Copy link
Member

@MarvinKlein1508 if you create your own AuthenticationStateProvider, you can inject services to check against the database.

services.AddScoped<AuthenticationStateProvider, CustomProvider>();
services.AddScoped<CustomProvider>(sp => (CustomProvider)sp.GetRequiredService<AuthenticationStateProvider>());

public class CustomProvider : AuthenticationStateProvider
{
   private Task<AuthenticationState> _authenticationStateTask;
   public CustomProvider(<<Dependencies>>)
   {
   }

   public async Task<...> CheckRolesAsync(...)
   {
        var authState = await _authenticationStateTask;
        var user = authState.User;
        var id = // Get the ID for your user from the claims principal you created on LoginCore
        // Check against your Db(s) using that ID.
        // Produce whatever response you want based on that.
   }

   public override Task<AuthenticationState> GetAuthenticationStateAsync()
        => _authenticationStateTask;

   public async Task Login(...)
   {
      // Trigger the login process
      _authenticationStateTask = LoginCore(...)
     // Signal the UI that you are authenticating the user
      NotifyAuthenticationStateChanged(_authenticationStateTask);
     // Return the result so that your component can act accordingly
      return _authenticationStateTask;
   }
   
   LoginCore(...)
   {
       // Validate credentials
       return new AuthenticationState(new ClaimsPrincipal(...));
   }
}

Here is some pseudo-code. Hope that helps

@mkArtakMSFT mkArtakMSFT added the ✔️ Resolution: Answered Resolved because the question asked by the original author has been answered. label Mar 30, 2023
@ghost ghost added the Status: Resolved label Mar 30, 2023
@MarvinKlein1508
Copy link
Author

@mkArtakMSFT I don‘t think this should be closed allready. @javiercn left it open to let other people join in.

As you can see from all my questions, the docs should be much more detailed.

It would be nice if you can reopen it and add the docs label.

@ghost ghost locked as resolved and limited conversation to collaborators Apr 29, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-blazor Includes: Blazor, Razor Components ✔️ Resolution: Answered Resolved because the question asked by the original author has been answered. Status: Resolved
Projects
None yet
Development

No branches or pull requests

3 participants