-
Notifications
You must be signed in to change notification settings - Fork 0
/
TokenProvider.cs
100 lines (84 loc) · 3.77 KB
/
TokenProvider.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
using System.Diagnostics.CodeAnalysis;
using System.Net.Http.Headers;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json.Linq;
namespace SaluteSpeechClient.Auth;
/// <summary>
/// Provides access to an access token.
/// </summary>
[SuppressMessage("Performance", "CA1848:Use the LoggerMessage delegates")]
public class TokenProvider : ITokenProvider
{
private static readonly SemaphoreSlim s_semaphoreSlim = new(1);
private readonly ILogger<TokenProvider>? _logger;
private readonly string _secretKey;
private DateTimeOffset _exp;
private string _token = string.Empty;
/// <summary>
/// Initializes a new instance of the <see cref="TokenProvider"/> class.
/// </summary>
/// <param name="secretKey">Client secret key.</param>
/// <param name="logger">Logger to log errors.</param>
/// <exception cref="ArgumentNullException">Thrown when <paramref name="secretKey"/> is null.</exception>
public TokenProvider(string secretKey, ILogger<TokenProvider>? logger = null)
{
_secretKey = secretKey ?? throw new ArgumentNullException(nameof(secretKey));
_logger = logger;
}
/// <summary>
/// Generates an access token, saves and returns it if it's empty or expired, else returns current access token.
/// </summary>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>Access token value.</returns>
/// <exception cref="HttpRequestException">Thrown when request to generate token failed.</exception>
public async Task<string> GetTokenAsync(CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
await s_semaphoreSlim.WaitAsync(cancellationToken).ConfigureAwait(false);
try
{
if (string.IsNullOrEmpty(_token) || _exp - DateTimeOffset.UtcNow <= TimeSpan.FromMinutes(1))
{
await GenerateTokenAsync(cancellationToken).ConfigureAwait(false);
}
return _token;
}
catch (HttpRequestException ex)
{
_logger?.Log(LogLevel.Error, ex, "Token generate request failed. Secret key: {@secretKey}", _secretKey);
throw;
}
finally
{
s_semaphoreSlim.Release();
}
}
private async Task GenerateTokenAsync(CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
const string requestBody = "scope=SALUTE_SPEECH_PERS";
const string endpoint = "https://ngw.devices.sberbank.ru:9443/api/v2/oauth";
string rqUid = Guid.NewGuid().ToString();
try
{
using HttpClient httpClient = new();
httpClient.DefaultRequestHeaders.Add("Authorization", "Basic " + _secretKey);
httpClient.DefaultRequestHeaders.Add("RqUID", rqUid);
using HttpContent httpContent = new StringContent(requestBody);
httpContent.Headers.ContentType = new MediaTypeHeaderValue("application/x-www-form-urlencoded");
HttpResponseMessage response = await httpClient.PostAsync(new Uri(endpoint), httpContent, cancellationToken).ConfigureAwait(false);
response.EnsureSuccessStatusCode();
string responseBody = await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false);
JObject json = JObject.Parse(responseBody);
_token = json.Value<string>("access_token") ?? string.Empty;
_exp = DateTimeOffset.FromUnixTimeMilliseconds(json.Value<long>("expires_at"));
}
catch (HttpRequestException ex)
{
_token = string.Empty;
ex.Data.Add("RqUID", rqUid);
ex.Data.Add("secret key", _secretKey);
throw;
}
}
}