forked from kubernetes-client/csharp
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathOidcTokenProvider.cs
116 lines (100 loc) · 4 KB
/
OidcTokenProvider.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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
using k8s.Exceptions;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
namespace k8s.Authentication
{
public class OidcTokenProvider : ITokenProvider
{
private readonly string _clientId;
private readonly string _clientSecret;
private readonly string _idpIssuerUrl;
private string _idToken;
private string _refreshToken;
private DateTimeOffset _expiry;
public OidcTokenProvider(string clientId, string clientSecret, string idpIssuerUrl, string idToken, string refreshToken)
{
_clientId = clientId;
_clientSecret = clientSecret;
_idpIssuerUrl = idpIssuerUrl;
_idToken = idToken;
_refreshToken = refreshToken;
_expiry = GetExpiryFromToken();
}
public async Task<AuthenticationHeaderValue> GetAuthenticationHeaderAsync(CancellationToken cancellationToken)
{
if (_idToken == null || DateTime.UtcNow.AddSeconds(30) > _expiry)
{
await RefreshToken().ConfigureAwait(false);
}
return new AuthenticationHeaderValue("Bearer", _idToken);
}
private DateTimeOffset GetExpiryFromToken()
{
try
{
var parts = _idToken.Split('.');
var payload = parts[1];
var jsonBytes = Base64UrlDecode(payload);
var json = Encoding.UTF8.GetString(jsonBytes);
using var document = JsonDocument.Parse(json);
if (document.RootElement.TryGetProperty("exp", out var expElement))
{
var exp = expElement.GetInt64();
return DateTimeOffset.FromUnixTimeSeconds(exp);
}
}
catch
{
// ignore to default
}
return default;
}
private static byte[] Base64UrlDecode(string input)
{
var output = input.Replace('-', '+').Replace('_', '/');
switch (output.Length % 4)
{
case 2: output += "=="; break;
case 3: output += "="; break;
}
return Convert.FromBase64String(output);
}
private async Task RefreshToken()
{
try
{
using var httpClient = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Post, _idpIssuerUrl);
request.Content = new FormUrlEncodedContent(new Dictionary<string, string>
{
{ "grant_type", "refresh_token" },
{ "client_id", _clientId },
{ "client_secret", _clientSecret },
{ "refresh_token", _refreshToken },
});
var response = await httpClient.SendAsync(request).ConfigureAwait(false);
response.EnsureSuccessStatusCode();
var responseContent = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
var jsonDocument = JsonDocument.Parse(responseContent);
if (jsonDocument.RootElement.TryGetProperty("id_token", out var idTokenElement))
{
_idToken = idTokenElement.GetString();
}
if (jsonDocument.RootElement.TryGetProperty("refresh_token", out var refreshTokenElement))
{
_refreshToken = refreshTokenElement.GetString();
}
if (jsonDocument.RootElement.TryGetProperty("expires_in", out var expiresInElement))
{
var expiresIn = expiresInElement.GetInt32();
_expiry = DateTimeOffset.UtcNow.AddSeconds(expiresIn);
}
}
catch (Exception e)
{
throw new KubernetesClientException($"Unable to refresh OIDC token. \n {e.Message}", e);
}
}
}
}