A) Recommended: Use Releases/Tags as versions
B) If you must use Branches as versions
Additional checks
Quick apply to your screenshots
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.
Notes:
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.
Yes. ABP supports showing an OTP step after a successful username/password login (Two-Factor Authentication). Users can use Email, SMS, or an Authenticator app. You can allow the user to choose Email or SMS on the OTP screen, or you can restrict to one provider.
Enable 2FA and providers:
How the built-in flow works:
Let the user choose Email or SMS:
public class MyAccountAppService : AccountAppService
  {
      public override async Task<ListResultDto<NameValue>> GetTwoFactorProvidersAsync(GetTwoFactorProvidersInput input)
      {
          var result = await base.GetTwoFactorProvidersAsync(input);
          // Keep only Email and SMS
          result.Items = result.Items.Where(p => p.Name == "Email" || p.Name == "SMS").ToList();
          return result;
      }
  }
Register your derived service in your module to replace the default IAccountAppService.
If you want to force a single provider (e.g., SMS only) and remove the dropdown:
Notes and prerequisites:
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.
Do this checklist and fix:
Generate a proper OpenIddict certificate for production (not the site TLS cert)
Configure ABP OpenIddict to use the PFX in production
public override void PreConfigureServices(ServiceConfigurationContext context)
    {
        var hostingEnvironment = context.Services.GetHostingEnvironment();
        var configuration = context.Services.GetConfiguration();
        if (!hostingEnvironment.IsDevelopment())
        {
            PreConfigure<AbpOpenIddictAspNetCoreOptions>(options =>
            {
                options.AddDevelopmentEncryptionAndSigningCertificate = false;
            });
            PreConfigure<OpenIddictServerBuilder>(serverBuilder =>
            {
                // If your pfx is in content root
                serverBuilder.AddProductionEncryptionAndSigningCertificate(
                    "openiddict.pfx",
                    "00000000-0000-0000-0000-000000000000"
                );
                // Set the public HTTPS issuer
                serverBuilder.SetIssuer(new Uri(configuration["AuthServer:Authority"]!));
            });
        }
    }
PreConfigure<OpenIddictServerBuilder>(serverBuilder =>
    {
        var flags = X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.EphemeralKeySet;
        serverBuilder.AddProductionEncryptionAndSigningCertificate("openiddict.pfx", "00000000-0000-0000-0000-000000000000", flags);
    });
Ensure the file is actually present on the server
Keep Authority and metadata HTTPS-only
"AuthServer": {
      "Authority": "https://your-authserver-host",
      "RequireHttpsMetadata": true,
      "CertificatePassPhrase": "00000000-0000-0000-0000-000000000000"
    }
app.Use(async (ctx, next) =>
      {
          ctx.Request.Scheme = "https";
          await next();
      });
