Activities of "AI-Bot"

  • Yes: with TenantUserSharingStrategy.Shared, the intended way to associate a user with a tenant is the invitation flow (invite → accept). If you “create the same user again” inside each tenant like in isolated mode, you end up with separate identities and you will “break” the shared-account model (no global identity to synchronize).

  • It’s not that a user must be created under Host manually. In Shared mode, the system is designed so that users are global, and tenants are workspaces a user joins. The normal onboarding is:

    1. Create tenant
    2. Invite a user to that tenant (you can pre-assign tenant roles in the invite)
    3. User accepts (if user doesn’t exist yet, they register during acceptance)
  • When a user is invited from Tenant A for the first time and they don’t already have an account, after they accept/register ABP will (conceptually) create:

    • a global/host-side identity record (the “main” identity; security like 2FA is managed at host level)
    • and a tenant association/replica for Tenant A
      So seeing more than one row is expected in Shared mode.
  • What role is assigned in Host?

    • None by default. Roles are per tenant, and the invitation UI lets the inviter pre-assign tenant roles so the user has permissions in that tenant immediately.
    • The user is not automatically a “host admin” or given host roles just because they joined a tenant.

Validated references:

  • https://abp.io/docs/10.6/modules/account/shared-user-accounts
  • https://abp.io/community/articles/mf3bkg79

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.

  • No, users do not have to be created under Host first.

    • In Shared User Accounts, the identity is global and ABP may create/keep a host-side record as the “main” identity record even if the user first arrives via an invitation + registration flow.
    • The intended way is: create tenant → invite user (email) → user accepts (registers if needed) → user becomes member of that tenant.
  • About My Account updates and syncing:

    • In Shared mode, ABP’s model is “one global identity + per-tenant working context”.
    • Security-related settings (including 2FA) are handled at the Host level (global identity).
    • Practically, you should treat “profile/security” data as global, not tenant-specific. If you allow users to edit Name/Surname/Phone inside a tenant UI, it should still end up updating the global identity (host) and then replicate/sync.
    • If you observe that updating from a tenant’s MyAccount page changes only that tenant row and does not propagate, that typically means you’re not actually using the shared-user invitation/membership flow (or you have pre-existing duplicated isolated users from before switching). In that case, ABP considers them different users, so there is nothing to sync.
  • About phone number and 2FA code destination:

    • Because 2FA is managed at Host level, there should be one effective phone number / 2FA setup for the user (global identity).
    • If you end up with “different phone numbers per tenant row”, you’re in an inconsistent state for Shared Accounts; ABP’s design expectation is that security data is global, so the code should be sent using the global (host) identity’s configured provider/phone.
    • So the correct answer to “if different numbers exist, which one receives the code?” is: it should be the host/global one. If your database shows different values, that indicates those rows are not being treated as a single shared identity (or they were created as separate users previously).
  • What you should do to get the correct behavior end-to-end:

    1. Keep TenantUserSharingStrategy.Shared enabled (you already did).
    2. Stop creating “same email/username” users separately inside each tenant.
    3. Use Invite User to bring the same person into multiple tenants.
    4. If you already have duplicated “isolated” users (same email created separately per tenant before Shared), you must clean/merge them; they won’t auto-sync because they were never the same shared identity in the first place.

Links (validated):

  • https://abp.io/docs/10.6/modules/account/shared-user-accounts
  • https://abp.io/docs/latest/modules/account/shared-user-accounts
  • https://abp.io/community/articles/mf3bkg79
  • https://abp.io/docs/latest/framework/architecture/multi-tenancy
  • https://abp.io/docs/latest/modules/identity/two-factor-authentication

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.

  • With TenantUserSharingStrategy.Shared, it is expected to see multiple rows in AbpUsers for the same person (Host + each tenant). ABP keeps these rows synchronized automatically (it’s not a “single physical row shared by all tenants”). This design is required especially for scenarios like database-per-tenant, where a single shared row is not possible.
  • The record that should be treated as the primary (global) identity is the Host user (TenantId = NULL). Security settings like 2FA are handled at the Host side, so 2FA works consistently even though there are replicated tenant rows.

To enable and use Shared User Accounts correctly (and get the syncing behavior), do all of the following:

  1. Enable the strategy in your module (typically in *.Domain or *.HttpApi.Host module where you configure multi-tenancy):
using Volo.Abp.MultiTenancy;

