Skip to content

Commit b72abf5

Browse files
CentralizedLogging Sdk is add but jwt token is not centralized
1 parent fa6ec77 commit b72abf5

File tree

8 files changed

+195
-0
lines changed

8 files changed

+195
-0
lines changed
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
namespace CentralizedLogging.Sdk.Abstractions
2+
{
3+
public interface IAccessTokenProvider
4+
{
5+
Task<string?> GetAccessTokenAsync(CancellationToken ct = default);
6+
void SetAccessToken(string token, int userId, DateTime expiresAtUtc);
7+
public Task RemoveAsync(string userId, CancellationToken ct = default);
8+
}
9+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+

2+
namespace CentralizedLogging.Sdk.Abstractions
3+
{
4+
public interface ICentralizedLoggingClient
5+
{
6+
//Task<AuthResponse> LoginAsync(LoginRequest request, CancellationToken ct = default);
7+
}
8+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net9.0</TargetFramework>
5+
<ImplicitUsings>enable</ImplicitUsings>
6+
<Nullable>enable</Nullable>
7+
</PropertyGroup>
8+
9+
<ItemGroup>
10+
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="9.0.8" />
11+
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.8" />
12+
<PackageReference Include="Microsoft.Extensions.Options" Version="9.0.8" />
13+
<PackageReference Include="Polly" Version="8.6.3" />
14+
<PackageReference Include="Polly.Extensions.Http" Version="3.0.0" />
15+
</ItemGroup>
16+
17+
</Project>
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
using System.Net.Http.Json;
2+
using System.Text.Json;
3+
using CentralizedLogging.Sdk.Abstractions;
4+
5+
namespace CentralizedLogging.Sdk
6+
{
7+
internal sealed class CentralizedLoggingClient : ICentralizedLoggingClient
8+
{
9+
private readonly HttpClient _http;
10+
11+
public CentralizedLoggingClient(HttpClient http) => _http = http;
12+
13+
//public async Task<AuthResponse> LoginAsync(LoginRequest request, CancellationToken ct = default)
14+
//{
15+
// var resp = await _http.PostAsJsonAsync("api/users/authenticate", request, ct);
16+
// var contentType = resp.Content.Headers.ContentType?.MediaType ?? "";
17+
18+
// var body = await resp.Content.ReadAsStringAsync(ct);
19+
20+
// if (!resp.IsSuccessStatusCode)
21+
// {
22+
// // Try to parse ProblemDetails for a better message
23+
// ProblemDetails? prob = null;
24+
// if (contentType.Contains("json", StringComparison.OrdinalIgnoreCase))
25+
// {
26+
// try { prob = JsonSerializer.Deserialize<ProblemDetails>(body); } catch { /* ignore */ }
27+
// }
28+
// var msg = prob?.Detail ?? prob?.Title ?? $"HTTP {(int)resp.StatusCode} {resp.ReasonPhrase}";
29+
// throw new HttpRequestException(msg);
30+
// }
31+
32+
// // Success → parse AuthResponse
33+
// if (!contentType.Contains("json", StringComparison.OrdinalIgnoreCase))
34+
// throw new InvalidOperationException($"Expected JSON but got '{contentType}'. Body: {body[..Math.Min(120, body.Length)]}");
35+
36+
// var auth = JsonSerializer.Deserialize<AuthResponse>(body, new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
37+
// return auth;
38+
//}
39+
}
40+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
7+
namespace CentralizedLogging.Sdk.Configuration
8+
{
9+
public sealed class CentralizedLoggingOptions
10+
{
11+
/// <summary>Base URL of the User Management API, e.g., https://auth.myorg.local/</summary>
12+
public Uri? BaseAddress { get; set; }
13+
14+
/// <summary>HttpClient timeout (default 30s).</summary>
15+
public TimeSpan Timeout { get; set; } = TimeSpan.FromSeconds(30);
16+
17+
/// <summary>Enable Polly resilience policies.</summary>
18+
public bool EnableResiliencePolicies { get; set; } = true;
19+
}
20+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
using Polly;
2+
using Polly.Extensions.Http;
3+
using System.Net;
4+
5+
namespace CentralizedLogging.Sdk.Extensions
6+
{
7+
internal static class HttpPolicies
8+
{
9+
public static IAsyncPolicy<HttpResponseMessage> GetRetryPolicy()
10+
=> HttpPolicyExtensions
11+
.HandleTransientHttpError() // 5xx + 408 + network failures
12+
.OrResult(msg => msg.StatusCode == HttpStatusCode.TooManyRequests)
13+
.WaitAndRetryAsync(new[]
14+
{
15+
TimeSpan.FromMilliseconds(200),
16+
TimeSpan.FromMilliseconds(500),
17+
TimeSpan.FromSeconds(1)
18+
});
19+
20+
public static IAsyncPolicy<HttpResponseMessage> GetCircuitBreakerPolicy()
21+
=> HttpPolicyExtensions
22+
.HandleTransientHttpError()
23+
.CircuitBreakerAsync(handledEventsAllowedBeforeBreaking: 5, durationOfBreak: TimeSpan.FromSeconds(30));
24+
}
25+
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
using System;
2+
using Microsoft.Extensions.Configuration;
3+
using Microsoft.Extensions.DependencyInjection;
4+
using Microsoft.Extensions.Options;
5+
using Polly;
6+
using CentralizedLogging.Sdk.Abstractions;
7+
using CentralizedLogging.Sdk.Auth;
8+
using CentralizedLogging.Sdk.Configuration;
9+
10+
11+
namespace CentralizedLogging.Sdk.Extensions
12+
{
13+
public static class ServiceCollectionExtensions
14+
{
15+
public static IServiceCollection AddUserManagementSdk(
16+
this IServiceCollection services,
17+
Action<CentralizedLoggingOptions>? configure = null)
18+
{
19+
services.AddOptions<CentralizedLoggingOptions>()
20+
.Configure<IConfiguration>((opt, config) =>
21+
{
22+
// optional: bind from config "CentralizedLogging"
23+
config.GetSection("CentralizedLogging").Bind(opt);
24+
});
25+
26+
if (configure is not null)
27+
services.PostConfigure(configure);
28+
29+
// Delegating handler MUST be transient
30+
services.AddTransient<BearerTokenHandler>();
31+
32+
// Register the token provider (memory-based)
33+
services.AddScoped<IAccessTokenProvider, MemoryCacheAccessTokenProvider>();
34+
35+
36+
// The Typed client
37+
services.AddHttpClient<ICentralizedLoggingClient, CentralizedLoggingClient>((sp, http) =>
38+
{
39+
var opts = sp.GetRequiredService<IOptions<CentralizedLoggingOptions>>().Value;
40+
if (opts.BaseAddress is null)
41+
throw new InvalidOperationException("CentralizedLoggingOptions.BaseAddress must be set.");
42+
43+
http.BaseAddress = opts.BaseAddress;
44+
http.Timeout = opts.Timeout;
45+
})
46+
.AddHttpMessageHandler<BearerTokenHandler>()
47+
.ConfigurePrimaryHttpMessageHandler(() => new HttpClientHandler
48+
{
49+
// If you need custom certs, proxies, cookies, etc.
50+
UseCookies = false
51+
})
52+
.AddPolicyHandler((sp, req) =>
53+
{
54+
var opts = sp.GetRequiredService<IOptions<CentralizedLoggingOptions>().Value;
55+
return opts.EnableResiliencePolicies
56+
? HttpPolicies.GetRetryPolicy()
57+
: Polly.Policy.NoOpAsync().AsAsyncPolicy<HttpResponseMessage>();
58+
})
59+
.AddPolicyHandler((sp, req) =>
60+
{
61+
var opts = sp.GetRequiredService<IOptions<CentralizedLoggingOptions>().Value;
62+
return opts.EnableResiliencePolicies
63+
? HttpPolicies.GetCircuitBreakerPolicy()
64+
: Polly.Policy.NoOpAsync().AsAsyncPolicy<HttpResponseMessage>();
65+
});
66+
67+
return services;
68+
}
69+
}
70+
}

CentralizedLoggingMonitoring.sln

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UserManagement.Contracts",
1313
EndProject
1414
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UserManagement.Sdk", "UserManagement.Sdk\UserManagement.Sdk.csproj", "{7F2BE969-553A-438D-9F79-2191C2ACF9A9}"
1515
EndProject
16+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CentralizedLogging.Sdk", "CentralizedLogging.Sdk\CentralizedLogging.Sdk.csproj", "{5DA73ABB-A410-4E63-A8B0-C55776CDC074}"
17+
EndProject
1618
Global
1719
GlobalSection(SolutionConfigurationPlatforms) = preSolution
1820
Debug|Any CPU = Debug|Any CPU
@@ -39,6 +41,10 @@ Global
3941
{7F2BE969-553A-438D-9F79-2191C2ACF9A9}.Debug|Any CPU.Build.0 = Debug|Any CPU
4042
{7F2BE969-553A-438D-9F79-2191C2ACF9A9}.Release|Any CPU.ActiveCfg = Release|Any CPU
4143
{7F2BE969-553A-438D-9F79-2191C2ACF9A9}.Release|Any CPU.Build.0 = Release|Any CPU
44+
{5DA73ABB-A410-4E63-A8B0-C55776CDC074}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
45+
{5DA73ABB-A410-4E63-A8B0-C55776CDC074}.Debug|Any CPU.Build.0 = Debug|Any CPU
46+
{5DA73ABB-A410-4E63-A8B0-C55776CDC074}.Release|Any CPU.ActiveCfg = Release|Any CPU
47+
{5DA73ABB-A410-4E63-A8B0-C55776CDC074}.Release|Any CPU.Build.0 = Release|Any CPU
4248
EndGlobalSection
4349
GlobalSection(SolutionProperties) = preSolution
4450
HideSolutionNode = FALSE

0 commit comments

Comments
 (0)