To add to the answer: Do the following to have the api-only (api with no database) permissions and permission grants added. *To ensure your microservice's permissions are persisted to the central Administration database:
Reference your microservice's Application.Contracts project from the AdministrationService. This allows the central service to discover and register your permission definitions.
Rebuild the solution and restart the AdministrationService. This ensures the new permissions are loaded and can be seeded.*
Okay... I got it to work (write the permissions) with my original code. I forgot to uncomment the database config getSection lines in the module class. These were commented out originally because we don't have a database for this microservice.
in the ClientServicesQueryModule.cs
private void ConfigureCustomConnectionStringResolver(ServiceConfigurationContext context)
{
// Register the custom connection string resolver
// This will override the default ABP connection string resolver
context.Services.AddTransient<IConnectionStringResolver, CustomConnectionStringResolver>();
// Configure database connection options for strongly-typed configuration
var configuration = context.Services.GetConfiguration();
// Configure individual database connection options sections if needed
// This allows for future extensibility with strongly-typed configuration
context.Services.Configure<DatabaseConnectionOptions>("AdministrationService",
configuration.GetSection("DatabaseConfig:AdministrationService"));
// context.Services.Configure<DatabaseConnectionOptions>("IdentityService",
// configuration.GetSection("DatabaseConfig:IdentityService"));
context.Services.Configure<DatabaseConnectionOptions>("AuditLoggingService",
configuration.GetSection("DatabaseConfig:AuditLoggingService"));
context.Services.Configure<DatabaseConnectionOptions>("SaasService",
configuration.GetSection("DatabaseConfig:SaasService"));
context.Services.Configure<DatabaseConnectionOptions>("LanguageService",
configuration.GetSection("DatabaseConfig:LanguageService"));
context.Services.Configure<DatabaseConnectionOptions>("AbpBlobStoring",
configuration.GetSection("DatabaseConfig:AbpBlobStoring"));
}
in the Data folder.
public class ClientServicesQueryDataSeeder : ITransientDependency
{
private readonly ILogger _logger;
private readonly IPermissionDataSeeder _permissionDataSeeder;
public ClientServicesQueryDataSeeder(IPermissionDataSeeder permissionDataSeeder,
ILogger<ClientServicesQueryDataSeeder> logger)
{
_permissionDataSeeder = permissionDataSeeder;
_logger = logger;
}
[UnitOfWork]
public virtual async Task SeedAsync(Guid? tenantId = null)
{
_logger.LogInformation("Starting ClientServicesQuery permission seeding...");
try
{
var permissions = ClientServicesQueryPermissions.GetAll();
_logger.LogInformation("Found {PermissionCount} permissions to seed: {Permissions}",
permissions.Length,
string.Join(", ", permissions));
// Grant permissions to the "admin" role
await _permissionDataSeeder.SeedAsync(
RolePermissionValueProvider.ProviderName,
"admin",
permissions,
tenantId
);
_logger.LogInformation("Successfully seeded ClientServicesQuery permissions for admin role");
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to seed ClientServicesQuery permissions");
throw;
}
}
}
Thanks for the quick response.
This isn't clear... 'add the ClientServicesQueryContractsModule to the DependsOn of the IdentityService or ApplicationService or both'? This means that the Admin or Identity service has to reference the ClientServicesQueryContractsModule project. This tightly couples the to microservices!
Check the docs before asking a question: https://abp.io/docs/latest Check the samples to see the basic tasks: https://abp.io/docs/latest/samples The exact solution to your question may have been answered before, and please first use the search on the homepage.
Provide us with the following info:
🧐 Hint: If you are using the ABP Studio, you can see all the information about your solution from the configuration window, which opens when you right-click on the solution and click on the Solution Configuration
button.
I have a microservice project in this solution that is api only --- it has no database. I'm having trouble getting the permissions for this service saved to the administration permissions and permissionGrants tables.
under the /Data folder I have the ClientServicesQueryDataSeeder.cs If I don't have a database for the microservice, do I need to have the following:
if so, how would I configure these without a database?
Here is the code that I've added that doesn't work:
public class ClientServicesQueryDataSeeder : ITransientDependency
{
private readonly ILogger _logger;
private readonly IPermissionDataSeeder _permissionDataSeeder;
public ClientServicesQueryDataSeeder(IPermissionDataSeeder permissionDataSeeder,
ILogger<ClientServicesQueryDataSeeder> logger)
{
_permissionDataSeeder = permissionDataSeeder;
_logger = logger;
}
[UnitOfWork]
public virtual async Task SeedAsync(Guid? tenantId = null)
{
_logger.LogInformation("Starting ClientServicesQuery permission seeding...");
try
{
var permissions = ClientServicesQueryPermissions.GetAll();
_logger.LogInformation("Found {PermissionCount} permissions to seed: {Permissions}",
permissions.Length,
string.Join(", ", permissions));
// Grant permissions to the "admin" role
await _permissionDataSeeder.SeedAsync(
RolePermissionValueProvider.ProviderName,
"admin",
permissions,
tenantId
);
_logger.LogInformation("Successfully seeded ClientServicesQuery permissions for admin role");
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to seed ClientServicesQuery permissions");
throw;
}
}
}
I need guidance on how to get the permission to persist to the administration permissions databases where I do not have a database to migrate in the microservice.
Provide us with the following info:
🧐 Hint: If you are using the ABP Studio, you can see all the information about your solution from the configuration window, which opens when you right-click on the solution and click on the Solution Configuration
button.
We're using the microservice template and using the auth-server app for authentication. The auth-server application has been slightly modified for custom branding. We are running the authserver and microservices in Azure Kubernetes.
Here is the 'HostTenantResolveContributor.cs'
public class HostTenantResolveContributor : TenantResolveContributorBase
{
public override async Task ResolveAsync(ITenantResolveContext context)
{
var currentContextAccessor = context.ServiceProvider.GetRequiredService<IHttpContextAccessor>();
var memberConfigService = context.ServiceProvider.GetRequiredService<IMemberConfigService>();
if (memberConfigService != null)
{
#if DEBUG
currentContextAccessor.HttpContext.Request.Host = new HostString("auth.homefree.cloverleafcms.us"); // Set the host header to a default value for testing purposes
//currentContextAccessor.HttpContext.Request.Path = new PathString("/Account/Login"); // Set the path to root for testing purposes
#endif
string? prefix = currentContextAccessor?.HttpContext?.Request?.GetPrefixFromHost();
Console.WriteLine($"Tenant prefix after GetPrefixFromHost is: {prefix}.");
if (!string.IsNullOrEmpty(prefix) && prefix != "admin")
{
var responseDto = await memberConfigService.GetMemberConfigByUrlPrefixAsync(prefix);
if (responseDto != null && responseDto.Success)
{
var member = responseDto.MemberConfig;
context.TenantIdOrName = member?.Id.ToString();
Console.WriteLine($"Member found for prefix: {prefix}");
Console.WriteLine($"MemberId: {member?.Id.ToString()}");
}
else
{
Console.WriteLine($"Member not found for prefix: {prefix}. See details: {responseDto?.ErrorResponse?.Error?.Message}");
context.TenantIdOrName = null;
}
return;
}
else
{
Console.WriteLine("Tenant prefix not found in the host.");
context.TenantIdOrName = null;
}
}
else
{
Console.WriteLine("MemberConfigService is not available.");
context.TenantIdOrName = null;
}
}
public override string Name => "Host";
}
Here is the GetPrefixFromHost.cs extension
public static class HttpRequestExtensions
{
public static string? GetPrefixFromHost(this HttpRequest request)
{
if (request?.Host.HasValue != true)
{
return "invalid";
}
string host = request.Host.Host;
try
{
// Check for localhost
if (host.Contains("localhost"))
return null;
if (host.Contains("host.docker.internal"))
return null;
// Split the host into parts
var parts = host.Split('.');
if (parts.Length < 2)
return null;
var first = parts[0];
var second = parts[1];
if (second.Equals("admin", StringComparison.OrdinalIgnoreCase))
{
Console.WriteLine("Admin subdomain detected, returning null for prefix.");
return null;
}
if (first.Equals("auth", StringComparison.OrdinalIgnoreCase) && parts.Length >= 3)
{
// Return the second part as prefix
Console.WriteLine($"Member subdomain detected, returning {parts[1]} for prefix.");
return parts[1];
}
return "invalid"; // Return "invalid" for other cases
}
catch
{
// Return "invalid" for invalid URLs
return "invalid";
}
}
}
Project shared... GitHub invite sent.
Check the docs before asking a question: https://abp.io/docs/latest Check the samples to see the basic tasks: https://abp.io/docs/latest/samples The exact solution to your question may have been answered before, and please first use the search on the homepage.
Provide us with the following info:
🧐 Hint: If you are using the ABP Studio, you can see all the information about your solution from the configuration window, which opens when you right-click on the solution and click on the Solution Configuration
button.
6/23/2025 8:23:51 PM [Information] "Bearer" was not authenticated. Failure message: "IDX10511: Signature validation failed. Keys tried: 'Microsoft.IdentityModel.Tokens.X509SecurityKey, KeyId: '4C44506B63683FAFB81F426A6A1225833F2BE6A8', InternalId: 'TERQa2NoP6-4H0JqahIlgz8r5qg'. , KeyId: 4C44506B63683FAFB81F426A6A1225833F2BE6A8 '. Number of keys in TokenValidationParameters: '0'. Number of keys in Configuration: '2'. Matched key was in 'Configuration'. kid: '4C44506B63683FAFB81F426A6A1225833F2BE6A8'. Exceptions caught: 'PII of type 'System.Text.StringBuilder' is hidden. For more details, see [https://aka.ms/IdentityModel/PII.]'. token: 'PII of type 'Microsoft.IdentityModel.JsonWebTokens.JsonWebToken' is hidden. For more details, see [https://aka.ms/IdentityModel/PII.]'. See https://aka.ms/IDX10511 for details." 6/23/2025 8:23:51 PM [Information] Authorization failed. "These requirements were not met: PermissionRequirement: ActionItemService.ActionItems" 6/23/2025 8:23:51 PM [Information] AuthenticationScheme: "Bearer" was challenged. 6/23/2025 8:23:51 PM [Information] Request finished "HTTP/1.1" "GET" "http"://"localhost:44379""""/api/actionitem/action-items""?clientId=10BECE0B-086C-F92C-A6D1-3A1AB084E5A2" - 401 0 null 3.5956ms
Once solution is running in AbpStudio then go to Postman and get a token via the TokenService using the host admin id and password.
Create an ActionItem.
Update and ActionItem.
Get the ActionItem.
I can successfully create and update an ActionItem with the host admin credentials but I get the above 401 not authorized when calling the GET endpoint.
I have the following version of Abp Studio:
Abp Studio crashes when I open my Abp Solution file. The solution will load and after a few seconds the Abp Studio app will crash.
Here is the error log:
2025-04-22 16:23:49.781 -05:00 [WRN] The process cannot access the file 'C:\Users\xxxxx\source\repos\CloverleafCMS-Microservices-Abp-1.abpstudio\state.json' because it is being used by another process.
System.IO.IOException: The process cannot access the file 'C:\Users\xxxxx\source\repos\CloverleafCMS-Microservices-Abp-1.abpstudio\state.json' because it is being used by another process.
at Microsoft.Win32.SafeHandles.SafeFileHandle.CreateFile(String fullPath, FileMode mode, FileAccess access, FileShare share, FileOptions options)
at Microsoft.Win32.SafeHandles.SafeFileHandle.Open(String fullPath, FileMode mode, FileAccess access, FileShare share, FileOptions options, Int64 preallocationSize, Nullable1 unixCreateMode) at System.IO.File.OpenHandle(String path, FileMode mode, FileAccess access, FileShare share, FileOptions options, Int64 preallocationSize) at System.IO.File.WriteToFileAsync(String path, FileMode mode, ReadOnlyMemory
1 contents, Encoding encoding, CancellationToken cancellationToken)
at Volo.Abp.Studio.UI.Solutions.SolutionState.SolutionStateSynchronizer.CilggAsHIm(String )
at Volo.Abp.Studio.UI.Solutions.SolutionState.SolutionStateSynchronizer.<>c__DisplayClass26_0.DDK1Yhg0gnvQQJBxeNZ.MoveNext()
--- End of stack trace from previous location ---
at System.Threading.Tasks.Task.<>c.b__128_0(Object state)
at Avalonia.Threading.SendOrPostCallbackDispatcherOperation.InvokeCore()
at Avalonia.Threading.DispatcherOperation.Execute()
at Avalonia.Threading.Dispatcher.ExecuteJob(DispatcherOperation job)
at Avalonia.Threading.Dispatcher.ExecuteJobsCore(Boolean fromExplicitBackgroundProcessingCallback)
at Avalonia.Threading.Dispatcher.Signaled()
at Avalonia.Win32.Win32Platform.WndProc(IntPtr hWnd, UInt32 msg, IntPtr wParam, IntPtr lParam)
at Avalonia.Win32.Interop.UnmanagedMethods.DispatchMessage(MSG& lpmsg)
at Avalonia.Win32.Win32DispatcherImpl.RunLoop(CancellationToken cancellationToken)
at Avalonia.Threading.DispatcherFrame.Run(IControlledDispatcherImpl impl)
at Avalonia.Threading.Dispatcher.PushFrame(DispatcherFrame frame)
at Avalonia.Threading.Dispatcher.MainLoop(CancellationToken cancellationToken)
at Avalonia.Controls.ApplicationLifetimes.ClassicDesktopStyleApplicationLifetime.Start(String[] args)
at ktmpmYYhNMEr3TNPigT.jGyPv8YNF0HevUNmIxp.rf7YgLwjGG(String[] )
I'm starting the Abp Studio with administrator privileges, but it still trips the following error: The process cannot access the file 'C:\Users\xxxxx\source\repos\CloverleafCMS-Microservices-Abp-1.abpstudio\state.json' because it is being used by another process.
Thanks for the local Kubernetes test. Still I think a test via a deployed application to an Azure Kubernetes service would be a better comparison to our situation. One question still.... do you (Abp.io) have any production clients that are running abpFramework microservices template in an Azure Kubernetes Service? If so, can we find out what there metrics are for obtaining a token?