Open Closed

Modify the login form #10256


User avatar
0
thucnx@csdlquocgia.com created
  • Template: microservice
  • Created ABP Studio Version: 2.1.3
  • Current ABP Studio Version: 2.1.3
  • Multi-Tenancy: Yes
  • UI Framework: angular
  • Theme: leptonx
  • Theme Style: system
  • Theme Menu Placement: side
  • Run Install Libs: No
  • Database Provider: ef
  • Database Management System: postgresql
  • Mobile Framework: none
  • Public Website: No
  • Social Login: No
  • Include Tests: Yes
  • Dynamic Localization: Yes
  • Kubernetes Configuration: Yes
  • Grafana Dashboard: Yes
  • Use Local References: No
  • Aspire: No
  • Optional Modules:
    • FileManagement
    • TextTemplateManagement
    • OpenIddictAdmin
  • Selected Languages: Tiįŗæng Việt, English
  • Default Language: Tiįŗæng Việt
  • Create Command: abp new kcb -t microservice --ui-framework angular --database-provider ef --database-management-system postgresql --theme leptonx --skip-migration --skip-migrator --without-cms-kit --dont-run-install-libs --dont-run-bundling --no-social-logins -no-gdpr -no-audit-logging -no-language-management -file-management

Hello, I would like the login form to include three textbox fields: Tenant Name, Username, and Password. The customer requires that no switch button be used. Could you please guide me on how I can accomplish this? Thank you very much


