using System.Net.Http; using IdokladSdk; using IdokladSdk.Authentication; using IdokladSdk.Authentication.Models; using IdokladSdk.Exceptions; using Newtonsoft.Json; namespace Idoklad.Client; /// /// iDoklad client_credentials authentication that sends only client_id and /// client_secret (no application_id). /// /// The official SDK's mandates an applicationId and /// always posts application_id, but iDoklad's standard client_credentials grant does not /// require it. This implementation is used when the caller does not supply an ApplicationId. /// public sealed class ClientSecretAuthentication : IAuthentication { private const string Scope = "idoklad_api"; private readonly string _clientId; private readonly string _clientSecret; public ClientSecretAuthentication(string clientId, string clientSecret) { _clientId = clientId; _clientSecret = clientSecret; } public DokladConfiguration Configuration { get; set; } = default!; public string RefreshToken => null!; public bool UseRefreshToken { get => false; set { } } public async Task GetTokenAsync(HttpClient httpClient, CancellationToken cancellationToken = default) { var postData = new List> { new("grant_type", "client_credentials"), new("client_id", _clientId), new("client_secret", _clientSecret), new("scope", Scope), }; // The SDK targets the v2 token endpoint (server/v2/connect/token), which mandates // application_id. Without an applicationId we use the classic endpoint // (server/connect/token), which authenticates with client_id + client_secret only. var tokenUrl = Configuration.IdentityServerTokenUrl.ToString() .Replace("/server/v2/connect/token", "/server/connect/token"); using var request = new HttpRequestMessage(HttpMethod.Post, tokenUrl) { Content = new FormUrlEncodedContent(postData), }; using var response = await httpClient.SendAsync(request, cancellationToken).ConfigureAwait(false); var content = await response.Content.ReadAsStringAsync().ConfigureAwait(false); if (!response.IsSuccessStatusCode) { throw new IdokladAuthenticationException(content); } var tokenizer = JsonConvert.DeserializeObject(content); if (tokenizer is null || string.IsNullOrEmpty(tokenizer.AccessToken)) { var error = JsonConvert.DeserializeObject(content); throw new IdokladAuthenticationException(error); } // GrantType defaults to ClientCredentials (enum value 0), which is correct here. return tokenizer; } public Task RefreshAccessTokenAsync(HttpClient httpClient, CancellationToken cancellationToken = default) => GetTokenAsync(httpClient, cancellationToken); }