AbpExt.TenantAwareIdentityClientConfiguration 1.0.2
AbpExt.TenantAwareIdentityClientConfiguration
A powerful ABP Framework extension that provides tenant-aware Identity Client configuration support for multi-tenant applications. This package allows different tenants to use different authentication servers, client credentials, and authority endpoints through simple appsettings.json configuration.
🚀 Features
- ✅ Tenant-Specific Authentication: Different auth servers per tenant
- ✅ Automatic Fallback: Falls back to host configuration when tenant config unavailable
- ✅ Simple Configuration: Uses familiar appsettings.json structure
- ✅ No Database Required: Configuration stored in application settings
- ✅ High Performance: Configuration resolved from memory
- ✅ ABP Integration: Seamlessly integrates with ABP Framework multi-tenancy
- ✅ Multiple Grant Types: Supports password, client_credentials, and other OAuth flows
📦 Installation
dotnet add package AbpExt.TenantAwareIdentityClientConfiguration
🔧 Setup
1. Add Module Dependency
Add the module to your ABP module:
[DependsOn(typeof(AbpExtTenantAwareIdentityClientConfigurationModule))]
public class YourApplicationModule : AbpModule
{
// Module configuration
}
2. Configure appsettings.json
Add your configuration to appsettings.json:
{
"IdentityClients": {
"Default": {
"GrantType": "password",
"ClientId": "MyApp_Client",
"UserName": "admin",
"UserPassword": "your_password",
"Authority": "https://auth.yourdomain.com",
"Scope": "api1 api2"
}
},
"TenantIdentityClients": {
"tenant1": {
"Default": {
"GrantType": "password",
"ClientId": "Tenant1_Client",
"UserName": "tenant1_admin",
"UserPassword": "tenant1_password",
"Authority": "https://tenant1.auth.com",
"Scope": "api1 api2"
}
},
"tenant2": {
"Default": {
"GrantType": "client_credentials",
"ClientId": "Tenant2_Client",
"ClientSecret": "tenant2_secret",
"Authority": "https://tenant2.auth.com",
"Scope": "api1"
}
}
}
}
💻 Usage
Basic Usage
public class MyApplicationService : ApplicationService
{
private readonly ITenantIdentityClientService _tenantClientService;
public MyApplicationService(ITenantIdentityClientService tenantClientService)
{
_tenantClientService = tenantClientService;
}
public async Task<string> GetCurrentTenantAuthServerAsync()
{
var config = await _tenantClientService.GetConfigurationAsync();
return config.Authority;
}
}
HTTP Client Authentication Example
public class ExternalApiService : ApplicationService
{
private readonly ITenantIdentityClientService _tenantClientService;
private readonly IHttpClientFactory _httpClientFactory;
public ExternalApiService(
ITenantIdentityClientService tenantClientService,
IHttpClientFactory httpClientFactory)
{
_tenantClientService = tenantClientService;
_httpClientFactory = httpClientFactory;
}
public async Task<string> CallExternalApiAsync()
{
// Get tenant-specific configuration
var config = await _tenantClientService.GetConfigurationAsync();
// Get access token using tenant configuration
var token = await GetAccessTokenAsync(config);
// Create authenticated HTTP client
var httpClient = _httpClientFactory.CreateClient();
httpClient.DefaultRequestHeaders.Authorization =
new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
// Make authenticated request
var response = await httpClient.GetAsync("https://api.external-service.com/data");
return await response.Content.ReadAsStringAsync();
}
private async Task<string> GetAccessTokenAsync(TenantIdentityClientConfiguration config)
{
using var tokenClient = _httpClientFactory.CreateClient();
var tokenEndpoint = $"{config.Authority.TrimEnd('/')}/connect/token";
var parameters = new List<KeyValuePair<string, string>>
{
new("grant_type", config.GrantType),
new("client_id", config.ClientId),
new("scope", config.Scope)
};
if (config.GrantType == "password")
{
parameters.Add(new("username", config.UserName));
parameters.Add(new("password", config.UserPassword));
}
else if (config.GrantType == "client_credentials")
{
parameters.Add(new("client_secret", config.ClientSecret));
}
var tokenRequest = new FormUrlEncodedContent(parameters);
var response = await tokenClient.PostAsync(tokenEndpoint, tokenRequest);
var content = await response.Content.ReadAsStringAsync();
var tokenData = System.Text.Json.JsonSerializer.Deserialize<JsonElement>(content);
return tokenData.GetProperty("access_token").GetString()!;
}
}
🔧 Configuration Structure
Host Configuration
The IdentityClients section defines the default configuration used by the host and as fallback for tenants:
"IdentityClients": {
"Default": {
"GrantType": "password",
"ClientId": "Host_Client",
"UserName": "admin",
"UserPassword": "password",
"Authority": "https://auth.host.com",
"Scope": "api1 api2"
}
}
Tenant-Specific Configuration
The TenantIdentityClients section defines per-tenant overrides:
"TenantIdentityClients": {
"{tenant-name}": {
"Default": {
// Tenant-specific configuration
}
}
}
🔄 Configuration Resolution Flow
- Host Mode: Uses
IdentityClients:Defaultconfiguration - Tenant Mode:
- First tries
TenantIdentityClients:{tenantName}:Default - Falls back to
IdentityClients:Defaultif not found - Tenant name is resolved via ABP's
ITenantStore
- First tries
🛡️ Security Considerations
⚠️ Important: This package stores authentication credentials in appsettings.json. For production environments, consider:
- Using Azure Key Vault or similar secret management solutions
- Environment variables for sensitive values
- Encrypted configuration sections
- Client certificate authentication instead of password flow
- Secure key rotation strategies
🔌 Integration with ABP Dynamic HTTP Client Proxies
To integrate with ABP's dynamic HTTP client proxies, create a custom authenticator:
public class TenantAwareHttpClientAuthenticator : IRemoteServiceHttpClientAuthenticator
{
private readonly ITenantIdentityClientService _tenantClientService;
public TenantAwareHttpClientAuthenticator(ITenantIdentityClientService tenantClientService)
{
_tenantClientService = tenantClientService;
}
public async Task Authenticate(RemoteServiceHttpClientAuthenticateContext context)
{
var config = await _tenantClientService.GetConfigurationAsync();
var token = await GetAccessTokenAsync(config);
context.Request.Headers.Authorization =
new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
}
}
📋 Requirements
- .NET 8.0 or later
- ABP Framework 8.0 or later
🤝 Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
📄 License
This project is licensed under the MIT License - see the LICENSE file for details.
🏷️ Versioning
We use SemVer for versioning.
📞 Support
If you encounter any issues or have questions, please open an issue on GitHub.
No packages depend on AbpExt.TenantAwareIdentityClientConfiguration.
.NET 8.0
- Microsoft.Extensions.Configuration.Abstractions (>= 8.0.0)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 8.0.0)
- Volo.Abp.Core (>= 8.0.0)
- Volo.Abp.Http.Client (>= 8.0.0)
- Volo.Abp.IdentityModel (>= 8.0.0)
- Volo.Abp.MultiTenancy (>= 8.0.0)