**Sometimes in-built eye icon is coming and diapering **
Please find the complete code here below
@page
@using Hon.IFS.Admin
@using Microsoft.AspNetCore.Mvc.Localization
@using Volo.Abp.Account.Localization
@using Volo.Abp.Account.Settings
@using Volo.Abp.Account.Web.Pages.Account
@using Volo.Abp.AspNetCore.Mvc.UI.Theme.LeptonX.Themes.LeptonX.Components.Common.PageAlerts
@using Volo.Abp.AspNetCore.Mvc.UI.Theming
@using Volo.Abp.Identity
@using Volo.Abp.Settings
@model Hon.IFS.Admin.Pages.Account.CustomLoginModel
@inject IHtmlLocalizer<AccountResource> L
@inject IThemeManager ThemeManager
@inject Volo.Abp.Settings.ISettingProvider SettingProvider
@inject AdminBrandingProvider adminBrandingProvider
@{
Layout = ThemeManager.CurrentTheme.GetAccountLayout();
}
~~~~
@section scripts
{
<abp-script-bundle name="@typeof(LoginModel).FullName">
<abp-script src="/Pages/Account/Login.js" />
</abp-script-bundle>
<script src="https://ajax.aspnetcdn.com/ajax/jquery/jquery-3.5.1.min.js"></script>
<script src="https://ajax.aspnetcdn.com/ajax/jquery.unobtrusive-ajax/3.2.6/jquery.unobtrusive-ajax.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
<script>
~~~~
document.addEventListener('DOMContentLoaded', () => {
document.querySelectorAll('.alert .btn-close').forEach(button => {
button.addEventListener('click', () => {
const alertBox = button.closest('.alert');
if (alertBox) {
alertBox.remove();
}
});
});
});
</script>
}
<style>
.card-padding {
padding: 0.5rem !important; /* Adjusted back to 0.5rem for consistency */
}
.full-height {
min-height: 100vh; /* Kept at 100vh for full height */
display: flex;
align-items: flex-start; /* Move content to the top */
padding-top: 50px; /* Adds space from the top to shift content down slightly */
}
.static-content {
padding-top: 40px;
padding-right: 20px;
}
.red-button {
background-color: red !important;
border-color: red !important;
color: white !important;
}
.form-check-input.small-switch {
width: 2em; /* Adjust width */
height: 1em; /* Adjust height */
}
<style >
/* Custom switch styles */
.form-check-input:checked {
background-color: red; /* Change the background color when checked */
border-color: red; /* Change the border color when checked */
}
.form-check-input:focus {
box-shadow: 0 0 0 0.2rem rgba(255, 0, 0, 0.25); /* Optional: Add a red focus ring */
}
.form-check-input {
background-color: #e9ecef; /* Change the background color when unchecked */
border-color: #ced4da; /* Change the border color when unchecked */
}
#passwordInput {
border-top-right-radius: 0.5rem !important;
border-bottom-right-radius: 0.5rem !important;
}
</style>
<div class="row" style="padding-top: 10px; padding-left: 20px">
<div class="col-12 col-md-6 static-content">
<h2 style="color: red">STAY</h2>
<h2 style="color: red"><strong>CONNECTED</strong></h2>
<h2 style="color: white">STAY</h2>
<h2 style="color: white"><strong>PROTECTED</strong></h2>
</div>
<div class="col-12 col-md-5 align-content-end pe-md-5">
<div style="display: flex; justify-content: center; align-items: center;">
<h3 style="color: white">@adminBrandingProvider.AppName</h3>
</div>
<div class="card shadow-sm rounded card-padding" style="max-height: 400px; max-width: 460px;">
<div class="card-body p-2">
<h3 class="text-center mb-3">@L["Login"]</h3>
<abp-alerts>
@await Component.InvokeAsync(typeof(PageAlertsViewComponent))
</abp-alerts>
<form method="post" class="mt-1">
<div class="mb-1">
<input asp-for="LoginInput.UserNameOrEmailAddress" class="form-control form-control-sm"
placeholder="User Name" />
<span asp-validation-for="LoginInput.UserNameOrEmailAddress" class="text-danger"></span>
</div>
<div class="mb-2" style="padding-top: 10px";>
<div class="input-group">
<input asp-for="LoginInput.Password" class="form-control form-control-sm"
autocomplete="new-password" maxlength="@IdentityUserConsts.MaxPasswordLength"
id="passwordInput" placeholder="Password" required-symbol="false" />
</div>
<div>
<span asp-validation-for="LoginInput.Password" style="cursor: pointer; class="text-danger"></span>
</div>
</div>
<div class="row">
<div class="col">
<div class="form-check form-switch mb-3">
<input type="checkbox" class="form-check-input" id="LoginInput_RememberMe" name="LoginInput.RememberMe" value="true" />
<label class="form-check-label" for="LoginInput_RememberMe">@Html.DisplayNameFor(m => m.LoginInput.RememberMe)</label>
</div>
</div>
<div class="col text-end">
<a href="@Url.Page("./ForgotPassword", new { returnUrl = Model.ReturnUrl, returnUrlHash = Model.ReturnUrlHash })">@L["ForgotPassword"]</a>
</div>
</div>
<div class="d-grid gap-1">
<abp-button type="submit" button-type="Primary" name="Action" value="Login" class="btn-sm mt-2 red-button">@L["Login"]</abp-button>
@if (Model.ShowCancelButton)
{
<abp-button type="submit" button-type="Secondary" formnovalidate="formnovalidate" name="Action" value="Cancel" class="btn-sm mt-2">@L["Cancel"]</abp-button>
}
</div>
</form>
@if (Model.VisibleExternalProviders.Any())
{
<div class="mt-1">
<h4>@L["UseAnotherServiceToLogIn"]</h4>
<form asp-page="./Login" asp-page-handler="ExternalLogin" asp-route-returnUrl="@Model.ReturnUrl" asp-route-returnUrlHash="@Model.ReturnUrlHash" method="post">
@foreach (var provider in Model.VisibleExternalProviders)
{
<button type="submit" class="btn btn-primary btn-sm m-1" name="provider" value="@provider.AuthenticationScheme" title="@L["GivenTenantIsNotAvailable", provider.DisplayName]">@provider.DisplayName</button>
}
</form>
</div>
}
@* @if (!Model.EnableLocalLogin && !Model.VisibleExternalProviders.Any())
{
<div class="alert alert-warning">
<strong>@L["InvalidLoginRequest"]</strong>
@L["ThereAreNoLoginSchemesConfiguredForThisClient"]
</div>
} *@
</div>
</div>
</div>
</div>
I am currently working with version 9.1 of the Angular frontend, and I have encountered some issues that persist in both the login and registration pages.
If you have any updated code or solutions to address these issues, I would greatly appreciate your assistance in providing that information.
Yes, this worked but in the custom login page Password Eye icon disappears all the time can you help me how to resolve it
@page
@using Hon.IFS.Admin
@{
Layout = ThemeManager.CurrentTheme.GetAccountLayout();
}
@section scripts
{
<abp-script-bundle name="@typeof(Hon.IFS.Admin.Pages.Account.CustomLoginModel).FullName">
<abp-script src="/Pages/Account/Login.js" />
</abp-script-bundle>
}
</style>
<div class="row" style="padding-top: 10px; padding-left: 20px">
<div class="col-12 col-md-6 static-content">
<h2 style="color: red">STAY</h2>
<h2 style="color: red"><strong>CONNECTED</strong></h2>
<h2 style="color: white">STAY</h2>
<h2 style="color: white"><strong>PROTECTED</strong></h2>
</div>
<div class="col-12 col-md-5 align-content-end pe-md-5">
<div style="display: flex; justify-content: center; align-items: center;">
<h3 style="color: white">@adminBrandingProvider.AppName</h3>
</div>
<div class="card shadow-sm rounded card-padding" style="max-height: 400px; max-width: 600px;">
<div class="card-body p-2">
<h3 class="text-center mb-3">@L["Login"]</h3>
@await Component.InvokeAsync(typeof(PageAlertsViewComponent))
<form method="post" class="mt-1">
<div class="mb-1">
<input asp-for="LoginInput.UserNameOrEmailAddress" class="form-control form-control-sm"
placeholder="User Name" />
<span asp-validation-for="LoginInput.UserNameOrEmailAddress" class="text-danger"></span>
</div>
<div class="mb-2" style="padding-top: 10px";>
<div class="input-group">
<input asp-for="LoginInput.Password" class="form-control form-control-sm"
autocomplete="new-password" maxlength="@IdentityUserConsts.MaxPasswordLength"
id="passwordInput" placeholder="Password" required-symbol="false" />
@* <span class="input-group-text" id="togglePassword" style="cursor: pointer;">
<i class="fa fa-eye" id="eyeIcon"></i>
</span> *@
<span asp-validation-for="LoginInput.Password" style="cursor: pointer; class="text-danger"></span>
</div>
</div>
<div class="row">
<div class="col">
<div class="form-check form-switch mb-3">
<input type="checkbox" class="form-check-input" id="LoginInput_RememberMe" name="LoginInput.RememberMe" value="true" />
<label class="form-check-label" for="LoginInput_RememberMe">@Html.DisplayNameFor(m => m.LoginInput.RememberMe)</label>
</div>
</div>
<div class="col text-end">
<a href="@Url.Page("./ForgotPassword", new { returnUrl = Model.ReturnUrl, returnUrlHash = Model.ReturnUrlHash })">@L["ForgotPassword"]</a>
</div>
</div>
<div class="d-grid gap-1">
<abp-button type="submit" button-type="Primary" name="Action" value="Login" class="btn-sm mt-2 red-button">@L["Login"]</abp-button>
@if (Model.ShowCancelButton)
{
<abp-button type="submit" button-type="Secondary" formnovalidate="formnovalidate" name="Action" value="Cancel" class="btn-sm mt-2">@L["Cancel"]</abp-button>
}
</div>
</form>
@if (Model.VisibleExternalProviders.Any())
{
<div class="mt-1">
<h4>@L["UseAnotherServiceToLogIn"]</h4>
<form asp-page="./Login" asp-page-handler="ExternalLogin" asp-route-returnUrl="@Model.ReturnUrl" asp-route-returnUrlHash="@Model.ReturnUrlHash" method="post">
@foreach (var provider in Model.VisibleExternalProviders)
{
<button type="submit" class="btn btn-primary btn-sm m-1" name="provider" value="@provider.AuthenticationScheme" title="@L["GivenTenantIsNotAvailable", provider.DisplayName]">@provider.DisplayName</button>
}
</form>
</div>
}
@* @if (!Model.EnableLocalLogin && !Model.VisibleExternalProviders.Any())
{
<div class="alert alert-warning">
<strong>@L["InvalidLoginRequest"]</strong>
@L["ThereAreNoLoginSchemesConfiguredForThisClient"]
</div>
} *@
</div>
</div>
</div>
</div>
namespace Hon.IFS.Admin.Pages.Account
{
[ExposeServices(typeof(CustomLoginModel), typeof(LoginModel))]
public class CustomLoginModel : LoginModel
{
private readonly ITenantRepository _tenantRepository;
public AdminBrandingProvider _adminBrandingProvider;
public DefaultBrandingProvider _defaultBrandingProvider;
public IIdentityUserRepository _identityUserRepository;
public IDirectoryDescriptorAppService _directoryDescriptorAppService;
public CoustomFileManager _coustomFileManager;
public CustomLoginModel(
IAuthenticationSchemeProvider schemeProvider,
IOptions<AbpAccountOptions> accountOptions,
IAccountExternalProviderAppService accountExternalProviderAppService,
ICurrentPrincipalAccessor currentPrincipalAccessor,
IAbpRecaptchaValidatorFactory recaptchaValidatorFactory,
IOptions<IdentityOptions> identityOptions,
IOptionsSnapshot<reCAPTCHAOptions> reCaptchaOptions,
ITenantRepository tenantRepository,
AdminBrandingProvider adminBrandingProvider,
DefaultBrandingProvider defaultBrandingProvider,
IIdentityUserRepository identityUserRepository,
IDirectoryDescriptorAppService directoryDescriptorAppService,
CoustomFileManager coustomFileManager)
: base(schemeProvider,
accountOptions,
recaptchaValidatorFactory,
accountExternalProviderAppService,
currentPrincipalAccessor,
identityOptions,
reCaptchaOptions)
{
_tenantRepository = tenantRepository;
_adminBrandingProvider = adminBrandingProvider;
_defaultBrandingProvider = defaultBrandingProvider;
_identityUserRepository = identityUserRepository;
_directoryDescriptorAppService = directoryDescriptorAppService;
_coustomFileManager = coustomFileManager;
}
public override async Task<IActionResult> OnGetAsync()
{
// Decode the entire query string to handle encoded parameters
var decodedQueryString = Uri.UnescapeDataString(Request.QueryString.ToString());
// Log the decoded query string for debugging purposes
System.Diagnostics.Debug.WriteLine(decodedQueryString);
// Parse the decoded query string manually to extract parameters
var queryParams = Microsoft.AspNetCore.WebUtilities.QueryHelpers.ParseQuery(decodedQueryString);
if (queryParams.TryGetValue("tenantKey", out var resolvedTenant))
{
// Convert resolvedTenant to a string
string tenantKey = resolvedTenant.ToString(); // Safely convert to string
// Replace "undefined" with an empty string
if (tenantKey.Equals("undefined"))
{
tenantKey = ""; // Set tenantKey to empty string
}
// Check if tenantKey is null or an empty string
if (string.IsNullOrEmpty(tenantKey))
{
// Set the app name to the default value
_adminBrandingProvider.SetAppName("IFS ADMIN"); // Defaults to "IFS ADMIN"
}
else
{
// If tenantKey is valid, set the app name to the resolved tenant
_adminBrandingProvider.SetAppName(tenantKey);
}
}
return await base.OnGetAsync();
}
public override async Task<IActionResult> OnPostAsync(string action)
{
// Decode the entire query string to handle encoded parameters
var decodedQueryString = Uri.UnescapeDataString(Request.QueryString.ToString());
// Extract the username and password from the posted form data
var username = Request.Form["LoginInput.UserNameOrEmailAddress"].ToString();
var password = Request.Form["LoginInput.Password"].ToString();
// Log the decoded query string for debugging purposes
System.Diagnostics.Debug.WriteLine(decodedQueryString);
// Parse the decoded query string manually to extract parameters
var queryParams = Microsoft.AspNetCore.WebUtilities.QueryHelpers.ParseQuery(decodedQueryString);
// Extract the tenantKey from the query parameters
if (!queryParams.TryGetValue("tenantKey", out var tenantKey))
{
//return BadRequest("Tenant key is not provided in the query parameters.");
}
// var resolvedUser = await _identityUserRepository.FindAsync()
var resolvedTenant = await _tenantRepository.FindByNameAsync(tenantKey);
// Step 3: Switch tenant and call base login logic with error handling
try
{
var user = await FindUserAsync(LoginInput.UserNameOrEmailAddress);
using (CurrentTenant.Change(user?.TenantId))
{
System.Diagnostics.Debug.WriteLine(CurrentTenant?.Name);
// Check ModelState before proceeding (e.g., from data annotations)
if (!ModelState.IsValid)
{
var errors = ModelState.Values.SelectMany(v => v.Errors).Select(e => e.ErrorMessage);
System.Diagnostics.Debug.WriteLine("Pre-Login Validation Errors: " + string.Join(", ", errors));
return Page();
}
// Check if base method added errors to ModelState (e.g., invalid credentials)
if (!ModelState.IsValid)
{
var errors = ModelState.Values.SelectMany(v => v.Errors).Select(e => e.ErrorMessage);
System.Diagnostics.Debug.WriteLine("Post-Login Errors: " + string.Join(", ", errors));
// Add custom error message if no specific model errors regarding the username/password
ModelState.AddModelError("", "Invalid username or password.");
return Page();
}
var result = await base.OnPostAsync(action);
if (!ModelState.IsValid)
{
return result;
}
var tenantId = user.TenantId.GetValueOrDefault(Guid.Empty);
// **Check if directories already exist**
var existingDirectories = await _coustomFileManager.GetFilesByTenantIdAsync(tenantId);
var existingNames = existingDirectories.Select(d => d.Name).ToList();
var defaultFolders = new List<CreateDirectoryInput>
{
new CreateDirectoryInput { Name = "Documents" },
new CreateDirectoryInput { Name = "Images" },
new CreateDirectoryInput { Name = "Backups" }
};
foreach (var folder in defaultFolders)
{
if (!existingNames.Contains(folder.Name)) // **Insert only if not present**
{
await _directoryDescriptorAppService.CreateAsync(folder);
}
}
return result;
}
}
catch (Exception ex)
{
// Handle exceptions from base login or tenant context switching
System.Diagnostics.Debug.WriteLine($"Login Processing Error: {ex.Message}");
ModelState.AddModelError("", "An unexpected error occurred during login. Please try again.");
// Alerts.Warning(L["Message"]);
return Page();
}
//using (CurrentTenant.Change(resolvedTenant?.Id, resolvedTenant?.Name))
//{
// System.Diagnostics.Debug.WriteLine(CurrentTenant?.Name);
// // Call base method and handle its result
// var result = await base.OnPostAsync(action);
// // Check if base method added errors to ModelState
// if (!ModelState.IsValid)
// {
// var errors = ModelState.Values.SelectMany(v => v.Errors).Select(e => e.ErrorMessage);
// System.Diagnostics.Debug.WriteLine("Post-Login Errors: " + string.Join(", ", errors));
// return Page();
// }
// return result;
//}
}
protected virtual async Task<IdentityUser> FindUserAsync(string uniqueUserNameOrEmailAddress)
{
IdentityUser user = null;
using (CurrentTenant.Change(null))
{
user = await UserManager.FindByNameAsync(LoginInput.UserNameOrEmailAddress) ??
await UserManager.FindByEmailAsync(LoginInput.UserNameOrEmailAddress);
if (user != null)
{
return user;
}
}
foreach (var tenant in await _tenantRepository.GetListAsync())
{
using (CurrentTenant.Change(tenant.Id))
{
user = await UserManager.FindByNameAsync(LoginInput.UserNameOrEmailAddress) ??
await UserManager.FindByEmailAsync(LoginInput.UserNameOrEmailAddress);
if (user != null)
{
return user;
}
}
}
return null;
}
}
}
I getting $ undefined error for login.js file Login.js?_v=638730725080000000:1 Uncaught ReferenceError: $ is not defined at Login.js?_v=638730725080000000:1:1
Message=
Source=
StackTrace:
at https://localhost:44330/Pages/Account/Login.js?_v=638730725080000000:1:1
@await Component.InvokeAsync(typeof(PageAlertsViewComponent))
it resolved after adding this line in index.chtml
I want to customize this login page when I am typing incorrect password, I am not getting any error like "Invalid username or password" Alert and it not catching any exception as well. I am following this documentation https://abp.io/docs/2.8/How-To/Customize-Login-Page-MVC Below I am providing my complete code login.chtml @page @using Hon.IFS.Admin @using Microsoft.AspNetCore.Mvc.Localization @using Volo.Abp.Account.Localization @using Volo.Abp.Account.Settings @using Volo.Abp.Account.Web.Pages.Account @using Volo.Abp.AspNetCore.Mvc.UI.Theming @using Volo.Abp.Identity @using Volo.Abp.Settings @model Hon.IFS.Admin.Pages.Account.CustomLoginModel @inject IHtmlLocalizer<AccountResource> L @inject IThemeManager ThemeManager @inject Volo.Abp.Settings.ISettingProvider SettingProvider @inject AdminBrandingProvider adminBrandingProvider
@{ Layout = ThemeManager.CurrentTheme.GetAccountLayout(); }
@section scripts { <abp-script-bundle name="@typeof(Hon.IFS.Admin.Pages.Account.CustomLoginModel).FullName"> <abp-script src="/Pages/Account/Login.js" /> </abp-script-bundle> <script src="https://ajax.aspnetcdn.com/ajax/jquery/jquery-3.5.1.min.js"></script> <script src="https://ajax.aspnetcdn.com/ajax/jquery.unobtrusive-ajax/3.2.6/jquery.unobtrusive-ajax.min.js"></script> }
<style> .card-padding { padding: 0.5rem !important; /* Adjusted back to 0.5rem for consistency */ }
.full-height {
min-height: 100vh; /* Kept at 100vh for full height */
display: flex;
align-items: flex-start; /* Move content to the top */
padding-top: 50px; /* Adds space from the top to shift content down slightly */
}
.static-content {
padding-top: 40px;
padding-right: 20px;
}
.red-button {
background-color: red !important;
border-color: red !important;
color: white !important;
}
.form-check-input.small-switch {
width: 2em; /* Adjust width */
height: 1em; /* Adjust height */
}
</style>
<div class="row" style="padding-top: 20px; padding-left: 20px"> <div class="col-12 col-md-6 static-content"> <h2 style="color: red">STAY</h2> <h2 style="color: red">CONNECTED</h2> <h2 style="color: white">STAY</h2> <h2 style="color: white">PROTECTED</h2> </div>
<div class="col-12 col-md-5 align-content-end pe-md-5">
<div style="display: flex; justify-content: center; align-items: center;">
<h3 style="color: white">@adminBrandingProvider.AppName</h3>
</div>
<div class="card shadow-sm rounded card-padding" style="max-height: 400px; max-width: 600px;">
<div class="card-body p-3">
<h3 class="text-center mb-3">@L["Login"]</h3>
<form method="post" class="mt-1">
<div class="mb-1">
<label asp-for="LoginInput.UserNameOrEmailAddress" class="form-label"></label>
<input asp-for="LoginInput.UserNameOrEmailAddress" class="form-control form-control-sm" />
<span asp-validation-for="LoginInput.UserNameOrEmailAddress" class="text-danger"></span>
</div>
<div class="mb-2">
<label asp-for="LoginInput.Password" class="form-label"></label>
<div class="input-group">
<input type="password" class="form-control form-control-sm" autocomplete="new-password"
maxlength="@IdentityUserConsts.MaxPasswordLength" asp-for="LoginInput.Password" />
</div>
<span asp-validation-for="LoginInput.Password" class="text-danger"></span>
</div>
<div class="row">
<div class="col">
<div class="form-check form-switch mb-3">
<input type="checkbox" class="form-check-input" id="LoginInput_RememberMe" name="LoginInput.RememberMe" value="true" />
<label class="form-check-label " for="LoginInput_RememberMe">Remember Me</label>
</div>
</div>
<div class="col text-end">
<a href="@Url.Page("./ForgotPassword", new { returnUrl = Model.ReturnUrl, returnUrlHash = Model.ReturnUrlHash })">@L["ForgotPassword"]</a>
</div>
</div>
<div class="d-grid gap-1">
<abp-button type="submit" button-type="Primary" name="Action" value="Login" class="btn-sm mt-2 red-button">@L["Login"]</abp-button>
@if (Model.ShowCancelButton)
{
<abp-button type="submit" button-type="Secondary" formnovalidate="formnovalidate" name="Action" value="Cancel" class="btn-sm mt-2">@L["Cancel"]</abp-button>
}
</div>
</form>
@if (Model.VisibleExternalProviders.Any())
{
<div class="mt-1">
<h4>@L["UseAnotherServiceToLogIn"]</h4>
<form asp-page="./Login" asp-page-handler="ExternalLogin" asp-route-returnUrl="@Model.ReturnUrl" asp-route-returnUrlHash="@Model.ReturnUrlHash" method="post">
@foreach (var provider in Model.VisibleExternalProviders)
{
<button type="submit" class="btn btn-primary btn-sm m-1" name="provider" value="@provider.AuthenticationScheme" title="@L["GivenTenantIsNotAvailable", provider.DisplayName]">@provider.DisplayName</button>
}
</form>
</div>
}
@* @if (!Model.EnableLocalLogin && !Model.VisibleExternalProviders.Any())
{
<div class="alert alert-warning">
<strong>@L["InvalidLoginRequest"]</strong>
@L["ThereAreNoLoginSchemesConfiguredForThisClient"]
</div>
} *@
</div>
</div>
</div>
</div>
login.chtml.cs using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Extensions; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages; using Microsoft.Extensions.Options; using Owl.reCAPTCHA; using System; using System.Linq; using System.Threading.Tasks; using Volo.Abp.Account.ExternalProviders; using Volo.Abp.Account.Public.Web; using Volo.Abp.Account.Public.Web.Pages.Account; using Volo.Abp.Account.Security.Recaptcha; using Volo.Abp.AspNetCore.MultiTenancy; using Volo.Abp.DependencyInjection; using Volo.Abp.Identity; using Volo.Abp.MultiTenancy; using Volo.Abp.Security.Claims; using Volo.Abp.Ui.Branding; using Volo.Saas.Tenants;
namespace Hon.IFS.Admin.Pages.Account { [ExposeServices(typeof(CustomLoginModel), typeof(LoginModel))] public class CustomLoginModel : LoginModel { private readonly ITenantRepository _tenantRepository; public AdminBrandingProvider _adminBrandingProvider; public DefaultBrandingProvider _defaultBrandingProvider; public IIdentityUserRepository _identityUserRepository; public CustomLoginModel( IAuthenticationSchemeProvider schemeProvider, IOptions<AbpAccountOptions> accountOptions, IAccountExternalProviderAppService accountExternalProviderAppService, ICurrentPrincipalAccessor currentPrincipalAccessor, IAbpRecaptchaValidatorFactory recaptchaValidatorFactory, IOptions<IdentityOptions> identityOptions, IOptionsSnapshot<reCAPTCHAOptions> reCaptchaOptions, ITenantRepository tenantRepository, AdminBrandingProvider adminBrandingProvider, DefaultBrandingProvider defaultBrandingProvider, IIdentityUserRepository identityUserRepository) : base(schemeProvider, accountOptions, recaptchaValidatorFactory, accountExternalProviderAppService, currentPrincipalAccessor, identityOptions, reCaptchaOptions) { _tenantRepository = tenantRepository; _adminBrandingProvider = adminBrandingProvider; _defaultBrandingProvider = defaultBrandingProvider; _identityUserRepository = identityUserRepository; } public override async Task<IActionResult> OnGetAsync() { // Decode the entire query string to handle encoded parameters var decodedQueryString = Uri.UnescapeDataString(Request.QueryString.ToString());
// Log the decoded query string for debugging purposes
System.Diagnostics.Debug.WriteLine(decodedQueryString);
// Parse the decoded query string manually to extract parameters
var queryParams = Microsoft.AspNetCore.WebUtilities.QueryHelpers.ParseQuery(decodedQueryString);
if (queryParams.TryGetValue("tenantKey", out var resolvedTenant))
{
_adminBrandingProvider.SetAppName(resolvedTenant.ToString());
ViewData["sathavva"] = resolvedTenant;
}
return await base.OnGetAsync();
}
public override async Task<IActionResult> OnPostAsync(string action)
{
// Decode the entire query string to handle encoded parameters
var decodedQueryString = Uri.UnescapeDataString(Request.QueryString.ToString());
// Extract the username and password from the posted form data
var username = Request.Form["LoginInput.UserNameOrEmailAddress"].ToString();
var password = Request.Form["LoginInput.Password"].ToString();
// Log the decoded query string for debugging purposes
System.Diagnostics.Debug.WriteLine(decodedQueryString);
// Parse the decoded query string manually to extract parameters
var queryParams = Microsoft.AspNetCore.WebUtilities.QueryHelpers.ParseQuery(decodedQueryString);
// Extract the tenantKey from the query parameters
if (!queryParams.TryGetValue("tenantKey", out var tenantKey))
{
//return BadRequest("Tenant key is not provided in the query parameters.");
}
// var resolvedUser = await _identityUserRepository.FindAsync()
var resolvedTenant = await _tenantRepository.FindByNameAsync(tenantKey);
// var tenantIdOrName = resolvedTenantId.TenantIdOrName;
if (resolvedTenant == null)
{
// Handle the case where the tenant could not be resolved (e.g., return an error view or redirect).
//return NotFound("Tenant could not be resolved.");
}
// Step 3: Switch tenant and call base login logic with error handling
try
{
using (CurrentTenant.Change(resolvedTenant?.Id, resolvedTenant?.Name ?? "Default"))
{
System.Diagnostics.Debug.WriteLine(CurrentTenant?.Name);
// Check ModelState before proceeding (e.g., from data annotations)
if (!ModelState.IsValid)
{
var errors = ModelState.Values.SelectMany(v => v.Errors).Select(e => e.ErrorMessage);
System.Diagnostics.Debug.WriteLine("Pre-Login Validation Errors: " + string.Join(", ", errors));
return Page();
}
// Call base method with try-catch for additional safety
var result = await base.OnPostAsync(action);
// Check if base method added errors to ModelState (e.g., invalid credentials)
if (!ModelState.IsValid)
{
var errors = ModelState.Values.SelectMany(v => v.Errors).Select(e => e.ErrorMessage);
System.Diagnostics.Debug.WriteLine("Post-Login Errors: " + string.Join(", ", errors));
// Add custom error message if no specific model errors regarding the username/password
ModelState.AddModelError("", "Invalid username or password.");
return Page();
}
return result; // Success: redirect or other action result
}
}
catch (Exception ex)
{
// Handle exceptions from base login or tenant context switching
System.Diagnostics.Debug.WriteLine($"Login Processing Error: {ex.Message}");
ModelState.AddModelError("", "An unexpected error occurred during login. Please try again.");
Alerts.Warning(L["Message"]);
return Page();
}
//using (CurrentTenant.Change(resolvedTenant?.Id, resolvedTenant?.Name))
//{
// System.Diagnostics.Debug.WriteLine(CurrentTenant?.Name);
// // Call base method and handle its result
// var result = await base.OnPostAsync(action);
// // Check if base method added errors to ModelState
// if (!ModelState.IsValid)
// {
// var errors = ModelState.Values.SelectMany(v => v.Errors).Select(e => e.ErrorMessage);
// System.Diagnostics.Debug.WriteLine("Post-Login Errors: " + string.Join(", ", errors));
// return Page();
// }
// return result;
//}
}
}
}
public override async Task<IActionResult> OnPostAsync(string action)
{
// Decode the entire query string to handle encoded parameters
var decodedQueryString = Uri.UnescapeDataString(Request.QueryString.ToString());
// Extract the username and password from the posted form data
var username = Request.Form["LoginInput.UserNameOrEmailAddress"].ToString();
var password = Request.Form["LoginInput.Password"].ToString();
// Log the decoded query string for debugging purposes
System.Diagnostics.Debug.WriteLine(decodedQueryString);
// Parse the decoded query string manually to extract parameters
var queryParams = Microsoft.AspNetCore.WebUtilities.QueryHelpers.ParseQuery(decodedQueryString);
// Extract the tenantKey from the query parameters
if (!queryParams.TryGetValue("tenantKey", out var tenantKey))
{
//return BadRequest("Tenant key is not provided in the query parameters.");
}
// var resolvedUser = await _identityUserRepository.FindAsync()
var resolvedTenant = await _tenantRepository.FindByNameAsync(tenantKey);
// var tenantIdOrName = resolvedTenantId.TenantIdOrName;
if (resolvedTenant == null)
{
// Handle the case where the tenant could not be resolved (e.g., return an error view or redirect).
//return NotFound("Tenant could not be resolved.");
}
// Step 3: Switch tenant and call base login logic with error handling
try
{
using (CurrentTenant.Change(resolvedTenant?.Id, resolvedTenant?.Name ?? "Default"))
{
System.Diagnostics.Debug.WriteLine(CurrentTenant?.Name);
// Check ModelState before proceeding (e.g., from data annotations)
if (!ModelState.IsValid)
{
var errors = ModelState.Values.SelectMany(v => v.Errors).Select(e => e.ErrorMessage);
System.Diagnostics.Debug.WriteLine("Pre-Login Validation Errors: " + string.Join(", ", errors));
return Page();
}
// Call base method with try-catch for additional safety
var result = await base.OnPostAsync(action);
// Check if base method added errors to ModelState (e.g., invalid credentials)
if (!ModelState.IsValid)
{
var errors = ModelState.Values.SelectMany(v => v.Errors).Select(e => e.ErrorMessage);
System.Diagnostics.Debug.WriteLine("Post-Login Errors: " + string.Join(", ", errors));
return Page();
}
return result; // Success: redirect or other action result
}
}
catch (Exception ex)
{
// Handle exceptions from base login or tenant context switching
System.Diagnostics.Debug.WriteLine($"Login Processing Error: {ex.Message}");
ModelState.AddModelError("", "An unexpected error occurred during login. Please try again.");
return Page();
}
//using (CurrentTenant.Change(resolvedTenant?.Id, resolvedTenant?.Name))
//{
// System.Diagnostics.Debug.WriteLine(CurrentTenant?.Name);
// // Call base method and handle its result
// var result = await base.OnPostAsync(action);
// // Check if base method added errors to ModelState
// if (!ModelState.IsValid)
// {
// var errors = ModelState.Values.SelectMany(v => v.Errors).Select(e => e.ErrorMessage);
// System.Diagnostics.Debug.WriteLine("Post-Login Errors: " + string.Join(", ", errors));
// return Page();
// }
// return result;
//}
}
}
}
Here in the OnPost method. I am not getting Any Exception when I am logging with incorrect password or username
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Extensions;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Options;
using Owl.reCAPTCHA;
using System;
using System.Linq;
using System.Threading.Tasks;
using Volo.Abp.Account.ExternalProviders;
using Volo.Abp.Account.Public.Web;
using Volo.Abp.Account.Public.Web.Pages.Account;
using Volo.Abp.Account.Security.Recaptcha;
using Volo.Abp.AspNetCore.MultiTenancy;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Identity;
using Volo.Abp.MultiTenancy;
using Volo.Abp.Security.Claims;
using Volo.Abp.Ui.Branding;
using Volo.Saas.Tenants;
namespace Hon.IFS.Admin.Pages.Account
{
[ExposeServices(typeof(CustomLoginModel), typeof(LoginModel))]
public class CustomLoginModel : LoginModel
{
private readonly ITenantRepository _tenantRepository;
public AdminBrandingProvider _adminBrandingProvider;
public DefaultBrandingProvider _defaultBrandingProvider;
public IIdentityUserRepository _identityUserRepository;
public CustomLoginModel(
IAuthenticationSchemeProvider schemeProvider,
IOptions<AbpAccountOptions> accountOptions,
IAccountExternalProviderAppService accountExternalProviderAppService,
ICurrentPrincipalAccessor currentPrincipalAccessor,
IAbpRecaptchaValidatorFactory recaptchaValidatorFactory,
IOptions<IdentityOptions> identityOptions,
IOptionsSnapshot<reCAPTCHAOptions> reCaptchaOptions,
ITenantRepository tenantRepository,
AdminBrandingProvider adminBrandingProvider,
DefaultBrandingProvider defaultBrandingProvider,
IIdentityUserRepository identityUserRepository)
: base(schemeProvider,
accountOptions,
recaptchaValidatorFactory,
accountExternalProviderAppService,
currentPrincipalAccessor,
identityOptions,
reCaptchaOptions)
{
_tenantRepository = tenantRepository;
_adminBrandingProvider = adminBrandingProvider;
_defaultBrandingProvider = defaultBrandingProvider;
_identityUserRepository = identityUserRepository;
}
public override async Task<IActionResult> OnGetAsync()
{
// Decode the entire query string to handle encoded parameters
var decodedQueryString = Uri.UnescapeDataString(Request.QueryString.ToString());
// Log the decoded query string for debugging purposes
System.Diagnostics.Debug.WriteLine(decodedQueryString);
// Parse the decoded query string manually to extract parameters
var queryParams = Microsoft.AspNetCore.WebUtilities.QueryHelpers.ParseQuery(decodedQueryString);
if (queryParams.TryGetValue("tenantKey", out var resolvedTenant))
{
_adminBrandingProvider.SetAppName(resolvedTenant.ToString());
ViewData["sathavva"] = resolvedTenant;
}
return await base.OnGetAsync();
}
public override async Task<IActionResult> OnPostAsync(string action)
{
// Decode the entire query string to handle encoded parameters
var decodedQueryString = Uri.UnescapeDataString(Request.QueryString.ToString());
// Extract the username and password from the posted form data
var username = Request.Form["LoginInput.UserNameOrEmailAddress"].ToString();
var password = Request.Form["LoginInput.Password"].ToString();
// Log the decoded query string for debugging purposes
System.Diagnostics.Debug.WriteLine(decodedQueryString);
// Parse the decoded query string manually to extract parameters
var queryParams = Microsoft.AspNetCore.WebUtilities.QueryHelpers.ParseQuery(decodedQueryString);
// Extract the tenantKey from the query parameters
if (!queryParams.TryGetValue("tenantKey", out var tenantKey))
{
//return BadRequest("Tenant key is not provided in the query parameters.");
}
// var resolvedUser = await _identityUserRepository.FindAsync()
var resolvedTenant = await _tenantRepository.FindByNameAsync(tenantKey);
// var tenantIdOrName = resolvedTenantId.TenantIdOrName;
if (resolvedTenant == null)
{
// Handle the case where the tenant could not be resolved (e.g., return an error view or redirect).
//return NotFound("Tenant could not be resolved.");
}
// Step 3: Switch tenant and call base login logic with error handling
try
{
using (CurrentTenant.Change(resolvedTenant?.Id, resolvedTenant?.Name ?? "Default"))
{
System.Diagnostics.Debug.WriteLine(CurrentTenant?.Name);
// Check ModelState before proceeding (e.g., from data annotations)
if (!ModelState.IsValid)
{
var errors = ModelState.Values.SelectMany(v => v.Errors).Select(e => e.ErrorMessage);
System.Diagnostics.Debug.WriteLine("Pre-Login Validation Errors: " + string.Join(", ", errors));
return Page();
}
// Call base method with try-catch for additional safety
var result = await base.OnPostAsync(action);
// Check if base method added errors to ModelState (e.g., invalid credentials)
if (!ModelState.IsValid)
{
var errors = ModelState.Values.SelectMany(v => v.Errors).Select(e => e.ErrorMessage);
System.Diagnostics.Debug.WriteLine("Post-Login Errors: " + string.Join(", ", errors));
return Page();
}
return result; // Success: redirect or other action result
}
}
catch (Exception ex)
{
// Handle exceptions from base login or tenant context switching
System.Diagnostics.Debug.WriteLine($"Login Processing Error: {ex.Message}");
ModelState.AddModelError("", "An unexpected error occurred during login. Please try again.");
return Page();
}
//using (CurrentTenant.Change(resolvedTenant?.Id, resolvedTenant?.Name))
//{
// System.Diagnostics.Debug.WriteLine(CurrentTenant?.Name);
// // Call base method and handle its result
// var result = await base.OnPostAsync(action);
// // Check if base method added errors to ModelState
// if (!ModelState.IsValid)
// {
// var errors = ModelState.Values.SelectMany(v => v.Errors).Select(e => e.ErrorMessage);
// System.Diagnostics.Debug.WriteLine("Post-Login Errors: " + string.Join(", ", errors));
// return Page();
// }
// return result;
//}
}
}
}