Open Closed

Add Tenant Code on Register Page #8859


User avatar
0
LiSong created
  • ABP Framework version: v9.X.X
  • UI Type: MVC / Blazor WASM / Blazor Server
  • Database System: EF Core (SQL Server, Oracle, MySQL, PostgreSQL, etc..) / MongoDB
  • Tiered (for MVC) or Auth Server Separated (for Angular): no
  • Exception message and full stack trace:
  • Steps to reproduce the issue:

We are trying to add a textbox on the register page, users can put a code in and we will use the code to pick a tenant for them, so instead of using https://localhost:44381/Account/register?__tenant=lego they will just use https://localhost:44381/Account/register and put a code 'lego' on the new textbox and then the code behind will select lego as the tenant how can we do that? thanks


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

    Hi,

    I would like to clarify a few things to fully understand your requirement. ABP already allows Tenant selection on the register page. What exactly do you want as a difference?

  • User Avatar
    0
    LiSong created

    Here are what we wanted

    1. removing the current tenant switch;
    2. adding a new textbox called Invite Code
    3. creating a function to map the invite codes to tenant id (for example, XDEF(Code) -> test(Tenant Id); i.e. when type XDEF for code, users will create a new account for test tenant

    The reason we do this is that we don't want to expose the platform's tenants externally.

  • User Avatar
    0
    berkansasmaz created
    Support Team .NET Developer

    I understand, for this you need to override the login page first. Then you can make the changes you want. You can follow the steps below to override the login page.

    1. Create Account folder in Pages folder like below:

    1. Create Login.cshtml and Login.cshml.cs files in Account folder.
    2. Update Login.cshtml and Login.cshml.cs files like below:

    Login.cshtml

    @page
    @using Microsoft.AspNetCore.Mvc.Localization
    @using Microsoft.AspNetCore.Mvc.TagHelpers
    @using Microsoft.Extensions.Options
    @using Owl.reCAPTCHA
    @using Owl.reCAPTCHA.v2.TagHelpers
    @using Owl.reCAPTCHA.v3.TagHelpers
    @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.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Alert
    @using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Button
    @using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Grid
    @using Volo.Abp.AspNetCore.Mvc.UI.Bundling.TagHelpers
    @using Volo.Abp.Settings
    @model NonTieredApp.Web.Pages.Account.TappLoginModel
    
    @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-bundle name="@typeof(LoginModel).FullName">
            <abp-script src="/Pages/Account/Login.js" />
        </abp-script-bundle>
    
        @if (Model.UseCaptcha)
        {
            if (reCaptchaVersion == 3)
            {
                <recaptcha-script-v3 />
                <recaptcha-script-v3-js action="login" execute="false" />
            }
            else
            {
                <recaptcha-script-v2 />
            }
        }
    }
    
    @if (Model.IsLinkLogin)
    {
        <abp-alert alert-type="Warning">
            @L["LinkAccountWarning", Url.PageLink()]
        </abp-alert>
    }
    
    @if (Model.BackToExternalLogins)
    {
        <div class="d-grid gap-2">
            <a class="mb-3 btn btn-primary btn-block" href="@Url.Page("./ExternalLogins")">@L["Back"]</a>
        </div>
    }
    <div class="account-module-form">
    
        @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, "Email Address")
                        <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() && false)
        {
            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>
        }
    </div>
    

    Login.cshtml.cs

    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.Auditing;
    using Volo.Abp.Security.Claims;
    
    namespace NonTieredApp.Web.Pages.Account;
    
    [DisableAuditing]
    public class TappLoginModel : LoginModel
    {
        public TappLoginModel(
            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)
        {
    
        }
    }
    

    You can then customize it any way you want. If you have a specific question in the implementation, you can ask.

  • User Avatar
    0
    LiSong created

    I can see we have the code in layout default.cshtml <div> <div class="row"> <div class="col"> <span style="font-size: .8em;" class="text-uppercase text-muted">@MultiTenancyStringLocalizer["Tenant"]</span><br /> <h6 class="m-0 d-inline-block"> @if (CurrentTenant.Id == null) { <span> @MultiTenancyStringLocalizer["NotSelected"] </span> } else { <strong> @(CurrentTenant.Name ?? CurrentTenant.Id.Value.ToString()) </strong> } </h6> </div> <div class="col-auto"> <a id="AbpTenantSwitchLink" href="javascript:;" class="btn btn-sm btn-outline-primary">@MultiTenancyStringLocalizer["Switch"]</a> </div> </div> </div>

    1. I can't find the js code to set the tenant with the __tenant value in querystring, can you show me where the js function is? because I want to change the UI a little bit. or is it just set by QueryStringTenantResolveContributor? QueryStringTenantResolveContributor -> CurrentTenant-> CurrentTenant.Name
    2. can you show me where the code for the switch tenant pop up window is? I couldn't find it

  • User Avatar
    0
    berkansasmaz created
    Support Team .NET Developer

    I can't find the js code to set the tenant with the __tenant value in querystring, can you show me where the js function is? because I want to change the UI a little bit. or is it just set by QueryStringTenantResolveContributor? QueryStringTenantResolveContributor -> CurrentTenant-> CurrentTenant.Name

    When you remove tenant selection from the UI, the parameter from the query will not be very important. So you don't need to worry about it.

    can you show me where the code for the switch tenant pop up window is? I couldn't find it

    This code comes from the layout of the theme.

    You can also look at the content published on this topic. See:

    • https://abp.io/community/articles/hide-the-tenant-switch-of-the-login-page-4foaup7p
    • https://abp.io/support/questions/1399/How-to-hide-Tenant-switch-from-login-page-but-without-disable-tenant
    • https://abp.io/support/questions/4914/ABP-Login-Hiding-Tenant--checking-the-user-in-which-tenant-to-sign-in-Directly
  • User Avatar
    0
    LiSong created

    *can you show me where the code for the switch tenant pop up window is? I couldn't find it

    This code comes from the layout of the theme. * I meant the modal, I found it for the mauiblazor , but couldn't find it for the MVC project: <Modal @ref="SwitchTenantModal" Closing="@SwitchTenantModal.CancelClosingModalWhenFocusLost"> <ModalContent Centered="true"> <Form> <ModalHeader> <ModalTitle>@L["SwitchTenant"]</ModalTitle> <CloseButton Clicked="CloseSwitchTenantModalAsync"/> </ModalHeader> <ModalBody> <Field> <FieldLabel>@L["Name"] *</FieldLabel> <TextEdit @bind-Text="TenantName" Autofocus="true"/> <div class="form-text">@L["SwitchTenantHint"]</div> </Field> </ModalBody> <ModalFooter> <Button Color="Color.Secondary" Clicked="CloseSwitchTenantModalAsync">@L["Cancel"]</Button> <SubmitButton Clicked="@SwitchTenantAsync"/> </ModalFooter> </Form> </ModalContent> </Modal>

  • User Avatar
    0
    berkansasmaz created
    Support Team .NET Developer

    Hi,

    I got it, this code comes from the Multi-Tenancy(open-source) module. Here is the code: https://github.com/abpframework/abp/blob/6f8847dd012fa715be029385b9bc1b52e1a0afd5/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Pages/Abp/MultiTenancy/TenantSwitchModal.cshtml

Boost Your Development
ABP Live Training
Packages
See Trainings
Mastering ABP Framework Book
Do you need assistance from an ABP expert?
Schedule a Meeting
Mastering ABP Framework Book
The Official Guide
Mastering
ABP Framework
Learn More
Mastering ABP Framework Book
Made with ❤️ on ABP v9.2.0-preview. Updated on March 17, 2025, 10:38