Open Closed

Override Login Page - Tiered application solution #9189


User avatar
0
Spospisil created

I'm trying to override the login page in my tired solution following this example (https://abp.io/docs/latest/framework/ui/mvc-razor-pages/customization-user-interface) and I am getting the following error in the browser.

image.png

Login.cshtml
`@page
@using Microsoft.AspNetCore.Mvc.Localization
@using Volo.Abp.AspNetCore.Mvc.UI.Theming
@using Microsoft.Extensions.Options
@using Owl.reCAPTCHA
@using Volo.Abp.Account.Localization
@using Volo.Abp.Account.Public.Web.Pages.Account;
@using Volo.Abp.Account.Public.Web.Security.Recaptcha
@using Volo.Abp.Account.Settings
@using Volo.Abp.Identity;
@using Volo.Abp.Settings

@model CFData.Strucutre.Account.CustomLoginModel
@inject IThemeManager ThemeManager
@inject IHtmlLocalizer L
@inject Volo.Abp.AspNetCore.Mvc.UI.Layout.IPageLayout PageLayout
@inject ISettingProvider SettingProvider

@{
PageLayout.Content.Title = L["Login"].Value;
Layout = ThemeManager.CurrentTheme.GetAccountLayout();
var reCaptchaVersion = await SettingProvider.GetAsync(AccountSettingNames.Captcha.Version);
if (Model.UseCaptcha)
{
await Model.ReCaptchaOptions.SetAsync(reCaptchaVersion == 3 ? reCAPTCHAConsts.V3 : reCAPTCHAConsts.V2);
}

}

@section scripts
{



@if (Model.UseCaptcha)
{
    if (reCaptchaVersion == 3)
    {
        <recaptcha-script-v3 />
        <recaptcha-script-v3-js action="login" execute="false" />
    }
    else
    {
        <recaptcha-script-v2 />
    }
}

}

@if (Model.IsLinkLogin)
{

@L["LinkAccountWarning", Url.PageLink()]

}

@if (Model.BackToExternalLogins)
{

<a class="mb-3 btn btn-primary btn-block" href="@Url.Page("./ExternalLogins")">@L["Back"]

}

@if (Model.IsSelfRegistrationEnabled)
{
    <h5 class="mb-2">@L["NotAMemberYet"] <a class="text-decoration-none" href="@Url.Page("./Register", new {returnUrl = Model.ReturnUrl, returnUrlHash = Model.ReturnUrlHash})">@L["Register"]</a></h5>
}

@if (Model.EnableLocalLogin)
{
    <form method="post" id="loginForm">
        @if (Model.UseCaptcha)
        {
            <input class="mb-3" data-captcha="true" type="hidden" name="@RecaptchaValidatorBase.RecaptchaResponseKey" id="@RecaptchaValidatorBase.RecaptchaResponseKey"/>
        }
        <div>
            <div class="form-floating mb-2">
                <input asp-for="LoginInput.UserNameOrEmailAddress" type="text" class="form-control" placeholder="name@example.com">
                @Html.LabelFor(m => m.LoginInput.UserNameOrEmailAddress)
                <span asp-validation-for="LoginInput.UserNameOrEmailAddress"/>
            </div>

            <div class="form-floating mb-2">
                <input asp-for="LoginInput.Password" id="password-input" type="password" class="form-control" placeholder="Password">
                @Html.LabelFor(m => m.LoginInput.Password)
                <i id="PasswordVisibilityButton" class="bi bi-eye-slash show-pass-icon" data-bs-toggle="tooltip" data-bs-placement="top" data-bs-html="true" aria-label="@L["ShowPassword"]" data-bs-original-title="@L["ShowPassword"]"></i>
                <i id="capslockicon" class="bi bi-capslock caps-lock-icon" style="display: none;" data-bs-toggle="tooltip" data-bs-placement="top" data-bs-html="true" aria-label="<i class='bi bi-exclamation-circle'></i> @L["CapsLockOn"]!" data-bs-original-title="<i class='bi bi-exclamation-circle'></i> @L["CapsLockOn"]!"></i>
                <span asp-validation-for="LoginInput.Password"/>
            </div>
        </div>
        <abp-row>
            <abp-column>
                <div class="form-switch ps-2">
                    <abp-input asp-for="LoginInput.RememberMe" class="mb-4"/>
                </div>
            </abp-column>
            <abp-column class="text-end">
                <a href="@Url.Page("./ForgotPassword", new {returnUrl = Model.ReturnUrl, returnUrlHash = Model.ReturnUrlHash})">@L["ForgotPassword"]</a>
            </abp-column>
        </abp-row>

        @if (reCaptchaVersion == 2)
        {
            <script>
                recaptchaCallback = function (token) {
                    $('form button[type=submit]').removeAttr("disabled");
                    $('#@RecaptchaValidatorBase.RecaptchaResponseKey').val(token)
                };
            </script>
            <div class="mb-3">
                <recaptcha-div-v2 callback="recaptchaCallback"/>
            </div>
        }

        <div class="d-grid gap-2">
            <abp-button button-type="Primary" type="submit" class="mb-3" name="Action" value="Login" disabled="true">
                <i class="bi bi-box-arrow-in-right me-1"></i>
                @L["Login"]
            </abp-button>
        </div>

        @if (Model.ShowCancelButton)
        {
            <div class="d-grid gap-2">
                <abp-button button-type="Secondary" type="submit" formnovalidate="formnovalidate" class="mb-3" name="Action" value="Cancel">@L["Cancel"]</abp-button>
            </div>
        }
    </form>
}

@if (Model.VisibleExternalProviders.Any())
{
    if(Model.EnableLocalLogin)
    {
        <hr/>
        @L["OrSignInWith"]
        <br/>
    }
    else
    {
        @L["SignInWithOneOfTheFollowingProviders"]
    }

    <form asp-page="./Login" asp-page-handler="ExternalLogin"
          asp-route-returnUrl="@Model.ReturnUrl"
          asp-route-returnUrlHash="@Model.ReturnUrlHash"
          asp-route-linkTenantId="@Model.LinkTenantId"
          asp-route-linkUserId="@Model.LinkUserId"
          asp-route-linkToken="@Model.LinkToken"
          method="post">
        @foreach (var provider in Model.VisibleExternalProviders)
        {
            <button type="submit"
                    class="mt-2 me-2 btn btn-outline-primary btn-sm"
                    name="provider"
                    value="@provider.AuthenticationScheme"
                    data-busy-text="@L["ProcessingWithThreeDot"]">
                @if (provider.Icon != null)
                {
                    <i class="@provider.Icon"></i>
                }
                <span>@provider.DisplayName</span>
            </button>
        }
    </form>
}

`
Login.cs
`using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Options;
using Owl.reCAPTCHA;
using Volo.Abp.Account.Security.Recaptcha;
using Volo.Abp.Account.ExternalProviders;
using Volo.Abp.Security.Claims;
using Volo.Abp.Account.Public.Web.Pages.Account;
using Volo.Abp.Account.Public.Web;

namespace CFData.Strucutre.Account;

public class CustomLoginModel : LoginModel
{

public CustomLoginModel(
    IAuthenticationSchemeProvider schemeProvider,
    IOptions<AbpAccountOptions> accountOptions,
    IAbpRecaptchaValidatorFactory recaptchaValidatorFactory,
    IAccountExternalProviderAppService accountExternalProviderAppService,
    ICurrentPrincipalAccessor currentPrincipalAccessor,
    IOptions<IdentityOptions> identityOptions,
    IOptionsSnapshot<reCAPTCHAOptions> reCaptchaOptions) : base (
        schemeProvider,
        accountOptions, 
        recaptchaValidatorFactory, 
        accountExternalProviderAppService,
        currentPrincipalAccessor,
        identityOptions, 
        reCaptchaOptions)
{
}

}

Login.js
`$(function () {

var isRecaptchaEnabled = typeof grecaptcha !== 'undefined';
if (isRecaptchaEnabled) {
    grecaptcha.ready(function () {
        $("#loginForm button[type=submit]").removeAttr("disabled");
    });
} else {
    $("#loginForm button[type=submit]").removeAttr("disabled");
}

$("#loginForm button[type=submit]").click(function (e) {
    e.preventDefault();
    var form = $("#loginForm");
    if (form.valid() && isRecaptchaEnabled && abp.utils.isFunction(grecaptcha.reExecute)) {
        grecaptcha.reExecute(function (token) {
            form.find("input[type=hidden][data-captcha=true]").val(token);
            form.submit();
        })
    } else {
        form.submit();
    }
});

$("#PasswordVisibilityButton").click(function (e) {
    let button = $(this);
    let passwordInput = $('#password-input');
    if (!passwordInput) {
        return;
    }

    if (passwordInput.attr("type") === "password") {
        passwordInput.attr("type", "text");
    }
    else {
        passwordInput.attr("type", "password");
    }

    let icon = $("#PasswordVisibilityButton");
    if (icon) {
        icon.toggleClass("bi-eye-slash").toggleClass("bi-eye");
    }
});

// CAPS LOCK CONTROL
const password = document.getElementById('password-input');
const passwordMsg = document.getElementById('capslockicon');
if (password && passwordMsg) {
    password.addEventListener('keyup', e => {
        if (typeof e.getModifierState === 'function') {
            passwordMsg.style = e.getModifierState('CapsLock') ? 'display: inline' : 'display: none';
        }
    });
}

});


