*.AuthServer (or equivalent host) running and reachable, e.g.
https://auth.mycompany.comopenid, profile, email, roles, plus your API scope if A will call B’s APIs).Project A must exist as an OpenIddict application in Project B (client_id/client_secret + redirect URIs).
Practical ABP way:
ProjectA_Blazorhttps://project-a-host/signin-oidchttps://project-a-host/signout-callback-oidcauthorization_code (with PKCE recommended)openid profile email roles (+ API scopes you need)In Project A appsettings.json:
"AuthServer": {
"Authority": "https://auth.mycompany.com",
"RequireHttpsMetadata": "true",
"ClientId": "ProjectA_Blazor",
"ClientSecret": "YOUR_CLIENT_SECRET"
}
Then in Project A authentication configuration (Program.cs / Module), set OpenId Connect to point to Project B (Authority = AuthServer:Authority) and ensure the redirect URI path matches /signin-oidc.
Bearer {access_token}.HttpApi.Client project reference pattern).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.
Please contact us at https://abp.io/contact.
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.
Prepare the Test environment schema
Copy the SaaS/Tenant management database (host database)
Copy tenant data databases Depending on your tenancy database model:
A) Shared database (TenantId column in the same DB)
B) Separate database per tenant
Fix tenant connection strings in the restored SaaS DB
Run the .DbMigrator applications in Test
.DbMigrator is designed to apply EF Core migrations to:
Optional: re-run seed only if you need environment-specific defaults
Verify multitenancy resolution and caches
.DbMigrator handles schema differences between Prod dump and current Test app version.Sources (validated)
.DbMigrator section)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.
In ABP MVC, abp.localization.getResource("QueuingSystem") (and abp.localization.localize(...)) always localizes using the current UI culture that was loaded into the page. It is not designed to return strings for an arbitrary culture (e.g., "en", "ar", "hi") on the fly.
If you need to show multiple languages at the same time in one page/widget, the efficient/ABP-native way is:
localizeText(key, culture) to read from that cache (no further server calls).A. Preload the localization script for each culture once (parallel) ABP exposes a JS endpoint that returns localization texts for a given culture (used by the framework itself). You can load it dynamically:
// resourceName: "QueuingSystem"
const resourceName = "QueuingSystem";
// culture -> { resourceName -> { key -> value } }
const localizationCache = {};
function loadCultureLocalization(cultureName) {
// If already loaded, reuse.
if (localizationCache[cultureName]) {
return Promise.resolve();
}
// This endpoint returns a JS script that populates ABP's localization store.
const url = `/Abp/ApplicationLocalizationScript?cultureName=${encodeURIComponent(cultureName)}`;
return new Promise((resolve, reject) => {
const s = document.createElement("script");
s.src = url;
s.onload = () => {
// After script loads, ABP's JS localization store knows that culture's texts
// (internals). We just mark it loaded to avoid re-loading.
localizationCache[cultureName] = true;
resolve();
};
s.onerror = reject;
document.head.appendChild(s);
});
}
B. Localize without extra calls After you’ve loaded a culture’s localization script, you can switch ABP’s culture temporarily, read the text, then switch back (so you can obtain multiple cultures’ values without additional requests):
async function localizeText(key, cultureName) {
await loadCultureLocalization(cultureName);
const previousCulture = abp.localization.currentCulture.cultureName;
try {
abp.localization.currentCulture.cultureName = cultureName;
const l = abp.localization.getResource(resourceName);
return l(key);
} finally {
abp.localization.currentCulture.cultureName = previousCulture;
}
}
C. Use it efficiently Load all needed cultures once (e.g., on widget init), then localize many keys with zero extra calls:
async function initWidget() {
await Promise.all([
loadCultureLocalization("en"),
loadCultureLocalization("ar"),
loadCultureLocalization("hi")
]);
const helloEn = await localizeText("HelloMsg", "en");
const helloAr = await localizeText("HelloMsg", "ar");
const helloHi = await localizeText("HelloMsg", "hi");
console.log({ helloEn, helloAr, helloHi });
}
initWidget();
Result: at most one HTTP request per culture (/Abp/ApplicationLocalizationScript?cultureName=xx) and then unlimited localizeText(...) calls with no additional server calls.
await loadCultureLocalization(...) and uses caching.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 you enabled in the UI (“Ldap Login Settings”) is LDAP username/password login, not SSO.
If your goal is SSO, you need to use an identity provider/protocol that supports SSO for SPAs, typically:
If your goal is LDAP login (not SSO) but it “does not work”, do all of these (host side):
Account.EnableLdapLogin must be enabled (and tenant too if you’re logging in as a tenant user).If you want “SSO only on login” for an Angular UI, the correct approach is usually:
Links (verified):
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.
appsettings.json (Host and AuthServer if you have separated auth):{
"MultiTenancy": {
"IsEnabled": true
}
}
AbpTenantResolveOptions and accidentally clear resolvers (this is a common reason tenant switching disappears/breaks).Configure<AbpTenantResolveOptions>(options =>
{
options.TenantResolvers.Clear(); // <- this can break tenant switching
});
application-configuration response and then sends it as the __tenant header on requests. If application-configuration is failing or not reachable, tenant switching won’t work correctly.GET /api/abp/application-configuration returns 200.AbpTenantSwitchLink) from the login page. Re-check your Account layout customizations.Links (verified)
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.
Volo.Abp.Account.Web. So you override it the same way: add a Razor Page with the same path in the host project that actually serves MVC endpoints (in most ABP Blazor Server solutions this is *.HttpApi.Host, not the *.Blazor project).Create the overriding Razor Page
app.UseConfiguredEndpoints() and serves /Account/Login):
Pages/AccountPages/Account/Login.cshtmlKeep ABP’s logic by deriving from the built-in LoginModel
CustomLoginModel) that inherits from ABP’s LoginModel and only calls the base constructor (so you keep default behavior).using Microsoft.AspNetCore.Authentication;
using Microsoft.Extensions.Options;
using Volo.Abp.Account.Web;
using Volo.Abp.Account.Web.Pages.Account;
namespace YourCompany.YourApp.HttpApi.Host.Pages.Account;
public class CustomLoginModel : LoginModel
{
public CustomLoginModel(
IAuthenticationSchemeProvider schemeProvider,
IOptions<AbpAccountOptions> accountOptions)
: base(schemeProvider, accountOptions)
{
}
}
Pages/Account/Login.cshtml, set:@page
@model YourCompany.YourApp.HttpApi.Host.Pages.Account.CustomLoginModel
Model.LoginInput.UserNameOrEmailAddressModel.LoginInput.PasswordModel.ReturnUrl, Model.ReturnUrlHashModel.EnableLocalLogin, Model.VisibleExternalProviders, etc._ViewImports.cshtml
Pages or Pages/Account, add _ViewImports.cshtml (if you don’t already have it):@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@addTagHelper *, Volo.Abp.AspNetCore.Mvc.UI
@addTagHelper *, Volo.Abp.AspNetCore.Mvc.UI.Bootstrap
@addTagHelper *, Volo.Abp.AspNetCore.Mvc.UI.Bundling
*.Blazor) from the server/host project (*.HttpApi.Host).*.HttpApi.Host). Putting Pages/Account/Login.cshtml into the *.Blazor project commonly won’t affect /Account/Login.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.
Issue 1 (Azure Redis Cache): Yes, ABP supports passing the full Azure Redis connection string via Redis:Configuration.
In ABP’s Azure deployment guidance for the layered (tiered) template, Redis is configured like this (note it’s the same “full string” format Azure provides, including password=..., ssl=true, etc.):
appsettings.json (each app/service you deploy that uses distributed cache / data-protection):
{
"Redis": {
"Configuration": "your-cache-name.redis.cache.windows.net:6380,password=YOUR_KEY,ssl=true,abortConnect=False"
}
}
So you should use Redis:Configuration (not Redis:Host) and put the full Azure connection string value there.
Important practical notes for Azure Cache for Redis:
Use port 6380 + ssl=true (Azure Redis typically requires TLS).
The “firewall allow all IPs” setting is not always the only gate; if you use Private Endpoint / VNET, the public endpoint won’t be reachable from App Service unless networking is configured accordingly. (This is Azure-side, not ABP-side.)
Issue 2 (Disable RabbitMQ / use in-memory bus): No verified source available in the provided context/tools for an ABP 9.2.1 “official/best-practice switch” to turn off RabbitMQ via only appsettings.json.
What can be stated definitively:
Since I couldn’t verify the exact supported “disable RabbitMQ / fall back to in-memory” switch for ABP 9.2.1 from the available sources, I will not provide an unverified config/code change here.
Sources (validated)
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.
You can’t solve this in ABP code if the error you see is from Azure AD (“redirect_uri is invalid”). Azure AD requires the redirect_uri in the authentication request to exactly match one of the redirect URIs configured in the App Registration. With per-tenant subdomains, the redirect URI becomes https://tenant1.app.mydomain.com/..., so Azure rejects it unless you register each tenant subdomain (which is what you want to avoid).
What ABP can do is:
app.mydomain.com) so Azure AD only needs one redirect URI.Goal: even when the user starts login at https://tenant1.app.mydomain.com, the OIDC redirect_uri sent to Microsoft should be https://app.mydomain.com/signin-oidc (or your configured callback path).
Register only one Redirect URI in Azure AD, for example:
https://app.mydomain.com/signin-oidc (typical OIDC callback path)In your ASP.NET Core/ABP host (Blazor Server), override the redirect URI that the Microsoft handler uses by handling OnRedirectToIdentityProvider:
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
context.Services.Configure<OpenIdConnectOptions>("Microsoft", options =>
{
options.Events ??= new OpenIdConnectEvents();
options.Events.OnRedirectToIdentityProvider = ctx =>
{
// Force Azure AD redirect_uri to always be the root domain callback:
// Adjust path if your callback differs.
var fixedRedirectUri = "https://app.mydomain.com/signin-oidc";
ctx.ProtocolMessage.RedirectUri = fixedRedirectUri;
// Optional but recommended: keep which tenant initiated the login,
// so after callback you can redirect back to tenant domain.
// One common way is to put tenant info into 'state' (OIDC will roundtrip it).
// If you already have returnUrl, it's typically already round-tripped by ABP/account flow.
//
// ctx.ProtocolMessage.State = ... (only if you know exactly what you're doing)
return Task.CompletedTask;
};
});
app.mydomain.com, redirect the user back to the tenant domain (e.g. by returnUrl). In your log, you already have:returnUrl=https%3A%2F%2Ftenant1.app.mydomain.com%2F...
So using a fixed callback host is compatible with “going back to the tenant” as long as your login flow preserves/uses the returnUrl.This approach avoids registering tenant-specific redirect URIs in Azure AD entirely.
If you ever see invalid redirect_uri coming from your own auth server/OpenIddict side, ABP’s wildcard-domain support helps there (so you don’t need to seed every tenant redirect URI into OpenIddict). But it does not change Azure AD’s redirect URI rules.
To enable ABP wildcard support (useful in OpenIddict/AuthServer scenarios):
using Volo.Abp.OpenIddict.WildcardDomains;
public override void PreConfigureServices(ServiceConfigurationContext context)
{
PreConfigure<AbpOpenIddictWildcardDomainOptions>(options =>
{
options.EnableWildcardDomainSupport = true;
options.WildcardDomainsFormat.Add("https://{0}.app.mydomain.com");
});
}
Keep your domain tenant resolver as-is:
using Volo.Abp.MultiTenancy;
Configure<AbpTenantResolveOptions>(options =>
{
options.AddDomainTenantResolver("{0}.app.mydomain.com");
});
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.
As of what I can verify from the available sources/tools right now, there is no published/verified “CMS Kit for Angular” installation & integration documentation, and I also couldn’t locate any @abp/ng.*cms-kit* (or similar) Angular package/module implementation in the indexed abpframework/abp repository via code search.
Because of that, I can’t provide definitive step-by-step Angular installation/configuration instructions for CMS Kit (packages to install, Angular modules to import, routes/menu contributions, proxy generation modules, etc.) without guessing.
Milestone link you referenced is reachable and confirms you’re looking at ABP 10.1 milestone items, but it doesn’t itself provide consumable installation steps for Angular:
For general Angular setup verification (not CMS Kit specific), these official quick-start pages are reachable:
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.