ZuriaMidi 1.1.1
dotnet add package ZuriaMidi --version 1.1.1
NuGet\Install-Package ZuriaMidi -Version 1.1.1
<PackageReference Include="ZuriaMidi" Version="1.1.1" />
<PackageVersion Include="ZuriaMidi" Version="1.1.1" />
<PackageReference Include="ZuriaMidi" />
paket add ZuriaMidi --version 1.1.1
#r "nuget: ZuriaMidi, 1.1.1"
#:package ZuriaMidi@1.1.1
#addin nuget:?package=ZuriaMidi&version=1.1.1
#tool nuget:?package=ZuriaMidi&version=1.1.1
AuthIntegrationService & IGenericApi
A clean, testable abstraction layer for integrating with external SSO (Single Sign-On) services in .NET applications. This module decouples authentication services from direct HTTP client dependencies through a generic API interface.
Table of Contents
- Overview
- Features
- Project Structure
- Installation
- Configuration
- Usage
- API Reference
- Testing
- Contributing
Overview
The AuthIntegrationService provides a robust foundation for SSO integration by abstracting HTTP operations through the IGenericApi interface. This design promotes loose coupling, testability, and maintainability while providing consistent logging and error handling.
Architecture Benefits
- Decoupled Design: Easy to swap HTTP implementations (HttpClient, Refit, etc.)
- Testability: Mock-friendly interface for unit testing
- Consistency: Centralized logging and error handling
- Extensibility: Simple to add new SSO endpoints
- Type Safety: Strongly-typed request/response models
Features
- ✅ Generic HTTP operations (GET, POST, PUT, PATCH, DELETE)
- ✅ Async/await support with cancellation tokens
- ✅ JSON serialization/deserialization
- ✅ Comprehensive error handling
- ✅ Built-in logging integration
- ✅ Dependency injection ready
- ✅ Unit test friendly
Project Structure
ZuriaMidi/
├── Features/
│ └── Authentication/
│ └── AuthIntegrationService.cs
├── Models/
│ └── Sso/ # SSO DTOs
│ ├── SsoBaseResponse.cs
│ ├── LoginRequest.cs
│ └── RegisterRequest.cs
├── Services/
│ └── AuthServices/
│ ├── IGenericApi.cs # Generic HTTP interface
│ └── HttpClientGenericApi.cs # HttpClient implementation
└── Common/
└── Config.cs # Configuration settings
Installation
Prerequisites
- .NET 5.0 or later
- System.Net.Http.Json package (included in .NET 5+)
NuGet Packages
<PackageReference Include="Microsoft.Extensions.Http" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="8.0.0" />
Configuration
1. Application Settings
Add SSO configuration to your appsettings.json:
{
"SSO": {
"BaseUrl": "https://your-sso-provider.com/api",
"EmailUrl": "https://your-sso-provider.com/api/email",
"RegisterUserUrl": "https://your-sso-provider.com/api/register",
"Timeout": "00:00:30"
}
}
2. Dependency Injection
Configure services in Program.cs:
// Register HttpClient with configuration
builder.Services.AddHttpClient<IGenericApi, HttpClientGenericApi>(client =>
{
var ssoConfig = builder.Configuration.GetSection("SSO");
client.BaseAddress = new Uri(ssoConfig["BaseUrl"]);
client.Timeout = TimeSpan.Parse(ssoConfig["Timeout"] ?? "00:00:30");
// Add default headers if needed
client.DefaultRequestHeaders.Add("Accept", "application/json");
});
// Register application services
builder.Services.AddScoped<AuthIntegrationService>();
Usage
Basic Implementation
[ApiController]
[Route("api/[controller]")]
public class AuthController : ControllerBase
{
private readonly AuthIntegrationService _authService;
private readonly ILogger<AuthController> _logger;
public AuthController(
AuthIntegrationService authService,
ILogger<AuthController> logger)
{
_authService = authService;
_logger = logger;
}
[HttpGet("check-email/{email}")]
public async Task<IActionResult> CheckEmail(string email, CancellationToken cancellationToken)
{
try
{
var result = await _authService.EmailExistOnSso(email);
return Ok(result);
}
catch (HttpRequestException ex)
{
_logger.LogError(ex, "Failed to check email existence for {Email}", email);
return StatusCode(500, "External service unavailable");
}
}
[HttpPost("register")]
public async Task<IActionResult> RegisterUser([FromBody] RegisterRequest request, CancellationToken cancellationToken)
{
try
{
var result = await _authService.RegisterUser(request);
return Ok(result);
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to register user");
return BadRequest("Registration failed");
}
}
}
Advanced Usage with Custom Headers
public class AuthIntegrationService
{
private readonly IGenericApi _ssoService;
private readonly ILogger<AuthIntegrationService> _logger;
public async Task<SsoBaseResponse> AuthenticatedRequest(string endpoint, string token)
{
// For requests requiring authentication, you might need to extend IGenericApi
// or configure the HttpClient with authentication headers
var url = $"{Config.BaseUrl}/{endpoint}";
var response = await _ssoService.GetAsync<SsoBaseResponse>(url);
return response;
}
}
API Reference
IGenericApi Interface
The core interface providing HTTP operations:
public interface IGenericApi
{
/// <summary>
/// Performs a GET request and deserializes the response
/// </summary>
Task<TResponse> GetAsync<TResponse>(string url, CancellationToken cancellationToken = default);
/// <summary>
/// Performs a POST request with JSON body
/// </summary>
Task<TResponse> CreateAsync<TResponse, TRequest>(string url, TRequest body, CancellationToken cancellationToken = default);
/// <summary>
/// Performs a PUT request with JSON body
/// </summary>
Task<TResponse> UpdateAsync<TResponse, TRequest>(string url, TRequest body, CancellationToken cancellationToken = default);
/// <summary>
/// Performs a PATCH request with JSON body
/// </summary>
Task<TResponse> PatchAsync<TResponse, TRequest>(string url, TRequest body, CancellationToken cancellationToken = default);
/// <summary>
/// Performs a DELETE request
/// </summary>
Task<TResponse> DeleteAsync<TResponse>(string url, CancellationToken cancellationToken = default);
/// <summary>
/// Performs a DELETE request with JSON body
/// </summary>
Task<TResponse> DeleteAsync<TResponse, TRequest>(string url, TRequest body, CancellationToken cancellationToken = default);
}
AuthIntegrationService Methods
public class AuthIntegrationService
{
/// <summary>
/// Checks if an email exists in the SSO system
/// </summary>
public async Task<SsoBaseResponse> EmailExistOnSso(string emailAddress);
/// <summary>
/// Registers a new user in the SSO system
/// </summary>
public async Task<SsoBaseResponse> RegisterUser(RegisterRequest request);
/// <summary>
/// Authenticates a user against the SSO system
/// </summary>
public async Task<LoginResponse> AuthenticateUser(LoginRequest request);
}
Testing
Unit Testing with Moq
public class AuthIntegrationServiceTests
{
private readonly Mock<IGenericApi> _mockApi;
private readonly Mock<ILogger<AuthIntegrationService>> _mockLogger;
private readonly AuthIntegrationService _service;
public AuthIntegrationServiceTests()
{
_mockApi = new Mock<IGenericApi>();
_mockLogger = new Mock<ILogger<AuthIntegrationService>>();
_service = new AuthIntegrationService(Mock.Of<ICreateLogin>(), _mockApi.Object, _mockLogger.Object);
}
[Fact]
public async Task EmailExistOnSso_Should_Return_Success_Response()
{
// Arrange
var email = "test@example.com";
var expectedResponse = new SsoBaseResponse { Success = true, Message = "Email found" };
_mockApi.Setup(x => x.GetAsync<SsoBaseResponse>(
It.Is<string>(url => url.Contains(email)),
It.IsAny<CancellationToken>()))
.ReturnsAsync(expectedResponse);
// Act
var result = await _service.EmailExistOnSso(email);
// Assert
Assert.NotNull(result);
Assert.True(result.Success);
Assert.Equal("Email found", result.Message);
}
[Fact]
public async Task EmailExistOnSso_Should_Handle_HttpRequestException()
{
// Arrange
var email = "test@example.com";
_mockApi.Setup(x => x.GetAsync<SsoBaseResponse>(
It.IsAny<string>(),
It.IsAny<CancellationToken>()))
.ThrowsAsync(new HttpRequestException("Service unavailable"));
// Act & Assert
await Assert.ThrowsAsync<HttpRequestException>(() =>
_service.EmailExistOnSso(email));
}
}
Integration Testing
public class AuthIntegrationTests : IClassFixture<WebApplicationFactory<Program>>
{
private readonly WebApplicationFactory<Program> _factory;
private readonly HttpClient _client;
public AuthIntegrationTests(WebApplicationFactory<Program> factory)
{
_factory = factory;
_client = _factory.CreateClient();
}
[Fact]
public async Task CheckEmail_Should_Return_Valid_Response()
{
// Arrange
var email = "test@example.com";
// Act
var response = await _client.GetAsync($"/api/auth/check-email/{email}");
// Assert
response.EnsureSuccessStatusCode();
var content = await response.Content.ReadAsStringAsync();
Assert.Contains("success", content.ToLower());
}
}
Error Handling
The service includes comprehensive error handling:
public async Task<SsoBaseResponse> SafeEmailCheck(string email)
{
try
{
return await EmailExistOnSso(email);
}
catch (HttpRequestException ex) when (ex.Message.Contains("timeout"))
{
_logger.LogWarning("SSO service timeout for email check: {Email}", email);
return new SsoBaseResponse { Success = false, Message = "Service temporarily unavailable" };
}
catch (TaskCanceledException ex)
{
_logger.LogWarning("SSO request cancelled: {Email}", email);
return new SsoBaseResponse { Success = false, Message = "Request cancelled" };
}
catch (Exception ex)
{
_logger.LogError(ex, "Unexpected error during email check: {Email}", email);
throw;
}
}
Best Practices
- Always use CancellationToken: Pass cancellation tokens through the call chain
- Configure Timeouts: Set appropriate timeout values for external services
- Implement Retry Logic: Consider adding Polly for retry policies
- Log Appropriately: Log requests/responses but avoid sensitive data
- Validate Input: Validate parameters before making external calls
- Handle Exceptions: Implement proper exception handling strategies
Troubleshooting
Common Issues
Issue: HttpRequestException: Name resolution failed
- Solution: Verify the SSO BaseUrl in configuration
Issue: TaskCanceledException: The operation was canceled
- Solution: Increase timeout values or check network connectivity
Issue: JsonException: The JSON value could not be converted
- Solution: Verify response DTOs match the actual API response structure
Debugging Tips
Enable detailed HTTP logging:
builder.Services.AddLogging(builder => builder.AddConsole().SetMinimumLevel(LogLevel.Debug));Add request/response logging to HttpClientGenericApi
Use tools like Fiddler or Postman to test SSO endpoints directly
Contributing
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
License
This project is licensed under the MIT License - see the LICENSE file for details.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net8.0
- Refit (>= 8.0.0)
- Refit.HttpClientFactory (>= 8.0.0)
- Serilog (>= 4.3.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.