6 Answer(s)
  • User Avatar
    0
    AI-Bot created
    Support Team ABP Agent
    1. Solution To have three textboxes on the login page (Tenant name, Username, Password) and remove the tenant switch button for an Angular UI with LeptonX theme and multi-tenancy enabled, do the following:

    A) Remove the tenant switch UI on the Account layout (server-side) for LeptonX

    • Create a view override to remove the built-in tenant switcher that appears on the public Account pages (Login, etc.).
    • In your AuthServer (or Public Web, depending on template), add this file with the same path to override the theme’s layout:
      • Path: Themes/LeptonX/Layouts/Account/Default.cshtml
    • Copy the original content from the LeptonX Account layout and remove (or comment out) the block that renders the tenant switch:
      • The block guarded by: if (MultiTenancyOptions.Value.IsEnabled && (TenantResolveResultAccessor.Result?.AppliedResolvers?.Contains(CookieTenantResolveContributor.ContributorName) == true || TenantResolveResultAccessor.Result?.AppliedResolvers?.Contains(QueryStringTenantResolveContributor.ContributorName) == true))
      • Delete that entire block (including the link with id="AbpTenantSwitchLink").
    • This ensures no switch button is shown on the login page.

    B) Add a Tenant Name textbox to the Angular login component and send it with the login request

    • In the Angular application (apps/angular), extend the login form to include a Tenant Name field and set the tenant header (__tenant) programmatically. ABP Angular sends the current tenant on each request from the application-configuration result. Since you need a textbox on the login form, set the tenant before calling the login API.

    Example steps:

    1. Create a form control and UI for tenant input:

      • If you are using the out-of-the-box LoginComponent from @abp/ng.account (Public), create a custom login component (or extend the existing one by wrapping it) that:
        • Adds a tenantName: FormControl to the form.
        • Renders a textbox above Username and Password.
    2. Set the tenant header before login:

      • Inject RestService or use ABP Angular’s tenant setter by writing the selected tenant name to the cookie/header before invoking login.
      • For Angular, ABP resolves tenant from:
        • __tenant header, or
        • abp.tenant cookie, or
        • query string (__tenant)
      • For a simple approach, set the abp.tenant cookie from the textbox value just before calling login; ABP’s standard backend resolves it.

    Example (illustrative, place inside your custom login component):

    import { Component, inject } from '@angular/core';
    import { FormBuilder, Validators } from '@angular/forms';
    import { RestService } from '@abp/ng.core';
    import { AccountService } from '@abp/ng.account/public/config'; // or your own auth call
    
    @Component({
      selector: 'app-custom-login',
      template: `
      <form [formGroup]="form" (ngSubmit)="login()">
        <label>Tenant Name</label>
        <input formControlName="tenantName" type="text" autocomplete="organization" />
    
        <label>Username</label>
        <input formControlName="username" type="text" autocomplete="username" />
    
        <label>Password</label>
        <input formControlName="password" type="password" autocomplete="current-password" />
    
        <button type="submit" [disabled]="form.invalid || submitting">Login</button>
      </form>
      `
    })
    export class CustomLoginComponent {
      private fb = inject(FormBuilder);
      private rest = inject(RestService);
      private account = inject(AccountService); // if you use the ABP Account endpoints
    
      submitting = false;
    
      form = this.fb.group({
        tenantName: [''],
        username: ['', Validators.required],
        password: ['', Validators.required],
        rememberMe: [false],
      });
    
      private setTenantCookie(name: string | null | undefined) {
        // When empty, clear cookie; when not empty, set cookie.
        const cookieName = 'abp.tenant';
        const encoded = name ? encodeURIComponent(name) : '';
        const expires = new Date();
        expires.setDate(expires.getDate() + 30);
        if (encoded) {
          document.cookie = `${cookieName}=${encoded}; path=/; expires=${expires.toUTCString()}; SameSite=Lax`;
        } else {
          // clear
          document.cookie = `${cookieName}=; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT; SameSite=Lax`;
        }
      }
    
      async login() {
        if (this.form.invalid) return;
        this.submitting = true;
        try {
          const tenantName = (this.form.value.tenantName || '').trim();
          // Ensure the backend resolves the request under this tenant:
          this.setTenantCookie(tenantName || null);
    
          // If you call login via a custom HTTP request, you can also set __tenant header:
          // Example:
          // await this.rest.request({
          //   method: 'POST',
          //   url: '/api/account/login',
          //   body: { userName: this.form.value.username, password: this.form.value.password },
          //   headers: tenantName ? { __tenant: tenantName } : {}
          // }).toPromise();
    
          // If using ABP AccountService (OIDC flow), just trigger sign-in flow after setting cookie
          await this.account.navigateToLogin(); // or start the flow used in your app
    
        } finally {
          this.submitting = false;
        }
      }
    }
    

    Notes:

    • If you use the standard OIDC login redirection provided by the Account Public module (AuthServer), setting abp.tenant cookie before starting the login is enough for the server to pick the correct tenant.
    • If you are calling a username/password endpoint directly, add the __tenant header to that request instead of (or in addition to) the cookie.

    C) Ensure tenant resolution is configured to accept cookie/header

    • ABP’s default multi-tenancy supports cookie and header resolvers. By default, Angular UI sends __tenant. If you rely on the cookie, ensure the CookieTenantResolveContributor is active (it is by default). If you previously customized AbpTenantResolveOptions to remove resolvers, re-include CookieTenantResolveContributor or HeaderTenantResolveContributor.

    Example to ensure defaults (in your HttpApiHostModule or AuthServer):

    using Volo.Abp.MultiTenancy;
    
    public override void ConfigureServices(ServiceConfigurationContext context)
    {
        Configure<AbpTenantResolveOptions>(options =>
        {
            // Keep default resolvers, or ensure header/cookie are present
            // options.TenantResolvers.Clear();
            // options.TenantResolvers.Add(new HeaderTenantResolveContributor());
            // options.TenantResolvers.Add(new CookieTenantResolveContributor());
            // options.TenantResolvers.Add(new QueryStringTenantResolveContributor());
        });
    }
    

    Outcome

    • Login page shows three textboxes: Tenant Name, Username, Password.
    • No tenant switch button is available on the page.
    • The chosen tenant is applied for the login request via cookie or header consistently with ABP multi-tenancy.
    1. Sources
    • https://abp.io/docs/latest/framework/ui/angular/multi-tenancy
    • https://abp.io/docs/latest/framework/architecture/multi-tenancy#host-or-tenant
    • https://abp.io/support/questions/9283

    This response was generated by AI. If it didn’t solve your problem, feel free to post a new answer — our support team will jump in to help you soon.

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    hi

    1. Download the leptonx module source code and copy the aspnet-core/volo/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.LeptonX/Themes/LeptonX/Layouts/Account/Default.cshtml to your project and hide the tenant selection component.
    2. Download account pro module source code and copy the abp/account/src/Volo.Abp.Account.Pro.Public.Web/Pages/Account/Login.cshtml to your project and add a new tenant name input.
    3. Override the public virtual async Task<IActionResult> OnPostAsync(string action)(abp/account/src/Volo.Abp.Account.Pro.Public.Web/Pages/Account/Login.cshtml.cs) to change the current tenant.

    Thanks.

  • User Avatar
    0
    thucnx@csdlquocgia.com created

    Override the public virtual async Task<IActionResult> OnPostAsync(string action)(abp/account/src/Volo.Abp.Account.Pro.Public.Web/Pages/Account/Login.cshtml.cs) to change the current tenant.

    I’m not clear about this step. Should I override it in the AuthServer? And in which file is the public virtual async Task<IActionResult> OnPostAsync(string action) method located?

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    hi

    Yes, the authserver project

    ````csharp
    [ExposeServices(typeof (OpenIddictSupportedLoginModel), typeof(LoginModel))]
    public class MyLoginModel : OpenIddictSupportedLoginModel
    {
        public override Task<IActionResult> OnPostAsync(string action)
        {
            var your_tenant_name = "my_tenant";// get it from post request.
            using(Current.Change(your_tenant_name))
            {
                return base.OnPostAsync(action);
            }
        }
    }
    
  • User Avatar
    0
    thucnx@csdlquocgia.com created

    Where should I add the code you provided? Please guide me step by step in detail. Thank you.

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    hi

    You can add these class/cshtml files to your AuthServer project.

    See https://abp.io/docs/latest/framework/ui/mvc-razor-pages/customization-user-interface#overriding-a-razor-page-cshtml https://abp.io/docs/latest/framework/ui/mvc-razor-pages/customization-user-interface#example-1

Learn More, Pay Less
33% OFF
All Trainings!
Get Your Deal
Mastering ABP Framework Book
The Official Guide
Mastering
ABP Framework
Learn More
Mastering ABP Framework Book
Made with ā¤ļø on ABP v10.1.0-preview. Updated on December 17, 2025, 07:08
1
ABP Assistant
šŸ” You need to be logged in to use the chatbot. Please log in first.