Jaderson C. Horst
Jaderson C. Horst

Reputation: 3

Error login in the login page with blazor and identity

I have a situation that I belive the problem is the life cicle of the components, using blazor server-side and .net 8. Using the structure that blazor ofers me, after going from the login page where the user can log in with email and password, I'm being redirect to the 'LoginWith2fa' page to insert the two-factor autenticator code. When i try to submit my form using OnValidSubmit the Input text of my code is empty.

This is static mode, if i try to use InterctiveServer it work, but I'll have another problem when I call some of the extensions methods of SignInManager like in OnInitialized or anywhere else saing that 'The response has already started'.

The login page after the login just call IdentityRedirectManager.RedirectTo("/Account/LoginWith2fa")

@page "/Account/LoginWith2fa"
 
@using System.ComponentModel.DataAnnotations
@using Microsoft.AspNetCore.Identity
@using BrokerWeb.Server.Data;
@using BrokerWeb.Server.Data.Identity
 
@inject SignInManager<ApplicationUser> SignInManager
@inject UserManager<ApplicationUser> UserManager
@inject IdentityRedirectManager RedirectManager
@inject ILogger<LoginWith2fa> Logger
 
<PageTitle>Two-factor authentication</PageTitle>
 
<EditForm FormName="MFAAuthentication" Model="Input" OnValidSubmit="this.OnValidSubmitAsync">
<MudPaper Class="pa-6" Elevation="15" MaxWidth="500px" Style="margin:auto; margin-top:50px;">
 
        <MudCard>
<MudCardContent>
 
                <MudText Typo="Typo.h4" Align="Align.Center">Two-factor authentication</MudText>
<MudDivider Class="mb-4" />
 
                <MudAlert Severity="MudBlazor.Severity.Info" Dense="true">
                    Seu login é protegido com autenticação em dois fatores. Informe o código de autenticação!
</MudAlert>
 
                <DataAnnotationsValidator />
<ValidationSummary class="text-danger" role="alert" />
 
                <MudTextField Label="MFA"
                                    @bind-Value="Input.TwoFactorCode"
                                    For="@(() => Input.TwoFactorCode)"
                                    Margin="Margin.Dense"
                                    Variant="Variant.Outlined"
                                    AdornmentColor="Color.Primary"
                                    Adornment="Adornment.Start"
                                    T="string"
                                    MaxLength="6" />
 
                <MudText Error="@ErrorMessage" Class="text-danger mb-2" />
 
                <MudCheckBox @bind-Checked="@Input.RememberMachine"
                             Label="Lembre-se de mim"
                             T="bool" />
</MudCardContent>
<MudCardActions>
<MudButton ButtonType="ButtonType.Submit"
                           Variant="Variant.Filled"
                           Color="Color.Primary"
                           FullWidth="true">
                    Log In
</MudButton>
</MudCardActions>
</MudCard>
 
    </MudPaper>
</EditForm>
 
@code {
    private string ErrorMessage = string.Empty;
 
    private ApplicationUser user = default!;
 
    private InputModel Input { get; set; }
        = new InputModel();
 
    [SupplyParameterFromQuery]
    private string ReturnUrl { get; set; }
 
    [SupplyParameterFromQuery]
    private bool RememberMe { get; set; }
 
    protected override async Task OnInitializedAsync()
    {
        user = await SignInManager.GetTwoFactorAuthenticationUserAsync() ??
            throw new InvalidOperationException("Unable to load two-factor authentication user.");
    }
 
    private async Task OnValidSubmitAsync()
    {
        var userId = await UserManager.GetUserIdAsync(user);
        try
        {
            if (string.IsNullOrEmpty(Input.TwoFactorCode))
                throw new ArgumentException("Código de autenticacao não informado!");
 
            var authenticatorCode = Input.TwoFactorCode!.Replace(" ", string.Empty).Replace("-", string.Empty);
            var result = await SignInManager.TwoFactorAuthenticatorSignInAsync(authenticatorCode, RememberMe, Input.RememberMachine);
 
            if (result.Succeeded)
            {
                Logger.LogInformation("Usuário id '{UserId}' logou com 2fa!", userId);
                RedirectManager.RedirectTo(ReturnUrl);
            }
            else if (result.IsLockedOut)
            {
                Logger.LogWarning("Conta do usuário id '{UserId}' bloqueada!", userId);
                RedirectManager.RedirectTo("Account/Lockout");
            }
            else
                throw new ArgumentException("Código de autenticação informado é inválido!");
 
        }
        catch (Exception exception)
        {
            Logger.LogWarning(exception.Message);
            ErrorMessage = exception.Message;
        }
    }
 
    private sealed class InputModel
    {
        [Required]
        public string TwoFactorCode { get; set; }
 
        public bool RememberMachine { get; set; }
    }
}

Upvotes: 0

Views: 79

Answers (1)

Qiang Fu
Qiang Fu

Reputation: 8811

There are 2 issues to be mentioned.
First is in SSR you need to define editform model like following

    [SupplyParameterFromForm]
    private InputModel Input { get; set; }

    protected override async Task OnInitializedAsync()
    {
        if (Input == null)
        {
            Input = new();
        }
    }

The other is editform and "Mud field" compatible issue in SSR. You need to add Name property to each field as workaround.

<MudTextField Label="MFA" Name="Input.TwoFactorCode" @bind-Value="Input.TwoFactorCode" ...
...
 <MudCheckBox Name="@Input.RememberMachine" @bind-Checked="@Input.RememberMachine" ...
...

And SignInManager needs HttpContext to work. But HttpContext in null when using websocket. So you can only use SSR for SignInManager.

Upvotes: 0

Related Questions