I have implemented SSO in a Next.js site using ABP.io, but when I call the API endpoints, I get a 401. Is there any doc on how to call an endpoint with the SSO token? thanks
I created new applications under OpenID > Applications, and SSO is working correctly. For example, I can sign in to my Next.js application using abp.io accounts.
Now I’d like to update the flow so that when users click the SSO sign-in button from the Next.js application, the abp.io site automatically switches to a specific tenant—for example, the tenant “NextJs”—when the user lands on the /Account/Register page.
I noticed that the Next.js app invokes /connect/authorize?client_id=Next_app. So I’m wondering: can we add server-side logic in abp.io to automatically switch to the “NextJs” tenant when client_id equals Next_app?
Is this a good solution? If so, how should it be implemented? Or is there a better recommended approach for tenant resolution in this case?
Is there a way disable dark theme? I have removed the dark/light theme swtich and set up the light theme as default, however, when I was testing the project locally, it shows in dark theme, is there a way to force the app to use light theme only?
I am creating a react app and use the SSO, it worked locally, however, the production env gives me this error
{ "error": "invalid_grant", "error_description": "The specified token is invalid.", "error_uri": "https://documentation.openiddict.com/errors/ID2004" }
from Request URL Request Method POST
any suggestions
For privacy protection of tenants, we cannot display the tenant list on the login and registration pages. A possible solution:
Registration: Always start with a link that contains tenant info. For example, send the user a link with the tenant embedded.
Login: Iterate through all tenants to check whether the email exists in any tenant. If a matching username/email + password combination is found in a tenant, log the user in.
If the same credentials are valid in multiple tenants, prompt the user to choose which tenant to log into.
any implementation suggestions?
Works correctly in production server, but in local development environment, siteverify always returns a low score, causing validation to fail with error message: ** Verification failed, score below threshold**
I have added localhost in the domain list on the google reCAPTCHA key page
I have a hangfilre job: public class HourlyBackgroundWorker : HangfireBackgroundWorkerBase { private readonly ILogger<HourlyBackgroundWorker> _logger; private readonly IUserFileFeedbackService _userFileFeedbackService; private readonly IDataConnectionUserAnswerService _dataConnectionUserAnswerService; private readonly IDataConnectionUserAnswerRepository _dataConnectionUserAnswerRepository; private readonly IFeedbackFilesUpdaterService _feedbackFilesUpdaterService;
public HourlyBackgroundWorker(ILogger<HourlyBackgroundWorker> logger,
IUserFileFeedbackService userFileFeedbackService,
IDataConnectionUserAnswerService dataConnectionUserAnswerService,
IDataConnectionUserAnswerRepository dataConnectionUserAnswerRepository,
IFeedbackFilesUpdaterService feedbackFilesUpdaterService)
{
RecurringJobId = nameof(HourlyBackgroundWorker);
CronExpression = Cron.Hourly();
_logger = logger;
_userFileFeedbackService = userFileFeedbackService;
_dataConnectionUserAnswerService = dataConnectionUserAnswerService;
_dataConnectionUserAnswerRepository = dataConnectionUserAnswerRepository;
_feedbackFilesUpdaterService = feedbackFilesUpdaterService;
}
public override async Task DoWorkAsync(CancellationToken cancellationToken = default)
{
using var uow = LazyServiceProvider.LazyGetRequiredService<IUnitOfWorkManager>().Begin();
_logger.LogInformation("Hangfire:HourlyBackgroundWorker::UpdateFeedbacks");
await _feedbackFilesUpdaterService.UpdateAllFeedbacksAsync();
}
Is Hangfire multi-tenant aware by default? does this hourly job runs for each tenant? i.e. runs once per tenant?
I need to use locking
I wrote a function and then called it three time in order to test it
cs
public async Task<IActionResult> InsertFormAnswerJson(Guid id, [FromBody] JsonElement json)
{
var key = $"test-{id}";
await _submissionLockCache.GetOrAddAsync(key, async () =>
{
// Simulate some processing
await Task.Delay(100);
return "test-value";
}, () => new DistributedCacheEntryOptions
{
AbsoluteExpiration = Constants.RedisConstants.CacheExpiration
});
var value = await _submissionLockCache.GetAsync(key);
var jsonString = json.GetRawText();
var currentUserId = _currentUser.Id;
if (currentUserId == null)
{
return Unauthorized();
}
var lockKey = $"form-submission-lock:{currentUserId}:{id}";
await using var handle =
await _distributedLock.TryAcquireAsync(lockKey,TimeSpan.FromSeconds(1));
if (handle != null)
{
try
{
_logger.LogInformation($"form-submission:{currentUserId}:{id}");
await _resultAnswerService.SaveUserAnswerAsync(id, currentUserId.Value, jsonString);
return Ok();
}
catch (Exception ex)
{
return StatusCode(500, "An error occurred while processing your submission.");
}
}
_logger.LogInformation($"form-submission[duplicate]:{currentUserId}:{id}");
return StatusCode(429, "Duplicate submission detected. Please wait a moment before retrying.");
}
the locking didnt work
I have a line on the top _submissionLockCache.GetOrAddAsync to test the redis, so the redis is working properly , and the lock timeout should be just 1 sec.
2025-07-17 14:15:09.180 -07:00 [Information] Route matched with "{action = \"InsertFormAnswerJson\", controller = \"DataConnectionForm\", area = \"\", page = \"\"}". Executing controller action with signature "System.Threading.Tasks.Task`1[Microsoft.AspNetCore.Mvc.IActionResult] InsertFormAnswerJson(System.Guid, System.Text.Json.JsonElement)" on controller "Tapp.Module.DataHub.Web.Controllers.DataConnections.DataConnectionFormController" ("Tapp.Module.DataHub.Web").
2025-07-17 14:15:25.215 -07:00 [Information] form-submission:fcc9c058-7590-4b02-f5a6-3a190bb27c6b:4ef0a401-5951-82b7-3d90-3a1adefa39ec
2025-07-17 14:15:26.529 -07:00 [Information] Start processing HTTP request "GET" "https://localhost:44381/health-status"
2025-07-17 14:15:26.529 -07:00 [Information] Sending HTTP request "GET" "https://localhost:44381/health-status"
2025-07-17 14:15:26.529 -07:00 [Information] Request starting "HTTP/1.1" "GET" "https"://"localhost:44381""""/health-status""" - null null
2025-07-17 14:15:26.532 -07:00 [Information] Executing endpoint '"Health checks"'
2025-07-17 14:15:26.711 -07:00 [Information] Executing StatusCodeResult, setting HTTP status code 200
2025-07-17 14:15:26.711 -07:00 [Information] Executed action "Tapp.Module.DataHub.Web.Controllers.DataConnections.DataConnectionFormController.InsertFormAnswerJson (Tapp.Module.DataHub.Web)" in 17530.5898ms
2025-07-17 14:15:26.711 -07:00 [Information] Executed endpoint '"Tapp.Module.DataHub.Web.Controllers.DataConnections.DataConnectionFormController.InsertFormAnswerJson (Tapp.Module.DataHub.Web)"'
2025-07-17 14:15:27.027 -07:00 [Information] Request finished "HTTP/2" "POST" "https"://"localhost:44381""""/api/data-connections/forms/4ef0a401-5951-82b7-3d90-3a1adefa39ec/answers""" - 200 null null 18225.3835ms
2025-07-17 14:15:27.213 -07:00 [Information] form-submission:fcc9c058-7590-4b02-f5a6-3a190bb27c6b:4ef0a401-5951-82b7-3d90-3a1adefa39ec
2025-07-17 14:15:27.388 -07:00 [Information] Executing StatusCodeResult, setting HTTP status code 200
2025-07-17 14:15:27.388 -07:00 [Information] Executed action "Tapp.Module.DataHub.Web.Controllers.DataConnections.DataConnectionFormController.InsertFormAnswerJson (Tapp.Module.DataHub.Web)" in 18207.493ms
2025-07-17 14:15:27.388 -07:00 [Information] Executed endpoint '"Tapp.Module.DataHub.Web.Controllers.DataConnections.DataConnectionFormController.InsertFormAnswerJson (Tapp.Module.DataHub.Web)"'
2025-07-17 14:15:27.536 -07:00 [Information] form-submission:fcc9c058-7590-4b02-f5a6-3a190bb27c6b:4ef0a401-5951-82b7-3d90-3a1adefa39ec
2025-07-17 14:15:27.625 -07:00 [Information] Request finished "HTTP/2" "POST" "https"://"localhost:44381""""/api/data-connections/forms/4ef0a401-5951-82b7-3d90-3a1adefa39ec/answers""" - 200 null null 18823.4763ms
2025-07-17 14:15:27.635 -07:00 [Information] Request starting "HTTP/2" "GET" "https"://"localhost:44381""""/""?dataconnection=4ef0a401-5951-82b7-3d90-3a1adefa39ec" - null null
2025-07-17 14:15:27.655 -07:00 [Information] Executing endpoint '"/Index"'
how can I fix it?
I read this doc https://abp.io/docs/9.2/framework//infrastructure/distributed-locking
I want to set up CI/CD with GitHub Actions to deploy your Web and public.web apps to Azure App Services, I am currently using VS publish.
and I had an old one set up before and I found some errors:
un dotnet build src/Tapp.Web/Tapp.Web.csproj --configuration Release Determining projects to restore... D:\a\tapp-9\tapp-9\src\Tapp.Domain\Tapp.Domain.csproj : warning NU1504: Duplicate 'PackageReference' items found. Remove the duplicate items or use the Update functionality to ensure a consistent restore behavior. The duplicate 'PackageReference' items are: Volo.Saas.Domain 9.1.0, Volo.Saas.Domain 9.1.0. D:\a\tapp-9\tapp-9\src\Tapp.HttpApi\Tapp.HttpApi.csproj : error NU1101: Unable to find package Volo.Abp.Identity.Pro.HttpApi. No packages exist with this id in source(s): nuget.org. PackageSourceMapping is enabled, the following source(s) were not considered: ABP Commerc ..... following source(s) were not considered: ABP Commercial NuGet Source, Microsoft Visual Studio Offline Packages. D:\a\tapp-9\tapp-9\src\Tapp.Web\Tapp.Web.csproj : error NU1101: Unable to find package Volo.Abp.LanguageManagement.Domain.Shared. No packages exist with this id in source(s): nuget.org. PackageSourceMapping is enabled, the following source(s) were not considered: ABP Commercial NuGet Source, Microsoft Visual Studio Offline Packages. D:\a\tapp-9\tapp-9\src\Tapp.Web\Tapp.Web.csproj : error NU1101: Unable to find package Volo.FileManagement.Domain.Shared. No packages exist with this id in source(s): nuget.org. PackageSourceMapping is enabled, the following source(s) were not considered: ABP Commercial NuGet Source, Microsoft Visual Studio Offline Packages. D:\a\tapp-9\tapp-9\src\Tapp.Web\Tapp.Web.csproj : error NU1101: Unable to find package Volo.Saas.Domain.Shared. No packages exist with this id in source(s): nuget.org. PackageSourceMapping is enabled, the following source(s) were not considered: ABP Commercial NuGet Source, Microsoft Visual Studio Offline Packages. D:\a\tapp-9\tapp-9\src\Tapp.Web\Tapp.Web.csproj : error NU1101: Unable to find package Volo.Abp.TextTemplateManagement.Domain.Shared. No packages exist with this id in source(s): nuget.org. PackageSourceMapping is enabled, the following source(s) were not considered: ABP Commercial NuGet Source, Microsoft Visual Studio Offline Packages. D:\a\tapp-9\tapp-9\src\Tapp.Web\Tapp.Web.csproj : error NU1101: Unable to find package Volo.Abp.Gdpr.Domain.Shared. No packages exist with this id in source(s): nuget.org. PackageSourceMapping is enabled, the following source(s) were not considered: ABP Commercial NuGet Source, Microsoft Visual Studio Offline Packages. D:\a\tapp-9\tapp-9\src\Tapp.Web\Tapp.Web.csproj : error NU1101: Unable to find package Volo.CmsKit.Pro.Domain.Shared. No packages exist with this id in source(s): nuget.org. PackageSourceMapping is enabled, the following source(s) were not considered: ABP Commercial NuGet Source, Microsoft Visual Studio Offline Packages. 2 Warning(s) 529 Error(s)
the host admin has a feature to login with a tenant admin and then switch back to the original account

I need to create a new page with the same feature, that is the host admin can see some of the tenant admin accounts, and clicking one of them will lead to the tenant site as the admin; and then on the homepage, I want to add a button says back to host site.
how can I implement it by calling the existing abp framework functions?