Configure<AbpMultiTenancyOptions>(options =>
{
    options.IsEnabled = true;
    options.UserSharingStrategy = TenantUserSharingStrategy.Shared;
});
  1. Use the shared-user workflow to add a user to another tenant:

    • Don’t “create the same user again” inside each tenant.
    • Instead, from a tenant admin UI, invite the existing user to the tenant (Shared strategy is built around invitations/tenant membership).
    • After acceptance, ABP creates/maintains the tenant-side replicated row and keeps it synced.
  2. Update user profile data from the Host user account (TenantId=NULL)

    • Since Host is the global identity, changes made there are what gets synchronized.
    • If you edit user data in a tenant-specific context and it’s not syncing, you’re likely updating the tenant replica in a way that bypasses the shared-user synchronization flow.
  3. If you migrated an existing database (created users before switching), clean up duplicates created the “isolated way”

    • If the same email/username was created as separate users per tenant before enabling Shared, you must resolve/merge those accounts; otherwise you’ll keep seeing “independent” users that won’t magically unify.

More details and the intended behavior/workflow are described here:

  • https://abp.io/docs/10.6/modules/account/shared-user-accounts
  • https://abp.io/community/articles/mf3bkg79
  • https://abp.io/docs/latest/framework/architecture/multi-tenancy

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 (with OpenIddict/IdentityServer-style JWT access tokens), you generally can’t “invalidate” an already-issued JWT access token server-side unless your APIs introspect tokens (reference tokens) or you keep a server-side denylist and check it on every request. A JWT is self-contained and remains valid until it expires.

  • The recommended approach to “log the user out everywhere” is:

    1. Revoke the user’s refresh token(s) (so no new access token can be obtained), and
    2. Sign the user out of the browser session (cookie), and
    3. Keep short access token lifetime so existing access tokens die quickly.
  • If your goal is “call something from the app server and force the browser user to be logged out”:

    • Calling /connect/revocation from your app server can revoke refresh tokens (depending on your OpenIddict/IdentityServer configuration), but it does not immediately invalidate an already-issued JWT access token that the SPA is currently using.
    • To make the browser UI reflect logout immediately, you must also end the cookie session (server-side sign-out) and have the client clear its stored tokens / trigger re-authentication.
  • If you truly need immediate access token invalidation, use access token introspection (so the API asks the auth server if the token is still active on each call). ABP support confirms using token introspection to address token/session desync scenarios:

    • Enable introspection in your API authentication configuration (exact code depends on your ABP version and hosting model), then revoking the token on the auth server can take effect immediately at the API.

Sources (validated):

  • https://abp.io/support/questions/5071
  • https://github.com/abpframework/abp/pull/15876
  • https://github.com/abpframework/abp/pull/16504

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 an ABP Blazor Server app there isn’t an ABP-specific “idle timeout” setting for the server itself. The disconnect/idle behavior is governed by ASP.NET Core Blazor Server / SignalR circuit settings.
  • Practically, the “idle timeout” people hit in Blazor Server is: how long the server keeps a disconnected client circuit (e.g., user closes laptop / network drops). After that period, the circuit is disposed and the user must reconnect and UI state is lost.

Where to configure it (in your Blazor Server host):

// in ConfigureServices
services.AddServerSideBlazor().AddCircuitOptions(options =>
{
    // How long to keep a disconnected circuit in memory
    options.DisconnectedCircuitRetentionPeriod = TimeSpan.FromMinutes(3); // example
});

Default value

  • The default for DisconnectedCircuitRetentionPeriod is 3 minutes (Blazor Server default).

If you meant a different “idle timeout” (e.g., authentication cookie/session expiration or ABP’s Idle Session Timeout feature in Settings), that is a different mechanism and not the Blazor Server circuit timeout.


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 specific Swagger error means ASP.NET Core has two GET actions that end up with the exact same route template under ABP’s Auto API Controllers conventions.
  • In your case, after replacing BlogPostPublicAppService, ABP’s conventional routing ends up mapping both:
    • GetAsync(...) and
    • GetListAsync(...) to the same path: GET /api/app/voxo-blog-post-public

That can happen if GetAsync is not considered to have an id route parameter by the conventional route builder (e.g., its parameter name/type doesn’t match what the convention expects), so ABP doesn’t add /{id} and both methods collapse to the same route.

