Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

.Net Core 3 Blazor Server - How to do basic login with claims?

I already have tables with usernames, passwords, and role IDs, so I do not want to use the ASP.NET Membership tables. And I only need a simple login page that asks for username and password, not their pre-built Identity.UI package.

But I do not know how to get Blazor Server to do a basic login for me. I have what I believe should be the basics configured in Startup.cs, such as:

In ConfigureServices:

services
    .AddAuthentication(Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationDefaults.AuthenticationScheme)
    .AddCookie();

In Configure():

app.UseAuthentication();
app.UseAuthorization();

Normally I would call something like this:

await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, 
    principal, 
    new AuthenticationProperties { IsPersistent = loginData.RememberMe });

And in fact the above does work if I run it from a .cshtml page, but I want this to work as a Blazor component (.razor).

I tried injecting httpContextAccessor but trying to call the SignInAsync method from there resulted in an error about the headers already being set.

I have a .razor Login component which I don't know what to call to do an actual sign in.

@page "/login"


@using Microsoft.AspNetCore.Authentication.Cookies;
@using Microsoft.AspNetCore.Components;
@using System.Security.Claims;


@inject NavigationManager navigationManager
@inject DataAccess.Models.CurrentDataContext db

<h3>Login</h3>

<div>
    Email:
    <input type="text" @bind="@email" />
</div>

<div>
    Password:
    <input type="text" @bind="@password" />
</div>

<div>
    <button class="btn btn-primary" @onclick="DoLogin">Log In</button>
    <span class="text-danger">@loginErrorMessage</span>
</div>

@code {
    string email = "";
    string password = "";
    string loginErrorMessage = "";

    protected async Task DoLogin()
    {
        var emailTrimmed = email.ToLower().Trim();
        var user = db.UserAccount.FirstOrDefault(u => u.EmailAddress.ToLower() == emailTrimmed);
        if (user != null) //if (user != null)
        {

            //TODO check password


            var claims = new List<Claim> { 
                new Claim(ClaimTypes.Name, emailTrimmed)
            };

            var claimsIdentity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);


            //SIGN in the user

            ///////////////////////////
            //   WHAT GOES HERE?   ////
            ///////////////////////////
            //

            navigationManager.NavigateTo("/fetchdata");

        }
        else
        {
            loginErrorMessage = "Error logging in.";
        }
    }
}
like image 624
FirstDivision Avatar asked Oct 15 '19 21:10

FirstDivision


2 Answers

I made a version, which is based on this guy's post: https://www.oqtane.org/Resources/Blog/PostId/527/exploring-authentication-in-blazor?fbclid=IwAR0rbQkY47cHHxs29HWCk0RggH7GHeLDx3kJ4vwmgUGMTsFU3hxpsQ9ybZo

Honestly, I made very little of my project myself, but mostly stole what I needed from the oqtane post above.

And I think he based his project on Michael's answer above.

I have the project on git (First time using git, I committed way too much, it's currently a bit messy): https://github.com/TroelsMortensen/DNP/tree/master/Blazor/JSLogin

I must admit I'm not entirely sure, what I'm doing. And it's not finished. Right now there's no check against user database or actual validation or whatever. And the claims are hardcoded. But that should be easy to implement. I needed something super simple, cookie authentication, without Identity. With this approach I control almost everything myself. I have a javascript (again, not really mine) which can send post requests to a razor page, I think.

The Pages/Account/Login.razor sends a call to Shared/Interop.cs to make it use the javascript in wwwroot/interop.js to call the post method in Pages/Account/LoginRequester.cshtml.cs. I think.

I include the interop.js in the Pages/_Host.cshtml to get access to it

I use the HttpContext to sign in. The LoginDisplay.razor is inserted into Shared/MainLayout.razor.

like image 180
Troels Avatar answered Oct 28 '22 02:10

Troels


As I cover in: A Demonstration of Simple Server-side Blazor Cookie Authentication

You can log a person in with the following code in a .cshtml file:

public class LoginModel : PageModel
{
    public string ReturnUrl { get; set; }
    public async Task<IActionResult> 
        OnGetAsync(string paramUsername, string paramPassword)
    {
        string returnUrl = Url.Content("~/");
        try
        {
            // Clear the existing external cookie
            await HttpContext
                .SignOutAsync(
                CookieAuthenticationDefaults.AuthenticationScheme);
        }
        catch { }
        // *** !!! This is where you would validate the user !!! ***
        // In this example we just log the user in
        // (Always log the user in for this demo)
        var claims = new List<Claim>
        {
            new Claim(ClaimTypes.Name, paramUsername),
            new Claim(ClaimTypes.Role, "Administrator"),
        };
        var claimsIdentity = new ClaimsIdentity(
            claims, CookieAuthenticationDefaults.AuthenticationScheme);
        var authProperties = new AuthenticationProperties
        {
            IsPersistent = true,
            RedirectUri = this.Request.Host.Value
        };
        try
        {
            await HttpContext.SignInAsync(
            CookieAuthenticationDefaults.AuthenticationScheme,
            new ClaimsPrincipal(claimsIdentity),
            authProperties);
        }
        catch (Exception ex)
        {
            string error = ex.Message;
        }
        return LocalRedirect(returnUrl);
    }
}

As to logging the person in in a .razor page, don't worry about that. You have to perform a full post back to establish the authentication cookie. a .razor pages doesn't have a 'post back' but a .cshtml page does.

like image 36
Michael Washington Avatar answered Oct 28 '22 02:10

Michael Washington