Skip to content

C# / .NET

Practical .NET client for Estia API: token caching, auto refresh, one place for authenticated requests.

You'll probably adapt naming, logging, resilience, and DI to your own codebase — but as a starting point, it works.

Full example

using System.Net.Http.Headers;
using System.Net.Http.Json;

public sealed class EstiaApiClient
{
    private readonly HttpClient _http;
    private readonly string _clientId;
    private readonly string _clientSecret;
    private string? _token;
    private DateTimeOffset _tokenExpiresAt;
    private readonly SemaphoreSlim _tokenLock = new(1, 1);

    public EstiaApiClient(string clientId, string clientSecret,
        string apiBaseUrl = "https://api.insurancegateway.gr")
    {
        _http = new HttpClient { BaseAddress = new Uri(apiBaseUrl) };
        _clientId = clientId;
        _clientSecret = clientSecret;
    }

    private async Task<string> GetTokenAsync(CancellationToken ct = default)
    {
        if (_token is not null && DateTimeOffset.UtcNow < _tokenExpiresAt.AddMinutes(-5))
            return _token;

        await _tokenLock.WaitAsync(ct);
        try
        {
            if (_token is not null && DateTimeOffset.UtcNow < _tokenExpiresAt.AddMinutes(-5))
                return _token;

            using var auth = new HttpClient();
            var resp = await auth.PostAsync(
                "https://auth.insurancegateway.gr/realms/estia/protocol/openid-connect/token",
                new FormUrlEncodedContent(new Dictionary<string, string>
                {
                    ["grant_type"] = "client_credentials",
                    ["client_id"] = _clientId,
                    ["client_secret"] = _clientSecret,
                }), ct);
            resp.EnsureSuccessStatusCode();

            var json = await resp.Content.ReadFromJsonAsync<TokenResponse>(ct);
            _token = json!.access_token;
            _tokenExpiresAt = DateTimeOffset.UtcNow.AddSeconds(json.expires_in);
            return _token;
        }
        finally
        {
            _tokenLock.Release();
        }
    }

    public async Task<T?> GetAsync<T>(string path, CancellationToken ct = default)
    {
        var token = await GetTokenAsync(ct);
        using var req = new HttpRequestMessage(HttpMethod.Get, path);
        req.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);

        var resp = await _http.SendAsync(req, ct);
        resp.EnsureSuccessStatusCode();
        return await resp.Content.ReadFromJsonAsync<T>(ct);
    }

    public async Task<T?> PostAsync<T>(string path, object body, CancellationToken ct = default)
    {
        var token = await GetTokenAsync(ct);
        using var req = new HttpRequestMessage(HttpMethod.Post, path)
        {
            Content = JsonContent.Create(body)
        };
        req.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);

        var resp = await _http.SendAsync(req, ct);
        resp.EnsureSuccessStatusCode();
        return await resp.Content.ReadFromJsonAsync<T>(ct);
    }

    private record TokenResponse(string access_token, int expires_in, string token_type);
}

Example usage

var client = new EstiaApiClient(
    clientId: Environment.GetEnvironmentVariable("ESTIA_CLIENT_ID")!,
    clientSecret: Environment.GetEnvironmentVariable("ESTIA_CLIENT_SECRET")!);

var brands = await client.GetAsync<List<Brand>>("/intersalonica/auto/brands");

Typical DI registration

builder.Services.AddSingleton(sp => new EstiaApiClient(
    clientId: builder.Configuration["Estia:ClientId"]!,
    clientSecret: builder.Configuration["Estia:ClientSecret"]!));

For heavier production traffic

Pair with IHttpClientFactory and resilience policies (retries, timeouts, circuit breakers), especially for scheduled or high-volume workloads.