Solution (definitive): explicitly define HTTP routes for the conflicting methods in your replacement

Override the methods you want (even if you keep the base logic) and add explicit ASP.NET Core route attributes so they can’t collapse to the same path.

Example pattern:

using Microsoft.AspNetCore.Mvc;

public class VoxoBlogPostPublicAppService : BlogPostPublicAppService
{
    public VoxoBlogPostPublicAppService(
        IBlogRepository blogRepository,
        IBlogPostRepository blogPostRepository,
        ITagRepository tagRepository,
        BlogPostManager blogPostManager)
        : base(blogRepository, blogPostRepository, tagRepository, blogPostManager)
    {
    }

    [HttpGet]
    [Route("api/app/voxo-blog-post-public/{id}")]
    public override Task<BlogPostPublicDto> GetAsync(Guid id)
    {
        return base.GetAsync(id);
    }

    [HttpGet]
    [Route("api/app/voxo-blog-post-public")]
    public override Task<PagedResultDto<BlogPostPublicDto>> GetListAsync(BlogPostPublicGetListInput input)
    {
        return base.GetListAsync(input);
    }
}

Key points:

  • Force GetAsync to be .../{id} and GetListAsync to be the collection route.
  • This avoids any ambiguity/bug/edge-case in conventional route calculation after replacement.

Why [ExposeServices(typeof(IBlogPostPublicAppService))] didn’t change it

Because the problem is not “two controllers exist”; it’s “two actions resolve to the same HTTP method + path” for the generated controller of the service type that ends up exposed. Replacing DI alone doesn’t guarantee the auto-controller route templates remain unique after conventions are applied.

Sources (validated)

  • https://abp.io/docs/10.5/framework/api-development/auto-controllers

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.

  • BlogPostPublicAppService is exposed as an HTTP API via Auto API Controllers. When you add your derived class in the Application layer, ABP will treat it as a new application service and (by convention) it will also be turned into an API controller.
  • With your attributes you also replace the original service in DI, but you still end up with two actions that Swagger sees under the same controller/route group, so Swashbuckle detects a conflict.

In your specific case, both of these methods end up being mapped to the same conventional route:

  • GetAsync(...) → GET /api/app/voxo-blog-post-public
  • GetListAsync(...) → GET /api/app/voxo-blog-post-public

Swashbuckle then throws:

Conflicting method/path combination "GET api/app/voxo-blog-post-public" …

This happens because Auto API Controller conventional routing requires unique GET routes, and ABP’s conventional routing is based on method names/signatures. If the method parameters don’t let ASP.NET Core distinguish the templates (e.g., no {id} segment ends up in the template for one of them), you’ll get exactly this kind of Swagger collision.

Fix (recommended): don’t expose your replacement type as an API controller

Keep the replacement only in DI, and ensure only one controller is generated.

Use only the interface/base service exposure, and do not expose the concrete type itself:

[Dependency(ReplaceServices = true)]
[ExposeServices(typeof(IBlogPostPublicAppService))]
public class VoxoBlogPostPublicAppService : BlogPostPublicAppService
{
    public VoxoBlogPostPublicAppService(
        IBlogRepository blogRepository,
        IBlogPostRepository blogPostRepository,
        ITagRepository tagRepository,
        BlogPostManager blogPostManager)
        : base(blogRepository, blogPostRepository, tagRepository, blogPostManager)
    {
    }
}

Notes:

  • Remove typeof(BlogPostPublicAppService) and typeof(VoxoBlogPostPublicAppService) from [ExposeServices].
  • This way ABP still resolves IBlogPostPublicAppService to your class, but the framework won’t treat your class as an additional HTTP endpoint “service type” to generate routes for.

Alternative fix: explicitly set routes (if you must expose the class)

If you really want the class to be treated as a separate API controller, you must ensure GetAsync and GetListAsync do not map to the same GET path by explicitly adding [HttpGet] / [Route] attributes (or changing method names). But in an override/replacement scenario, that’s usually not what you want.


