Files
KArtSell.Aegis/docs/Design/kbx-foundation-v52-fe-operational-navigation-screen-anatomy/backend/Shared/Providers/KisAccessTokenProvider.cs
T
kjh2064 c41e5063b7 chore: remove kbx-foundation-v36 reference (superseded by v4 implementation)
Removed entire kbx-foundation-v36 directory as it's been replaced by
the new KBX Foundation v4 patterns implemented in this session:
- Registry-driven screen definitions
- Density-aware UI adapter components
- Feature module templates (ShadowRun, Models)

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-08-12 01:39:58 +09:00

40 lines
2.3 KiB
C#

using System.Text;
using System.Text.Json;
namespace Kbx.Shared.Providers;
public interface IKisAccessTokenProvider { ValueTask<string> GetAsync(bool sandbox, CancellationToken cancellationToken); }
public sealed class KisAccessTokenProvider(HttpClient http, IKbxProviderSecretStore secrets) : IKisAccessTokenProvider
{
private readonly SemaphoreSlim _gate = new(1,1);
private string? _token;
private DateTimeOffset _expiresAt;
private bool _sandbox;
public async ValueTask<string> GetAsync(bool sandbox, CancellationToken ct)
{
if (_token is not null && _sandbox == sandbox && DateTimeOffset.UtcNow < _expiresAt.AddMinutes(-5)) return _token;
await _gate.WaitAsync(ct);
try
{
if (_token is not null && _sandbox == sandbox && DateTimeOffset.UtcNow < _expiresAt.AddMinutes(-5)) return _token;
var appKey = await secrets.GetRequiredAsync("ExternalProviders:Kis:AppKey", ct);
var appSecret = await secrets.GetRequiredAsync("ExternalProviders:Kis:AppSecret", ct);
var baseUri = sandbox ? "https://openapivts.koreainvestment.com:29443" : "https://openapi.koreainvestment.com:9443";
var payload = JsonSerializer.Serialize(new { grant_type="client_credentials", appkey=appKey, appsecret=appSecret });
using var req = new HttpRequestMessage(HttpMethod.Post, baseUri + "/oauth2/tokenP") { Content = new StringContent(payload, Encoding.UTF8, "application/json") };
using var res = await http.SendAsync(req, ct);
res.EnsureSuccessStatusCode();
using var doc = JsonDocument.Parse(await res.Content.ReadAsStringAsync(ct));
_token = doc.RootElement.GetProperty("access_token").GetString() ?? throw new InvalidOperationException("KIS access_token missing");
_sandbox = sandbox;
if (doc.RootElement.TryGetProperty("access_token_token_expired", out var exp) && DateTimeOffset.TryParse(exp.GetString(), out var parsed)) _expiresAt = parsed;
else if (doc.RootElement.TryGetProperty("expires_in", out var sec) && sec.TryGetInt32(out var seconds)) _expiresAt = DateTimeOffset.UtcNow.AddSeconds(seconds);
else _expiresAt = DateTimeOffset.UtcNow.AddHours(24);
return _token;
}
finally { _gate.Release(); }
}
}