If you temporarily set RequireHttpsMetadata = false, discovery will emit http URLs and SPAs will reject them; keep it true and fix scheme/forwarding instead.
About the “key rollover” message
IIS-specific tips
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.
What to change
Example const routes: Routes = [ { path: 'identity', loadChildren: () => import('@volo/abp.ng.identity').then(m => m.createRoutes()) }, { path: 'saas', loadChildren: () => import('@volo/abp.ng.saas').then(m => m.createRoutes()) }, { path: 'audit-logging', loadChildren: () => import('@volo/abp.ng.audit-logging').then(m => m.createRoutes()) }, { path: 'language-management', loadChildren: () => import('@volo/abp.ng.language-management').then(m => m.createRoutes()) }, { path: 'text-template-management', loadChildren: () => import('@volo/abp.ng.text-template-management').then(m => m.createRoutes()) }, { path: 'account', loadChildren: () => import('@volo/abp.ng.account').then(m => m.createRoutes()) }, ];
Notes
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.
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.
Apply these steps:
Use createRoutes for SAAS routes (even in a module-based app) In your AppRoutingModule (or wherever you define feature routes), load the Saas routes using the package’s route factory. This ensures the Tenants standalone component receives its providers.
Example: import { RouterModule, Routes } from '@angular/router'; import { createRoutes as createSaasRoutes } from '@volo/abp.ng.saas';
const routes: Routes = [ { path: 'saas', loadChildren: () => import('@volo/abp.ng.saas').then(m => createSaasRoutes()), }, // ...other routes ];
@NgModule({ imports: [RouterModule.forRoot(routes)], exports: [RouterModule], }) export class AppRoutingModule {}
Keep provideSaasConfig() in root providers You already have:
That’s fine. The critical part is that the route uses createSaasRoutes() so the Tenants standalone component runs with the correct Feature providers. If you currently lazy-load a module via SaasConfigModule.forRoot() alone or navigate to the component outside its created routes, the DI context misses feature tokens.
Do not declare or import TenantsComponent manually Don’t import or declare _TenantsComponent (or any SAAS component) directly in your own module. Always navigate through the SAAS routes returned by createSaasRoutes().
Version alignment Ensure all ABP/Commercial Angular packages are on the same exact version range as the ABP CLI template you’re targeting, and Angular is aligned. For ABP 9.3.6:
If you previously used forLazy() Replace any old lazy-load syntax like: loadChildren: () => import('@volo/abp.ng.saas').then(m => m.SaasModule.forLazy({...})) with the new: loadChildren: () => import('@volo/abp.ng.saas').then(m => m.createRoutes({...}))
Last-resort guard If you still see the InjectionToken undefined from a standalone component, it almost always means that page is rendered outside its feature route tree. Double-check that:
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.
What Volo.Abp.Cli is and how it’s used
Why it appears in your vulnerability scan after upgrading to 8.3.4
How to remove or mitigate the vulnerability finding
Important notes
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.
Check and fix the following:
app.UseRouting();
  app.UseAuthentication();
  app.UseMultiTenancy();
  app.UseAuthorization();
Wrong order (multi-tenancy before authentication) leads to missing tenant context during DbContext creation and thus host DB usage.
using (_currentTenant.Change(tenantId))
  {
      // Resolve repositories/DbContexts here, after tenant has been set
      var ctx = await repository.GetDbContextAsync();
      // Now ctx.Database.GetConnectionString() should be the tenant’s connection string
  }
Do not cache DbContext, DbContextOptions, or services that depend on tenant context across tenant boundaries.
Configure<AbpDbContextOptions>(options =>
  {
      options.UseSqlServer(); // no literal connection string here
  });
If you pass a literal connection string in UseSqlServer, it will always use that (host) connection, bypassing the resolver.
[ConnectionStringName("Default")]
  public class MyDbContext : AbpDbContext<MyDbContext> { ... }
Since your checks show ConnectionStringNameAttribute.GetConnStringName returns the correct name and the resolver returns the tenant string, this is likely fine.
[Dependency(ReplaceServices = true)]
  public class EncryptedConnectionStringChecker : SqlServerConnectionStringChecker
  {
      private readonly IStringEncryptionService _enc;
      public EncryptedConnectionStringChecker(IStringEncryptionService enc) => _enc = enc;
      public override Task<AbpConnectionStringCheckResult> CheckAsync(string cs)
          => base.CheckAsync(_enc.Decrypt(cs));
  }
Don’t read Database.GetConnectionString too early If you log dbContext.Database.GetConnectionString() before the tenant scope is active or before the provider initializes, you may see host/null. Inspect after resolving DbContext within the tenant scope.
Background workers and integration events For hosted services, background jobs, or event handlers (out of HTTP pipeline), wrap operations with _currentTenant.Change(tenantId) before resolving repositories/DbContexts.
Minimal checklist you can try now:
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.
Paso a paso
A) Usar el layout Side Menu de LeptonX En tu módulo Blazor:
using Volo.Abp.AspNetCore.Components.Web.LeptonXTheme;
public override void ConfigureServices(ServiceConfigurationContext context)
{
    Configure<LeptonXThemeBlazorOptions>(options =>
    {
        // Asegura el layout con menú lateral
        options.Layout = typeof(Volo.Abp.AspNetCore.Components.Web.LeptonXTheme.Components.ApplicationLayout.SideMenu.SideMenuLayout);
    });
}
Con esto, el layout de LeptonX ya renderiza el menú lateral.
B) Si mantienes tu CustomApplicationLayout.razor Incluye los componentes de LeptonX del layout “SideMenu” en tu layout personalizado. Ejemplo mínimo:
@inherits LayoutComponentBase
@using Volo.Abp.AspNetCore.Components.Web.LeptonXTheme.Components.ApplicationLayout.SideMenu.MainHeader
@using Volo.Abp.AspNetCore.Components.Web.LeptonXTheme.Components.ApplicationLayout.SideMenu.SideMenu
@using Volo.Abp.AspNetCore.Components.Web.LeptonXTheme.Components.ApplicationLayout.Common
<div id="lpx-wrapper" class="lpx-layout lpx-layout--side">
    <!-- Header con brand, user, etc. -->
    <MainHeader />
    <div class="lpx-container">
        <!-- MENÚ LATERAL (área roja) -->
        <SideMenu />
        <!-- Contenido -->
        <div class="lpx-content-container">
            <!-- tu Submenu (verde) -->
            <CustomSubmenu />
            <div class="lpx-topbar-container">
                <div class="lpx-topbar">
                    <Breadcrumbs />
                </div>
            </div>
            <div class="lpx-content-wrapper">
                <div class="lpx-content">
                    <!-- Acciones de página (azul) -->
                    <CustomPageActions />
                    @Body
                </div>
            </div>
            <footer><Footer /></footer>
        </div>
    </div>
    <MobileNavbar />
</div>
Claves:
C) Asegúrate de no cambiar al layout TopMenu por error Si en Configure<LeptonXThemeBlazorOptions> apuntas a TopMenuLayout o si tu CustomApplicationLayout usa componentes del namespace “TopMenu”, el side menu no se renderiza. Verifica namespaces:
D) Menú principal y elementos El SideMenu se llena con el Main Menu de ABP (IMenuManager y tus MenuContributors). No necesitas dibujarlo a mano, solo asegúrate de agregar los ApplicationMenuItem en tu MenuContributor (padres e hijos).
E) Si el menú lateral aparece colapsado/oculto El layout puede estar en modo hover/collapsed por CSS. Asegúrate de no añadir clases que escondan el menú o scripts que cambien el estado. En caso de necesitar forzar estado abierto por defecto, puedes controlar clases de “lpx-wrapper” según el comportamiento deseado.
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.
 
                                