Open Closed

Overriding UI of Login Page - Identity Server #1668


User avatar
0
Spospisil created

How can I override the UI of the login page using Identity server/Blazor. There is an article in the community section however it appears to be based on a earlier version of ABP where the 'LoginModel' class is in a different namespace.

ABP Framework version:** v4.4 UI type**: Blazor DB provider**: EF Core Identity Server Separated: yes


14 Answer(s)
  • User Avatar
    0
    berkansasmaz created
    Support Team .NET Developer

    Hi,

    I'm assuming you have created the Account folder in the Pages folder of the MyProjectName.IdentityServer project :)

    CustomLoginModel.cs in Account folder

        [Dependency(ReplaceServices = true)]
        [ExposeServices(typeof(LoginModel))]
        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)
            {
                Console.WriteLine("Test QA Question");
            }
        }
    

    Please let us know if it works after you try it.

  • User Avatar
    0
    murat.yuceer created

    Hi, how can i change tenant change panel?

  • User Avatar
    0
    Spospisil created

    Hi,

    I'm assuming you have created the Account folder in the Pages folder of the MyProjectName.IdentityServer project :)

    CustomLoginModel.cshtml in Account folder

        [Dependency(ReplaceServices = true)] 
        [ExposeServices(typeof(LoginModel))] 
        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) 
            { 
                Console.WriteLine("Test QA Question"); 
            } 
        } 
    

    Please let us know if it works after you try it.

    I'm a little confused by your response as you mentioned that this should be done in the cshtml file not the class. Could you list exactly what I need to do in order to do this as I've seend some conflicting information in the documentation.

    Thanks

  • User Avatar
    0
    Spospisil created

    Hi,

    I'm assuming you have created the Account folder in the Pages folder of the MyProjectName.IdentityServer project :)

    CustomLoginModel.cshtml in Account folder

        [Dependency(ReplaceServices = true)]  
        [ExposeServices(typeof(LoginModel))]  
        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)  
            {  
                Console.WriteLine("Test QA Question");  
            }  
        }  
    

    Please let us know if it works after you try it.

    I'm a little confused by your response as you mentioned that this should be done in the cshtml file not the class. Could you list exactly what I need to do in order to do this as I've seend some conflicting information in the documentation.

    Thanks

    To be more specific I want to override the look of of the login page, meaning I don't want the tennant selector and I also want to add/remove items in the login box and do custom code (calling different services) behind the various button clicks.

  • User Avatar
    0
    berkansasmaz created
    Support Team .NET Developer

    First of all it will be CustomLoginModel.cs not CustomLoginModel.cshtml :(

    So it's my fault that you're confused, I hope I can make up for it :)

    If we have to go step by step 👇

    • Create a new Login.cshtml under Pages\Account folder

    Login.cshtml

    @page
    @using Microsoft.AspNetCore.Mvc.Localization
    @using Microsoft.Extensions.Options
    @using Owl.reCAPTCHA
    @using Volo.Abp.Account.Localization
    @using Volo.Abp.Account.Public.Web.Security.Recaptcha
    @using Volo.Abp.Account.Settings
    @using Volo.Abp.Settings
    @model Volo.Abp.Account.Public.Web.Pages.Account.LoginModel
    @inject IHtmlLocalizer<AccountResource> L
    @inject Volo.Abp.AspNetCore.Mvc.UI.Layout.IPageLayout PageLayout
    @inject ISettingProvider SettingProvider
    @{
        PageLayout.Content.Title = L["Login"].Value;
        var reCaptchaVersion = await SettingProvider.GetAsync<int>(AccountSettingNames.Captcha.Version);
        if (Model.UseCaptcha)
        {
            await Model.ReCaptchaOptions.SetAsync(reCaptchaVersion == 3 ? reCAPTCHAConsts.V3 :reCAPTCHAConsts.V2);
        }
    
    }
    
    @section scripts
    {
        <abp-script src="/Pages/Account/Login.js" />
        @if (Model.UseCaptcha)
        {
            if (reCaptchaVersion == 3)
            {
                <recaptcha-script-v3/>
                <recaptcha-script-v3-js action="login" callback="(function(){$('#@RecaptchaValidatorBase.RecaptchaResponseKey').val(token)})"/>
            }
            else
            {
                <recaptcha-script-v2/>
            }
        }
    }
    
    @section styles
    {
        <abp-style src="/Pages/Account/Login.css" />
    }
    
    @if (Model.IsLinkLogin)
    {
        <abp-alert alert-type="Warning">
            @L["LinkAccountWarning", Url.PageLink()]
        </abp-alert>
    }
    
    <div class="account-module-form">
        @if (Model.EnableLocalLogin)
        {
            <form method="post">
                @if (Model.UseCaptcha)
                {
                    <input type="hidden" name="@RecaptchaValidatorBase.RecaptchaResponseKey" id="@RecaptchaValidatorBase.RecaptchaResponseKey"/>
                }
                <p>Test QA Question: 1668</p>
                <abp-input asp-for="LoginInput.UserNameOrEmailAddress" required-symbol="false"/>
                <abp-input asp-for="LoginInput.Password" required-symbol="false"/>
                <abp-row>
                    <abp-column>
                        <abp-input asp-for="LoginInput.RememberMe" class="mb-4"/>
                    </abp-column>
                    <abp-column class="text-right">
                        <a href="@Url.Page("./ForgotPassword", new { returnUrl = Model.ReturnUrl, returnUrlHash = Model.ReturnUrlHash })">@L["ForgotPassword"]</a>
                    </abp-column>
                </abp-row>
    
                @if (reCaptchaVersion == 2)
                {
                    <recaptcha-div-v2 callback="(function(){$('#@RecaptchaValidatorBase.RecaptchaResponseKey').val(token)})" />
                }
    
                <abp-button button-type="Primary" size="Block" type="submit" class="mt-2 mb-3" name="Action" value="Login">@L["Login"]</abp-button>
                @if (Model.ShowCancelButton)
                {
                    <abp-button button-type="Secondary" size="Block" type="submit" formnovalidate="formnovalidate" class="mt-2 mb-3" name="Action" value="Cancel">@L["Cancel"]</abp-button>
                }
            </form>
            if (Model.IsSelfRegistrationEnabled)
            {
                @L["NotAMemberYet"]
                <a href="@Url.Page("./Register", new {returnUrl = Model.ReturnUrl, returnUrlHash = Model.ReturnUrlHash})">@L["Register"]</a>
            }
        }
    
        @if (Model.VisibleExternalProviders.Any())
        {
            <hr/>
            @L["OrSignInWith"]<br/>
            <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="mt-2 mr-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>
        }
    
        @if (!Model.EnableLocalLogin && !Model.VisibleExternalProviders.Any())
        {
            <div class="alert alert-warning">
                <strong>Invalid login request</strong>
                There are no login schemes configured for this client.
            </div>
        }
    </div>
    

    Then... 👇👇👇

  • User Avatar
    0
    berkansasmaz created
    Support Team .NET Developer
    • Create CustomLoginModel.cs file in the same folder (Pages\Account) CustomLoginModel.cs
    using System;
    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.DependencyInjection;
    using Volo.Abp.Security.Claims;
    
    namespace TestQABZ.Pages.Account
    {
        [Dependency(ReplaceServices = true)]
        [ExposeServices(typeof(LoginModel))]
        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)
            {
                Console.WriteLine("Test QA");
            }
        }
    }
    
    • Add a CSS to play with the existing styles. Create Login.css in the same folder (Pages\Account) Login.css
    .container {
        background-color: #d0d08c;
    }
    
    .card {
        background: #bccce4;
    }
    
    
    • Add a JS to write JavaScript. Create Login.js in the same folder (Pages\Account) Login.js
    alert('login page loaded.');
    
    

    When we do all these, you will have a structure similar to the one below:

    Once it's up and running, you can make any changes you want, but if you encounter a problem, please let us know again.

  • User Avatar
    0
    Spospisil created

    Hi,

    I've tried what you suggest and still continue to get an error as shown below.

  • User Avatar
    0
    Spospisil created

    Keep in mind I'm trying to override the login page at the 'public' web application level, not Identity Server.

  • User Avatar
    0
    berkansasmaz created
    Support Team .NET Developer

    Hi,

    In our previous conversations, you did not say that you want to do it on the public web side.

    As a solution: Compare the MyProjectNameWebPublicModule in the MyProjectName.Web.Public project with the MyProjectNameIdentityServerModule in the MyProjectName.IdentityServer project and add the missing ones to the MyProjectNameWebPublicModule. As a result, you will reach the view in the image below.

    Please let us know if it works after you try it.

  • User Avatar
    0
    Spospisil created

    Hi,

    That resolved what I was trying to accomplish, but I'm still getting other errors. Do you have an working example of a public web site where I can completely override not only the UI aspect of the login page but also put additional logic in the OnPostAsync methed of the login page to do additional processing after the base Volo.Abp.Account.Public.Web.Pages.Account.LoginModel OnPostAsync method runs?

    The Volo.EasyCrm real world application does not implement the login page in this same way.

    Steve.

  • User Avatar
    0
    berkansasmaz created
    Support Team .NET Developer

    Hi,

    To do this, you need to override the OnPostAsync method;

            // For example
            public override async Task<IActionResult> OnPostAsync(string action)
            {
                Console.WriteLine("OnPost - Before");
                var result = await base.OnPostAsync(action);
                Console.WriteLine("OnPost - After");
    
                return result;
            }
    

    Then remove the following two lines from the ConfigureAuthentication method in MyProjectNamePublicWebModule.

        options.DefaultScheme = "Cookies";
        options.DefaultChallengeScheme = "oidc";
    

    The final state should be as in the picture;

    Finally, add the following code to the appsettings.json file of the MyProjectName.Web.Public project:

      "ConnectionStrings": {
        "Default": "MY-CONNECTION-STRING"
      }
    

    You can try to run the application and login to the public web side.

    Note: If you encounter a problem like the picture below after logging in, remove the relevant places from MyProjectNamePublicMenuContributor.

    As a result, you can now login via the Public web application and customize it as you wish.

  • User Avatar
    0
    Spospisil created

    Ok, I was finally able to get this to work. My question now is how can I interrogate whether or not the user was successfully authenticated after the .base.OnPostAsync(action) result is returned. I don't see an obvious way to determine this.

    // For example public override async Task<IActionResult> OnPostAsync(string action) { Console.WriteLine("OnPost - Before"); var result = await base.OnPostAsync(action); Console.WriteLine("OnPost - After");

            return result;
        }
    
  • User Avatar
    0
    Spospisil created

    Disregard. It looks like I'm having an issue with identity server and the 'CurrentUser.IsAuthenticated' property is never being set to true.

  • User Avatar
    0
    berkansasmaz created
    Support Team .NET Developer

    Actually, I faced the same problem, and deleting the following two lines that I said in my previous answer solved my problem.

    Then remove the following two lines from the ConfigureAuthentication method in MyProjectNamePublicWebModule.

        options.DefaultScheme = "Cookies";
        options.DefaultChallengeScheme = "oidc";
    

    I think you can search other places where this is used and delete and test the ones you find one by one.

    However, since this issue has been resolved, I'm closing this here.

Made with ❤️ on ABP v8.2.0-preview Updated on March 25, 2024, 15:11