AbpExt.TenantAwareIdentityClientConfiguration 1.0.3

AbpExt.TenantAwareIdentityClientConfiguration

NuGet NuGet Downloads

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

  1. Host Mode: Uses IdentityClients:Default configuration
  2. Tenant Mode:
    • First tries TenantIdentityClients:{tenantName}:Default
    • Falls back to IdentityClients:Default if not found
    • Tenant name is resolved via ABP's ITenantStore

🛡️ 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.

Version Downloads Last updated
1.0.3 38 09/29/2025
1.0.2 6 09/29/2025
1.0.1 18 09/26/2025
1.0.0 433 07/01/2025