Yes, this is expected by default when you use self-contained JWT access tokens with ABP (OpenIddict) and your APIs validate them locally (signature + exp, etc.).
Logging out from the Angular app typically:
Recommended way to make logout invalidate access immediately: switch to a model where the API can detect revocation server-side, e.g. token introspection / reference tokens (or otherwise force the API to consult a server-side store on each request).
options.IntrospectAccessToken(); in the API authentication configuration (exact placement depends on your host/API project setup).About your considered options:
Practical guidance (secure-by-design):
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.
In ABP v10.7.0, the Chat module UI does not have a built-in integration point/setting to automatically use Account Pro profile pictures for avatars (contact list + conversation header). With the publicly verifiable material available here, there is no documented configuration switch or extension point that enables this behavior without overriding/replacing UI components/pages.
The good news is: Account Pro already exposes an HTTP API endpoint to retrieve a user’s profile picture, so technically the Chat UI can be adjusted to call that endpoint and fall back to initials when there is no picture:
GET /api/account/profile-picture-file/{userId} (returns IRemoteStreamContent)Therefore, the practical options today are:
UI override/customization (recommended path with current capabilities): override the Chat module’s Blazor components that render avatars and update them to:
<img> using the Account profile-picture endpoint URL for that userThis still involves overriding module UI pieces, but it can be done at the component level (not necessarily “replace the entire Chat page”), depending on how Chat is structured in your solution’s source.
Get Chat module source and implement it in the module itself (then you’ll need to maintain the delta across updates):
abp get-source Volo.ChatIf you want this supported directly by the module (so you don’t maintain UI overrides), that’s currently a product feature request for the commercial Chat module (i.e., the module would need to ship avatar support + a toggle, as you suggested).
(Links are validated below.)
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 Blazor Web App / Server prerendered (InteractiveAuto / first full load), the page can render before the browser-side “system theme (prefers-color-scheme)” detection runs.
In that short window, LeptonX may already look dark (because it is mostly CSS-variable driven), while MudBlazor still uses its default light palette, so grids/lists/content surfaces appear white until the client finishes initialization.
Fix: bind MudBlazor’s dark mode to LeptonX’s actual resolved style and re-render after the client applies it (so MudBlazor doesn’t stay with the initial light palette).
MudThemeProvider and you control IsDarkMode:@* MainLayout.razor (or your app layout) *@
@using MudBlazor
<MudThemeProvider IsDarkMode="_isDarkMode" />
<MudDialogProvider />
<MudSnackbarProvider />
@Body
@code {
private bool _isDarkMode;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (!firstRender)
return;
// Read the resolved theme from the DOM AFTER LeptonX JS applies "System" (light/dark)
_isDarkMode = await JS.InvokeAsync<bool>("abpLeptonXMud.isDarkMode");
StateHasChanged(); // forces MudBlazor to re-render with correct palette
}
[Inject] IJSRuntime JS { get; set; } = default!;
}
/wwwroot/abpLeptonXMud.js:window.abpLeptonXMud = {
isDarkMode: function () {
// LeptonX applies theme on the <html> element using Bootstrap's data attribute.
// Dark mode => data-bs-theme="dark"
const theme = document.documentElement.getAttribute('data-bs-theme');
return theme === 'dark';
}
};
Once MudThemeProvider gets the correct IsDarkMode value after the first client render, MudBlazor components (MudDataGrid rows, MudList, surfaces, etc.) will switch to the dark palette consistently, including on first visit / full reload.
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.
TenantUserSharingStrategy.Shared), the supported/standard way to add a host-created (global) user into a tenant is the invitation flow. Joining a tenant is designed to happen by accepting an invitation (and roles can be pre-assigned at invitation time).To mass-associate users during migration (e.g., in SaasDataSeedContributor), the verified approach is:
This still uses the official invitation/accept mechanism, but removes the operational burden of SMTP.
I’m not including exact service/class names for “create invitation” from code because I couldn’t verify them from source in the available repositories here. The official docs confirm the behavior, but don’t expose the internal API surface in the snippet.
Replace IIdentityEmailSender to not send and instead log/persist the invite link. This is a verified approach from an ABP support ticket (used when SMTP is blocked). You can then have your migration/admin process consume those links (or forward them via internal tooling).
If your goal is truly “no user interaction at all”, that would require calling the internal “accept invitation” logic directly (or manipulating invitation/tenant-user tables), but I don’t have a verified source for the required public API or the correct persistence model to recommend doing that safely.
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 happens because the distributed handler (AccountIdentityUserCreatedEventHandler) runs in the RabbitMQ consumer pipeline without an ambient ABP Unit of Work, but it calls Identity token generation (AbpSingleActiveTokenProvider.GenerateAsync(...)), which needs an EF Core DbContext. Without a UOW, ABP throws:
A DbContext can only be created inside a unit of work!
Fix: ensure the handler method is executed inside a Unit of Work via interception:
[UnitOfWork]virtual (required for ABP’s dynamic proxy interception)using Volo.Abp.Uow;Example:
using System.Threading.Tasks;
using Volo.Abp.Uow;
public class AccountIdentityUserCreatedEventHandler
{
[UnitOfWork]
public virtual async Task HandleEventAsync(IdentityUserCreatedEto eventData)
{
// existing handler code (no other changes needed)
}
}
Why the virtual matters: ABP applies [UnitOfWork] through dynamic proxy interceptors. If the method isn’t virtual, the interceptor won’t run, so no UOW is created, and token generation fails. This matches the behavior you observed and also the known pattern from ABP support Q&A.
Alternative (works but usually not necessary if interception works): manually begin a UOW inside the handler:
using var uow = _unitOfWorkManager.Begin();
// ...
await uow.CompleteAsync();
But the recommended approach is the attribute + virtual so the infrastructure manages it consistently.
Links checked:
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 Commercial Chat module, the Chat → Messaging → Searching permission is used to control whether a user is allowed to search / start a new conversation with arbitrary users (i.e., users they don’t already have a conversation/contact relationship with).
So the expected behavior for a user without this permission should be:
AbpRemoteCallException: Forbidden that breaks the Blazor page) is a client-side unhandled exception and is not desirable.Practically, you can treat what you’re seeing as a UI/client robustness bug in the Chat Blazor UI: the backend authorization is correctly enforced, but the Blazor page is not catching/handling AbpRemoteCallException for that action.
Workaround (recommended in your app until it’s fixed upstream):
IAuthorizationService / IPermissionChecker / AuthorizeView).SendMessageAsync call in the page/component and handle AbpRemoteCallException (status 403) by showing a message and reverting UI state instead of letting it bubble and kill the circuit.Example pattern (you’ll need to adapt names to your page code-behind):
try
{
await ConversationClientProxy.SendMessageAsync(input);
}
catch (Volo.Abp.Http.Client.AbpRemoteCallException ex)
{
// Forbidden
if (ex.HttpStatusCode == System.Net.HttpStatusCode.Forbidden)
{
// Show a UI message/toast and prevent the page from breaking
// e.g. await MessageService.Error(L["YouAreNotAllowedToStartConversation"]);
return;
}
throw; // keep other errors visible
}
(Links omitted because I couldn’t retrieve repository sources via the available tools in this environment to validate exact implementation details/URLs.)
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 behavior is caused by authorization on the Chat settings application service being enforced at class level (requires Chat.SettingManagement). Since the Chat page initializes by calling SettingsAppService.GetAsync, the whole page fails to initialize for users who only have Chat.Messaging (Conversation → Messages) and they get redirected to Access Denied.
In ABP/Blazor, the most practical workaround (without modifying the commercial module source) is to override/replace the Chat page component and avoid calling the settings endpoint unless the user has the Setting Management permission.
Volo.Chat.Blazor.MudBlazor (same technique used to override built-in module UI components).OnInitializedAsync, check permission before calling SettingsAppService.GetAsync:
Chat.SettingManagement: load settings (admin UI behavior)Example pattern (conceptual):
public override async Task OnInitializedAsync()
{
// ... other init
if (await AuthorizationService.IsGrantedAsync("Chat.SettingManagement"))
{
Settings = await SettingsAppService.GetAsync();
}
// continue loading conversations/messages
}
This keeps messaging access independent from settings management while preserving settings features for authorized users.
If you have a customization point (e.g., you can replace the settings app service), provide a read-only settings endpoint that is authorized with Chat.Messaging (or no permission if settings are not sensitive), and make the UI call that endpoint instead. However, this requires backend customization/replacement and is typically more invasive than the UI override.
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 v10.7, the AI Management MudBlazor chat widget (ChatClientChat) ultimately relies on a MudBlazor text input for composing the message. In MudBlazor v9, AutoGrow is removed, and if the input is not explicitly configured to be multi-line, MudBlazor will render a single-line <input> (so Shift+Enter can’t insert a newline, because there is no <textarea>).
Fix: ensure the message composer is rendered as a textarea by configuring it as multi-line. In MudBlazor, the reliable way is to set Lines (or equivalent multi-line setting) on the underlying MudTextField used for the message input.
ChatClientChat as-is and can’t change its internal markup, the only definitive fix is to override/replace the component (copy source into your project) and update the message field to be multi-line (i.e., Lines="X" so it becomes a <textarea>). After that, Shift+Enter will work normally (and Enter handling can still be kept for “send” logic).Notes for MudBlazor v9 migration:
AutoGrow anymore (it becomes a stray autogrow="" attribute, exactly as you observed).InputSizing.Auto is about sizing behavior; it does not automatically convert a single-line input to a textarea. You still must configure the field as multi-line.Minimal example of what the message editor should look like inside the chat component (the key part is Lines="3"; increase as needed):
<MudTextField @bind-Value="_message"
Lines="3"
Placeholder="Type a message..."
Immediate="true" />
Sources:
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.
Fix (recommended): keep filtering and selection state in one place, and re-apply the filter after every navigation/change
NavigationManager.LocationChanged.StateHasChanged).Minimal pattern (put into your custom menu component/code-behind; adapt names to your project):
@inject NavigationManager NavigationManager
@code {
private string _filterText = string.Empty;
protected override void OnInitialized()
{
NavigationManager.LocationChanged += OnLocationChanged;
}
private void OnLocationChanged(object? sender, Microsoft.AspNetCore.Components.Routing.LocationChangedEventArgs e)
{
// Re-apply filter after navigation so "active item" logic can't re-insert items
ApplyFilter();
InvokeAsync(StateHasChanged);
}
private void ApplyFilter()
{
// IMPORTANT:
// Rebuild the rendered menu items *only* from your filter result.
// Do not let rendering also pull in "active item" from the original full menu tree.
//
// Pseudocode:
// FilteredMenu = FullMenu.FilterByDisplayName(_filterText);
}
public void Dispose()
{
NavigationManager.LocationChanged -= OnLocationChanged;
}
}
Alternative behavior (if you want LeptonX’s “active item is always visible”):
Links:
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’re seeing is a real EF Core optimistic-concurrency race inside the OpenIddict token persistence flow (typically: two parallel operations touching the same token(s), e.g., refresh-token reuse/redeem + revoke-by-authorization). EF Core then throws DbUpdateConcurrencyException, and ABP wraps it as AbpDbConcurrencyException.
The problematic part in your case is that the ABP exception is not being converted back into OpenIddict’s expected OpenIddictExceptions.ConcurrencyException, so it can bubble up and be logged as an application error (and sometimes surface as unhandled if it escapes the /connect/* pipeline).
There is a closely related, confirmed ABP issue in the /connect pipeline where the request UOW could complete after the response is flushed, causing timing/race problems around OpenIddict/Identity Session persistence; ABP fixed that by completing the request UOW before flushing /connect responses (included in later versions; PR below). While that specific ticket is about AbpSessions, it demonstrates ABP has been actively fixing /connect-pipeline timing/race behaviors, and your symptoms (fixed “minute marks”, steady background traffic, and concurrency exceptions in OpenIddict store) fit the same class of problems: background/automated clients + token rotation/revocation + concurrent writes.
Verified fix exists for /connect request timing (commit-before-flush) and is merged here:
If you can, the most definitive mitigation is:
If you cannot upgrade immediately, the practical workaround pattern (already validated in ABP support) is to force the relevant OpenIddict work to be committed inside a new UnitOfWork earlier in the OpenIddict event pipeline (so the DB state is consistent/visible before other concurrent operations proceed). The support ticket shows this pattern for identity sessions; you can apply the same “commit now” idea to the part of the pipeline that creates/updates tokens in your scenario.
A second (often necessary) mitigation is at the client/API gateway level:
AbpOpenIddictTokenStore.UpdateAsync/CreateAsync translate to OpenIddictExceptions.ConcurrencyException?Yes: OpenIddict’s server pipeline expects concurrency failures from the store to be surfaced as OpenIddictExceptions.ConcurrencyException so it can translate them into protocol-appropriate responses (typically invalid_grant for refresh flows). If ABP lets AbpDbConcurrencyException escape, it will be logged as an infrastructure error and can leak as 500/unhandled depending on where it’s caught.
I couldn’t verify the exact AbpOpenIddictTokenStore implementation in source because the code-search tool backend was unavailable in this environment, so I can’t point to the exact catch/translate location in ABP 10.4.1. However, based on the observed behavior and OpenIddict conventions, this translation is the correct behavior and aligns with how OpenIddict stores are expected to report concurrency issues.
The fixed minute marks strongly suggest a scheduled/non-human client (mobile app token refresh loop, kiosk, integration service, health-check identity probe, etc.) rather than ABP’s own token pruning:
UPDATE ... WHERE AuthorizationId = @id) matches a “revoke tokens by authorization” style operation, which is commonly triggered by refresh token rotation/reuse detection or authorization revocation flows.What to do operationally:
(client_id, subject) for refresh endpoint).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.