Create Login Page With FluentEditForm and FluentTextField #2957
-
how to create Login Page With FluentEditForm and FluentTextField for captcha code im using https://github.com/VahidN/DNTCaptcha.Blazor @page "/Account/Login"
@layout LoginLayout
@attribute [AllowAnonymous]
<PageTitle>@AppStringResource.AccountStrings.LoginPageTitle</PageTitle>
<div style="
margin: 0;
background-image: url('/Assets/Images/loginBackground.svg'); /* Update path if needed */
background-size: cover;
background-position: center;
background-repeat: no-repeat;
background-attachment: fixed;
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;">
<div
style="display: flex; width: 60%; height: 480px; border-radius: 15px; box-shadow: 0px 8px 16px rgba(0, 0, 0, 0.2); overflow: hidden;">
<div
style="flex: 1; background-color: white; display: flex; align-items: center; justify-content: center; color: black; font-size: 24px;">
<FluentStack Orientation="Orientation.Vertical" VerticalAlignment="VerticalAlignment.Center">
<FluentLabel Style="width: 100%" Weight="FontWeight.Bold" Typo="Typography.H1"
Alignment="HorizontalAlignment.Center"
Color="Color.Accent">@AppStringResource.AccountStrings.LoginPageTitle</FluentLabel>
<EditForm method="post" Model="Content" Enhance="true" FormName="@FormName" style="padding: 25px"
OnValidSubmit="LoginUser">
<Validate/>
<FluentStack Orientation="Orientation.Horizontal" VerticalAlignment="VerticalAlignment.Center"
HorizontalAlignment="HorizontalAlignment.Start">
<FluentLabel Style="width: 10rem"
Typo="Typography.Subject">@Content.GetDisplayName(d => d.UserName)</FluentLabel>
<FluentTextField @bind-Value="Content.UserName"
DisplayName="Content.UserName"
Id="UserName"
AdditionalAttributes="@( new Dictionary<string, object>(){{"autocomplete","username"}})"
Size="100"
Required="true"
Appearance="FluentInputAppearance.Outline"
Maxlength="50"
Autofocus="true"
AutoComplete="true"
Immediate="true"
ImmediateDelay="500"
InputMode="InputMode.Text"
Minlength="1"
TextFieldType="TextFieldType.Text">
<FluentIcon Id="NameIcon" Value="@(new MyIcons.User())" Color="@Color.Neutral"
Slot="start"/>
</FluentTextField>
</FluentStack>
<FluentStack Style="row-gap: 1px; margin-top: 16px" Orientation="Orientation.Horizontal"
VerticalAlignment="VerticalAlignment.Center"
HorizontalAlignment="HorizontalAlignment.Start">
<FluentLabel Style="width: 10rem"
Typo="Typography.Subject">@Content.GetDisplayName(d => d.Password)</FluentLabel>
<FluentTextField @bind-Value="Content.Password"
DisplayName="Content.Password"
Size="100"
Id="Password"
AdditionalAttributes="@( new Dictionary<string, object>(){{"autocomplete","password"}})"
Required="true"
Appearance="FluentInputAppearance.Outline"
Maxlength="50"
Immediate="true"
ImmediateDelay="500"
InputMode="InputMode.Text"
Minlength="1"
TextFieldType="@_passwordTextFieldType">
<FluentIcon Value="@(new Icons.Regular.Size16.Password())" Color="@Color.Neutral"
Slot="start"/>
@if (_showPassword)
{
<FluentIcon Value="@(new Icons.Regular.Size16.Eye())" Color="@Color.Neutral" Slot="end"
OnClick="ChangeShowPassword"/>
}
else
{
<FluentIcon Value="@(new Icons.Regular.Size16.EyeOff())" Color="@Color.Neutral"
Slot="end" OnClick="ChangeShowPassword"/>
}
</FluentTextField>
</FluentStack>
<FluentStack Style="row-gap: 1px; margin-top: 16px" Orientation="Orientation.Horizontal"
VerticalAlignment="VerticalAlignment.Center"
HorizontalAlignment="HorizontalAlignment.Start">
<FluentLabel Style="width: 10rem"
Typo="Typography.Subject">@AppStringResource.CaptchaCode</FluentLabel>
<FluentTextField @bind-Value="Content.CaptchaCode"
DisplayName="Content.CaptchaCode"
Size="85"
Required="true"
Appearance="FluentInputAppearance.Outline"
Maxlength="8"
Immediate="true"
ImmediateDelay="500"
InputMode="InputMode.Text"
Minlength="4"
TextFieldType="TextFieldType.Text">
<FluentIcon Value="@(new Icons.Regular.Size16.LockShield())" Color="@Color.Neutral"
Slot="start"/>
</FluentTextField>
<DntInputCaptcha @bind-Value="Content.GeneratedCaptchaCode"
Language="@NumberToWordLanguage.Persian"
@rendermode="InteractiveServer"
DisplayMode="@DisplayMode.ShowDigits"
AllowThousandsSeparators="false"
Max="9000"
Min="1000"
AbsoluteExpiration="@TimeSpan.FromMinutes(2)"
ShowRefreshButton="false"
FontSize="21"
FontName="Times New Roman"
FontColor="#603F83FF"
BackgroundColor="#FCF6F5FF"
RandomLinesCount="2"
RandomCirclesCount="6"
BorderWidth="0.3"
BorderColor="grey"
NoisePointsCount="25"
Padding="11"
ShadowBlur="7"
ShadowColor="navy"
ShadowOffsetX="-3"
ShadowOffsetY="-3"
TimerInterval="@TimeSpan.FromMilliseconds(2500)"
CaptchaCanvasClass="canvas-small"
CaptchaDivClass="d-flex justify-content-center align-self-center ms-1"
RefreshButtonClass="bi-arrow-repeat btn"
RefreshButtonTitle="Refresh"/>
</FluentStack>
<div
Style="padding-right: 6rem; margin-top: 3rem; display: flex; justify-content: start; width: 100% !important">
<Button Style="width: 100px !important; justify-content: center;"
type="submit"
FormId="@FormName">@AppStringResource.AccountStrings.Login</Button>
</div>
<FluentStack Style="padding-left: 10px; padding-right: 10px;" Orientation="Orientation.Vertical"
VerticalAlignment="VerticalAlignment.Center"
HorizontalAlignment="HorizontalAlignment.Center">
<LoginStatusMessage Message="@errorMessage"></LoginStatusMessage>
</FluentStack>
</EditForm>
</FluentStack>
</div>
<div
style="flex: 1; background: linear-gradient(135deg, #8e2de2,#4a00e0); display: flex; align-items: center; justify-content: center; color: white; font-size: 24px;">
</div>
</div>
</div> using System.ComponentModel.DataAnnotations;
using System.Security.Claims;
using AngleSharp.Html.Dom.Events;
using Ganss.Xss;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.AspNetCore.Components.Forms;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Microsoft.FluentUI.AspNetCore.Components;
using YasBasculeWebApp.Domain.Entities;
using YasBasculeWebApp.Features.Account.Commands;
using YasBasculeWebApp.Shared.Components;
using YasBasculeWebApp.Shared.Resources;
namespace YasBasculeWebApp.Features.Account.Pages;
public partial class Login
{
[Inject] SignInManager<AppIdentityUser> SignInManager { get; set; }
[Inject] UserManager<AppIdentityUser> UserManager { get; set; }
[Inject] ILogger<Login> Logger { get; set; }
[Inject] IdentityRedirectManager RedirectManager { get; set; }
[CascadingParameter] private HttpContext? HttpContext { get; set; }
[SupplyParameterFromQuery] private string? ReturnUrl { get; set; }
[Inject] private NavigationManager NavigationManager { get; set; }
[CascadingParameter] private Task<AuthenticationState>? authenticationState { get; set; }
[SupplyParameterFromForm] private LoginCommand.LoginRequest? Content { get; set; } = default!;
private const string FormName = "LoginForm";
private string? messageFromCookie;
private bool _showPassword;
private List<string> errorMessage = new();
protected override void OnInitialized()
{
if (Content is null)
{
Content = new LoginCommand.LoginRequest
{
ReturnUrl = ReturnUrl ?? UiHelper.NavigationUrls.Dashboard,
Password = string.Empty,
UserName = string.Empty,
GeneratedCaptchaCode = string.Empty,
AntiForgeryToken = string.Empty,
CaptchaCode = string.Empty
};
}
base.OnInitialized();
}
protected override async Task OnInitializedAsync()
{
if (HttpContext is not null && HttpMethods.IsGet(HttpContext.Request.Method))
{
// Clear the existing external cookie to ensure a clean login process
await HttpContext.SignOutAsync(IdentityConstants.ExternalScheme);
messageFromCookie = HttpContext.Request.Cookies[IdentityRedirectManager.StatusCookieName];
if (messageFromCookie is not null)
{
HttpContext.Response.Cookies.Delete(IdentityRedirectManager.StatusCookieName);
}
}
}
public async ValueTask DisposeAsync()
{
// if (EditContext is not null)
// {
// EditContext.OnValidationStateChanged -= EditContextOnOnValidationStateChanged;
// }
await Task.CompletedTask;
}
// private async void EditContextOnOnValidationStateChanged(object? sender, ValidationStateChangedEventArgs e)
// {
// errorMessage = [];
// try
// {
// errorMessage.AddRange(EditContext.GetValidationMessages().Distinct().ToList());
// await InvokeAsync(StateHasChanged);
// }
// catch (Exception ex)
// {
// errorMessage.Add(AppStringResource.ErrorHappened);
// }
// }
[HttpPost]
[AutoValidateAntiforgeryToken]
private async void LoginUser(EditContext editContext)
{
try
{
var validate = editContext.Validate();
//editContext.NotifyValidationStateChanged();
if (!validate)
{
return;
}
ReturnUrl = new HtmlSanitizer().Sanitize(ReturnUrl ?? string.Empty);
if (string.IsNullOrEmpty(ReturnUrl))
{
ReturnUrl = "/"; // Set a default redirect URL if ReturnUrl is empty.
}
var user = await UserManager.FindByNameAsync(Content!.UserName);
if (user is null)
{
errorMessage.Add(AppStringResource.AccountStrings.InvalidUserNameOrPassword);
return;
}
if (!await UserManager.CheckPasswordAsync(user, Content.Password))
{
errorMessage.Add(AppStringResource.AccountStrings.InvalidUserNameOrPassword);
return;
}
var checkSignInResult = await SignInManager.CheckPasswordSignInAsync(user, Content.Password, true);
if (checkSignInResult.IsLockedOut)
{
errorMessage.Add(AppStringResource.AccountStrings.UserAccountLockout);
return;
}
if (checkSignInResult.IsNotAllowed)
{
errorMessage.Add(AppStringResource.AccountStrings.UserAccountIsNotAllowed);
return;
}
if (!checkSignInResult.Succeeded)
{
errorMessage.Add(AppStringResource.AccountStrings.InvalidUserNameOrPassword);
return;
}
if (checkSignInResult.RequiresTwoFactor)
{
RedirectManager.RedirectTo(
"Account/LoginWith2fa", new Dictionary<string, object?>
{
{ "returnUrl", ReturnUrl }
});
return;
}
var userRoles = await UserManager.GetRolesAsync(user!);
var userClaims = await UserManager.GetClaimsAsync(user!);
var claims = new List<Claim>();
claims.AddRange(userClaims);
claims.AddRange(userRoles.Select(role => new Claim(ClaimTypes.Role, role)));
await HttpContext!.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme,
new ClaimsPrincipal(new ClaimsIdentity(claims)));
RedirectManager.RedirectTo(ReturnUrl);
}
catch (Exception e)
{
errorMessage.Add(AppStringResource.ErrorHappened);
}
}
private TextFieldType _passwordTextFieldType = TextFieldType.Password;
public Login()
{
Content = new LoginCommand.LoginRequest
{
ReturnUrl = ReturnUrl ?? UiHelper.NavigationUrls.Dashboard,
Password = string.Empty,
UserName = string.Empty,
GeneratedCaptchaCode = string.Empty,
AntiForgeryToken = string.Empty,
CaptchaCode = string.Empty
};
}
private async Task ChangeShowPassword(MouseEventArgs arg)
{
if (arg.Button == (long)MouseButtons.Primary)
{
_showPassword = !_showPassword;
_passwordTextFieldType = _showPassword ? TextFieldType.Text : TextFieldType.Password;
await InvokeAsync(StateHasChanged);
}
await Task.CompletedTask;
}
} Because httpcontext and SigninManager are required for login, rendermode = InteractiveServer is not used. |
Beta Was this translation helpful? Give feedback.
Replies: 3 comments
-
I think the |
Beta Was this translation helpful? Give feedback.
-
i set value to the name property. but dos now worked. @page "/Account/Login"
@layout LoginLayout
@attribute [AllowAnonymous]
<PageTitle>@AppStringResource.AccountStrings.LoginPageTitle</PageTitle>
<div style="
margin: 0;
background-image: url('/Assets/Images/loginBackground.svg'); /* Update path if needed */
background-size: cover;
background-position: center;
background-repeat: no-repeat;
background-attachment: fixed;
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;">
<div
style="display: flex; width: 60%; height: 480px; border-radius: 15px; box-shadow: 0px 8px 16px rgba(0, 0, 0, 0.2); overflow: hidden;">
<div
style="flex: 1; background-color: white; display: flex; align-items: center; justify-content: center; color: black; font-size: 24px;">
<FluentStack Orientation="Orientation.Vertical" VerticalAlignment="VerticalAlignment.Center">
<FluentLabel Style="width: 100%" Weight="FontWeight.Bold" Typo="Typography.H1"
Alignment="HorizontalAlignment.Center"
Color="Color.Accent">@AppStringResource.AccountStrings.LoginPageTitle</FluentLabel>
<FluentEditForm method="post" Model="Content" Enhance="true" FormName="@FormName" style="padding: 25px"
OnValidSubmit="LoginUser">
<Validate/>
<FluentStack Orientation="Orientation.Horizontal" VerticalAlignment="VerticalAlignment.Center"
HorizontalAlignment="HorizontalAlignment.Start">
<FluentLabel Style="width: 10rem"
Typo="Typography.Subject">@Content.GetDisplayName(d => d.UserName)</FluentLabel>
<FluentTextField @bind-Value="Content.UserName"
DisplayName="@Content.UserName"
Name="UserName"
AdditionalAttributes="@( new Dictionary<string, object>(){{"autocomplete","username"}})"
Size="100"
Required="true"
Appearance="FluentInputAppearance.Outline"
Maxlength="50"
Autofocus="true"
AutoComplete="true"
Immediate="true"
ImmediateDelay="500"
InputMode="InputMode.Text"
Minlength="1"
TextFieldType="TextFieldType.Text">
<FluentIcon Id="NameIcon" Value="@(new MyIcons.User())" Color="@Color.Neutral"
Slot="start"/>
</FluentTextField>
</FluentStack>
<FluentStack Style="row-gap: 1px; margin-top: 16px" Orientation="Orientation.Horizontal"
VerticalAlignment="VerticalAlignment.Center"
HorizontalAlignment="HorizontalAlignment.Start">
<FluentLabel Style="width: 10rem"
Typo="Typography.Subject">@Content.GetDisplayName(d => d.Password)</FluentLabel>
<FluentTextField @bind-Value="Content.Password"
DisplayName="Content.Password"
Size="100"
Name="Password"
AdditionalAttributes="@( new Dictionary<string, object>(){{"autocomplete","password"}})"
Required="true"
Appearance="FluentInputAppearance.Outline"
Maxlength="50"
Immediate="true"
ImmediateDelay="500"
InputMode="InputMode.Text"
Minlength="1"
TextFieldType="@_passwordTextFieldType">
<FluentIcon Value="@(new Icons.Regular.Size16.Password())" Color="@Color.Neutral"
Slot="start"/>
@if (_showPassword)
{
<FluentIcon Value="@(new Icons.Regular.Size16.Eye())" Color="@Color.Neutral" Slot="end"
OnClick="ChangeShowPassword"/>
}
else
{
<FluentIcon Value="@(new Icons.Regular.Size16.EyeOff())" Color="@Color.Neutral"
Slot="end" OnClick="ChangeShowPassword"/>
}
</FluentTextField>
</FluentStack>
<FluentStack Style="row-gap: 1px; margin-top: 16px" Orientation="Orientation.Horizontal"
VerticalAlignment="VerticalAlignment.Center"
HorizontalAlignment="HorizontalAlignment.Start">
<FluentLabel Style="width: 10rem"
Typo="Typography.Subject">@AppStringResource.CaptchaCode</FluentLabel>
<FluentTextField @bind-Value="Content.CaptchaCode"
DisplayName="Content.CaptchaCode"
Name="CaptchaCode"
Size="85"
Required="true"
Appearance="FluentInputAppearance.Outline"
Maxlength="8"
Immediate="true"
ImmediateDelay="500"
InputMode="InputMode.Text"
Minlength="4"
TextFieldType="TextFieldType.Text">
<FluentIcon Value="@(new Icons.Regular.Size16.LockShield())" Color="@Color.Neutral"
Slot="start"/>
</FluentTextField>
<DntInputCaptcha @bind-Value="Content.GeneratedCaptchaCode"
Language="@NumberToWordLanguage.Persian"
@rendermode="InteractiveServer"
AdditionalAttributes="@(new Dictionary<string, object>(){{"Name","GeneratedCaptchaCode"}})"
DisplayMode="@DisplayMode.ShowDigits"
AllowThousandsSeparators="false"
Max="9000"
Min="1000"
AbsoluteExpiration="@TimeSpan.FromMinutes(2)"
ShowRefreshButton="false"
FontSize="21"
FontName="Times New Roman"
FontColor="#603F83FF"
BackgroundColor="#FCF6F5FF"
RandomLinesCount="2"
RandomCirclesCount="6"
BorderWidth="0.3"
BorderColor="grey"
NoisePointsCount="25"
Padding="11"
ShadowBlur="7"
ShadowColor="navy"
ShadowOffsetX="-3"
ShadowOffsetY="-3"
TimerInterval="@TimeSpan.FromMilliseconds(2500)"
CaptchaCanvasClass="canvas-small"
CaptchaDivClass="d-flex justify-content-center align-self-center ms-1"
RefreshButtonClass="bi-arrow-repeat btn"
RefreshButtonTitle="Refresh"/>
</FluentStack>
<div
Style="padding-right: 6rem; margin-top: 3rem; display: flex; justify-content: start; width: 100% !important">
<Button Style="width: 100px !important; justify-content: center;"
type="submit"
FormId="@FormName">@AppStringResource.AccountStrings.Login</Button>
</div>
<FluentStack Style="padding-left: 10px; padding-right: 10px;" Orientation="Orientation.Vertical"
VerticalAlignment="VerticalAlignment.Center"
HorizontalAlignment="HorizontalAlignment.Center">
<LoginStatusMessage Message="@errorMessage"></LoginStatusMessage>
</FluentStack>
</FluentEditForm>
</FluentStack>
</div>
<div
style="flex: 1; background: linear-gradient(135deg, #8e2de2,#4a00e0); display: flex; align-items: center; justify-content: center; color: white; font-size: 24px;">
</div>
</div>
</div> |
Beta Was this translation helpful? Give feedback.
-
There is no need to use |
Beta Was this translation helpful? Give feedback.
I think the
Name
attribute is required for Input components.See https://www.fluentui-blazor.net/Forms