How to Customize the Login Page for MVC / Razor Page Applications

When you create a new application using the application startup template, source code of the login page will not be inside your solution, so you can not directly change it. The login page comes from the Account Module that is used a NuGet package reference.

This document explains how to customize the login page for your own application.

Create a Login PageModel

Create a new class inheriting from the LoginModel of the Account module.

public class CustomLoginModel : LoginModel
{
    public CustomLoginModel(
    Microsoft.AspNetCore.Authentication.IAuthenticationSchemeProvider schemeProvider,
    Microsoft.Extensions.Options.IOptions<Volo.Abp.Account.Web.AbpAccountOptions> accountOptions)
        : base(schemeProvider, accountOptions)
        {
        }
}

Naming convention is important here. If your class name doesn't end with LoginModel, you need to manually replace the LoginModel using the dependency injection system.

Then you can override any method you need and add new methods and properties needed by the UI.

Overriding the Login Page UI

Create folder named Account under Pages directory and create a Login.cshtml under this folder. It will automatically override the Login.cshtml file defined in the Account Module thanks to the Virtual File System.

A good way to customize a page is to copy its source code. Click here for the source code of the login page. At the time this document has been written, the source code was like below:

@page
@using Volo.Abp.Account.Settings
@using Volo.Abp.Settings
@model Acme.BookStore.Web.Pages.Account.CustomLoginModel
@inherits Volo.Abp.Account.Web.Pages.Account.AccountPage
@inject Volo.Abp.Settings.ISettingProvider SettingProvider
@if (Model.EnableLocalLogin)
{
    <div class="card mt-3 shadow-sm rounded">
        <div class="card-body p-5">
            <h4>@L["Login"]</h4>
            @if (await SettingProvider.IsTrueAsync(AccountSettingNames.IsSelfRegistrationEnabled))
            {
                <strong>
                    @L["AreYouANewUser"]
                    <a href="@Url.Page("./Register", new {returnUrl = Model.ReturnUrl, returnUrlHash = Model.ReturnUrlHash})" class="text-decoration-none">@L["Register"]</a>
                </strong>
            }
            <form method="post" class="mt-4">
                <input asp-for="ReturnUrl" />
                <input asp-for="ReturnUrlHash" />
                <div class="form-group">
                    <label asp-for="LoginInput.UserNameOrEmailAddress"></label>
                    <input asp-for="LoginInput.UserNameOrEmailAddress" class="form-control" />
                    <span asp-validation-for="LoginInput.UserNameOrEmailAddress" class="text-danger"></span>
                </div>
                <div class="form-group">
                    <label asp-for="LoginInput.Password"></label>
                    <input asp-for="LoginInput.Password" class="form-control" />
                    <span asp-validation-for="LoginInput.Password" class="text-danger"></span>
                </div>
                <div class="form-check">
                    <label asp-for="LoginInput.RememberMe" class="form-check-label">
                        <input asp-for="LoginInput.RememberMe" class="form-check-input" />
                        @Html.DisplayNameFor(m => m.LoginInput.RememberMe)
                    </label>
                </div>
                <abp-button type="submit" button-type="Primary" name="Action" value="Login" class="btn-block btn-lg mt-3">@L["Login"]</abp-button>
            </form>
        </div>

        <div class="card-footer text-center border-0">
            <abp-button type="button" button-type="Link" name="Action" value="Cancel" class="px-2 py-0">@L["Cancel"]</abp-button> @* TODO: Only show if identity server is used *@
        </div>
    </div>
}

@if (Model.VisibleExternalProviders.Any())
{
    <div class="col-md-6">
        <h4>@L["UseAnotherServiceToLogIn"]</h4>
        <form asp-page="./Login" asp-page-handler="ExternalLogin" asp-route-returnUrl="@Model.ReturnUrl" asp-route-returnUrlHash="@Model.ReturnUrlHash" method="post">
            <input asp-for="ReturnUrl" />
            <input asp-for="ReturnUrlHash" />
            @foreach (var provider in Model.VisibleExternalProviders)
            {
                <button type="submit" class="btn btn-primary" 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>
}

Just changed the @model to Acme.BookStore.Web.Pages.Account.CustomLoginModel to use the customized PageModel class. You can change it however your application needs.

The Source Code

You can find the source code of the completed example here.

See Also

Contributors


Last updated: April 06, 2020 Edit this page on GitHub

Was this page helpful?

Please make a selection.

To help us improve, please share your reason for the negative feedback in the field below.

Please enter a note.

Thank you for your valuable feedback!

Please note that although we cannot respond to feedback, our team will use your comments to improve the experience.

In this document
Community Talks

Building Modular Monolith Applications Using .Net and ABP Framework

17 Oct, 20:00
Online
Register Now
Mastering ABP Framework Book
Mastering ABP Framework

This book will help you gain a complete understanding of the framework and modern web application development techniques.

Learn More