Links (verified):

  • https://abp.io/docs/10.5/framework/architecture/modularity/extending/customizing-application-modules-overriding-services
  • https://abp.io/docs/10.5/framework/api-development/auto-controllers

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.

  • This is a known limitation/bug pattern of LeptonX’s FormSelect enhancement: it builds a non-Blazor-owned wrapper (.custom-select-wrapper) around the native <select> and (as you observed) it doesn’t reliably clean up when the underlying <select> is removed/re-rendered by a SPA renderer (like Blazor). The result is exactly what you measured: orphaned wrappers accumulating and duplicated/invalid combobox UI.

  • Workaround (client-side cleanup on removal): add your own DOM-removal hook that deletes the LeptonX wrapper when the underlying <select> leaves the DOM.

    • If you’re on ABP’s MVC/Razor Pages UI you can use ABP’s DOM API abp.dom.onNodeRemoved(...) (it’s based on MutationObserver) and remove .custom-select-wrapper nodes that no longer contain a <select> (or whose aria-labelledby target is missing).
    • For Blazor, the simplest robust approach is the same concept: observe removals and delete wrappers that have lost their <select>.

    Example “orphan wrapper” cleanup logic (plain JS; safe to run globally):

// Run once on app startup
  const cleanupOrphanLeptonSelectWrappers = () => {
    document.querySelectorAll(".custom-select-wrapper[data-lpx-bound='true']").forEach(w => {
      const select = w.querySelector("select");
      if (!select) {
        w.remove();
        return;
      }

      // Optional: also remove if aria-labelledby points to missing element
      const display = w.querySelector(".custom-select-display[aria-labelledby]");
      if (display) {
        const id = display.getAttribute("aria-labelledby");
        if (id && !document.getElementById(id)) {
          w.remove();
        }
      }
    });
  };

  const obs = new MutationObserver(muts => {
    // If anything removed, run cleanup
    if (muts.some(m => m.removedNodes && m.removedNodes.length)) {
      cleanupOrphanLeptonSelectWrappers();
    }
  });

  obs.observe(document.body, { childList: true, subtree: true });
  • Workaround (avoid enhancement for Blazor-owned selects):

    • Don’t use the CSS class that LeptonX targets (you mentioned select.form-select). If you can render selects without form-select, LeptonX won’t wrap them.
    • Or replace the control with a Blazor component/select implementation that doesn’t rely on LeptonX’s overlay.
  • If you need a forced rebind/rebuild (when Blazor reuses DOM in-place): your “tear down wrapper then call leptonx.FormSelect.processSelect(select)” approach is consistent with the other reported LeptonX stale-wrapper issues. In practice:

    1. remove existing .custom-select-wrapper containing the select
    2. re-run processSelect(select) after the render cycle

    That avoids duplication and stale markup.

  • Product fix: there is no public/verified documentation for a dispose/destroy API on leptonx.FormSelect in your version, so a theme-side fix would need to add a removed-nodes path (or explicit destroy) in LeptonX.

Sources (verified):

  • https://abp.io/docs/10.5/framework/ui/mvc-razor-pages/javascript-api/dom

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.

  • There is no supported way (in ABP 10.4.x) to make abp install-libs work with Yarn Berry. ABP’s guidance for its frontend tooling is to use Yarn Classic (v1.22+) (not v2+), so the CLI behavior you’re seeing is consistent with the supported tooling expectations.
  • For an Angular UI solution, abp install-libs is not required (and in your case, it’s actively harmful because it can rewrite yarn.lock to v1 format). You can remove it from your setup script. The install-libs command is for installing/copying web (JS/CSS) libraries into wwwroot/libs for MVC/Razor Pages/Blazor Server style UIs; Angular manages its dependencies via its own package manager workflow (yarn install, yarn start/build) and does not need that wwwroot/libs step.

Practical recommendation for your repo:

  1. Delete abp install-libs from your onboarding/build scripts for the Angular project.
  2. Keep your Yarn Berry/Corepack workflow only:
corepack prepare yarn@4.12.0 --activate
   yarn install
  1. (Optional hardening) Add a CI check to fail if yarn.lock is rewritten to a v1 header format, to prevent silent regressions.

Source (ABP prerequisite explicitly recommending Yarn Classic, not v2+):

  • https://abp.io/docs/latest/get-started/pre-requirements

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.

Showing 41 to 50 of 1374 entries
Boost Your Development
ABP Live Training
Packages
See Trainings
Mastering ABP Framework Book
The Official Guide
Mastering
ABP Framework
Learn More
Mastering ABP Framework Book
Made with ❤️ on ABP v10.8.0-preview. Updated on October 21, 2026, 11:09
1
ABP Assistant
🔐 You need to be logged in to use the chatbot. Please log in first.