14 Answer(s)
  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    hi

    Can you try to remove Layout = ThemeManager.CurrentTheme.GetAccountLayout();?

    Thanks.

  • User Avatar
    0
    Spospisil created

    I have already tried that with no difference

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    hi

    Can you share a test project?

    liming.ma@volosoft.com

    Thanks

  • User Avatar
    0
    Spospisil created

    I figured you'd ask for this, but if you go ahead and produce your own test project and use the code I've included in the ticket I'm sure you'll receive the same error. Let me know when you've done this and then we can proceed.

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    hi

    Most of the time, we are unable to reproduce the issue. That is why we need you to provide the test project.

    Thanks.

  • User Avatar
    0
    Spospisil created

    Why don't you try first rather than answering with a generality?

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    hi

    Try [ExposeServices(typeof (LoginModel), typeof(OpenIddictSupportedLoginModel), typeof(CustomLoginModel))]

    using Microsoft.AspNetCore.Authentication;
    using Microsoft.AspNetCore.Identity;
    using Microsoft.Extensions.Options;
    using Owl.reCAPTCHA;
    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.Account.Web.Pages.Account;
    using Volo.Abp.DependencyInjection;
    using Volo.Abp.OpenIddict;
    using Volo.Abp.Security.Claims;
    
    namespace AbpSolution9.Pages.Account;
    
    [ExposeServices(typeof (LoginModel), typeof(OpenIddictSupportedLoginModel), typeof(CustomLoginModel))]
    public class CustomLoginModel : OpenIddictSupportedLoginModel
    {
        public CustomLoginModel(IAuthenticationSchemeProvider schemeProvider, IOptions<AbpAccountOptions> accountOptions,
            IAbpRecaptchaValidatorFactory recaptchaValidatorFactory,
            IAccountExternalProviderAppService accountExternalProviderAppService,
            ICurrentPrincipalAccessor currentPrincipalAccessor, IOptions<IdentityOptions> identityOptions,
            IOptionsSnapshot<reCAPTCHAOptions> reCaptchaOptions, AbpOpenIddictRequestHelper openIddictRequestHelper) : base(
            schemeProvider, accountOptions, recaptchaValidatorFactory, accountExternalProviderAppService,
            currentPrincipalAccessor, identityOptions, reCaptchaOptions, openIddictRequestHelper)
        {
        }
    }
    
    

    image.png

  • User Avatar
    0
    Spospisil created

    same result

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    hi

    You can share a not working project.

    : )

  • User Avatar
    0
    Spospisil created

    what's your github id?

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    hi

    Please share a simple project.

    https://github.com/maliming

    Thanks

  • User Avatar
    0
    Spospisil created

    I sent you an invite

  • User Avatar
    0
    Spospisil created

    disregard. I figured it out on my own.

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    Great

Boost Your Development
ABP Live Training
Packages
See Trainings
Mastering ABP Framework Book
The Official Guide
Mastering
ABP Framework
Learn More
Mastering ABP Framework Book
Made with ❤️ on ABP v9.3.0-preview. Updated on May 21, 2025, 13:37