Skip to content

Commit fe7863f

Browse files
committed
Refactor OidcTokenProvider to remove dependency on IdentityModel and improve token handling
1 parent 73b318b commit fe7863f

File tree

2 files changed

+66
-30
lines changed

2 files changed

+66
-30
lines changed

src/KubernetesClient/Authentication/OidcTokenProvider.cs

Lines changed: 66 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,32 @@
1-
using IdentityModel.OidcClient;
21
using k8s.Exceptions;
3-
using System.IdentityModel.Tokens.Jwt;
2+
using System.Net.Http;
43
using System.Net.Http.Headers;
4+
using System.Text;
55

66
namespace k8s.Authentication
77
{
88
public class OidcTokenProvider : ITokenProvider
99
{
10-
private readonly OidcClient _oidcClient;
10+
private readonly string _clientId;
11+
private readonly string _clientSecret;
12+
private readonly string _idpIssuerUrl;
13+
1114
private string _idToken;
1215
private string _refreshToken;
1316
private DateTimeOffset _expiry;
1417

1518
public OidcTokenProvider(string clientId, string clientSecret, string idpIssuerUrl, string idToken, string refreshToken)
1619
{
20+
_clientId = clientId;
21+
_clientSecret = clientSecret;
22+
_idpIssuerUrl = idpIssuerUrl;
1723
_idToken = idToken;
1824
_refreshToken = refreshToken;
19-
_oidcClient = getClient(clientId, clientSecret, idpIssuerUrl);
20-
_expiry = getExpiryFromToken();
25+
26+
if (!string.IsNullOrEmpty(_idToken))
27+
{
28+
_expiry = GetExpiryFromToken();
29+
}
2130
}
2231

2332
public async Task<AuthenticationHeaderValue> GetAuthenticationHeaderAsync(CancellationToken cancellationToken)
@@ -30,49 +39,78 @@ public async Task<AuthenticationHeaderValue> GetAuthenticationHeaderAsync(Cancel
3039
return new AuthenticationHeaderValue("Bearer", _idToken);
3140
}
3241

33-
private DateTime getExpiryFromToken()
42+
private DateTimeOffset GetExpiryFromToken()
3443
{
35-
long expiry;
36-
var handler = new JwtSecurityTokenHandler();
37-
try
44+
var parts = _idToken.Split('.');
45+
if (parts.Length != 3)
3846
{
39-
var token = handler.ReadJwtToken(_idToken);
40-
expiry = token.Payload.Expiration ?? 0;
47+
throw new ArgumentException("Invalid JWT token format.");
4148
}
42-
catch
49+
50+
var payload = parts[1];
51+
var jsonBytes = Base64UrlDecode(payload);
52+
var json = Encoding.UTF8.GetString(jsonBytes);
53+
54+
using var document = JsonDocument.Parse(json);
55+
if (document.RootElement.TryGetProperty("exp", out var expElement))
4356
{
44-
expiry = 0;
57+
var exp = expElement.GetInt64();
58+
var expiryDateTime = DateTimeOffset.FromUnixTimeSeconds(exp);
59+
return expiryDateTime;
60+
}
61+
else
62+
{
63+
throw new ArgumentException("JWT token does not contain 'exp' claim.");
4564
}
46-
47-
return DateTimeOffset.FromUnixTimeSeconds(expiry).UtcDateTime;
4865
}
4966

50-
private OidcClient getClient(string clientId, string clientSecret, string idpIssuerUrl)
67+
private static byte[] Base64UrlDecode(string input)
5168
{
52-
OidcClientOptions options = new OidcClientOptions
69+
var output = input.Replace('-', '+').Replace('_', '/');
70+
switch (output.Length % 4)
5371
{
54-
ClientId = clientId,
55-
ClientSecret = clientSecret ?? "",
56-
Authority = idpIssuerUrl,
57-
};
72+
case 2: output += "=="; break;
73+
case 3: output += "="; break;
74+
}
5875

59-
return new OidcClient(options);
76+
return Convert.FromBase64String(output);
6077
}
6178

6279
private async Task RefreshToken()
6380
{
6481
try
6582
{
66-
var result = await _oidcClient.RefreshTokenAsync(_refreshToken).ConfigureAwait(false);
83+
using var httpClient = new HttpClient();
84+
var request = new HttpRequestMessage(HttpMethod.Post, _idpIssuerUrl);
85+
request.Content = new FormUrlEncodedContent(new Dictionary<string, string>
86+
{
87+
{ "grant_type", "refresh_token" },
88+
{ "client_id", _clientId },
89+
{ "client_secret", _clientSecret },
90+
{ "refresh_token", _refreshToken },
91+
});
92+
93+
var response = await httpClient.SendAsync(request).ConfigureAwait(false);
94+
response.EnsureSuccessStatusCode();
6795

68-
if (result.IsError)
96+
var responseContent = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
97+
var jsonDocument = JsonDocument.Parse(responseContent);
98+
99+
if (jsonDocument.RootElement.TryGetProperty("id_token", out var idTokenElement))
69100
{
70-
throw new Exception(result.Error);
101+
_idToken = idTokenElement.GetString();
71102
}
72103

73-
_idToken = result.IdentityToken;
74-
_refreshToken = result.RefreshToken;
75-
_expiry = result.AccessTokenExpiration;
104+
if (jsonDocument.RootElement.TryGetProperty("refresh_token", out var refreshTokenElement))
105+
{
106+
_refreshToken = refreshTokenElement.GetString();
107+
}
108+
109+
if (jsonDocument.RootElement.TryGetProperty("expires_in", out var expiresInElement))
110+
{
111+
var expiresIn = expiresInElement.GetInt32();
112+
_expiry = DateTimeOffset.UtcNow.AddSeconds(expiresIn);
113+
}
76114
}
77115
catch (Exception e)
78116
{

src/KubernetesClient/KubernetesClient.csproj

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@
77
</PropertyGroup>
88

99
<ItemGroup>
10-
<PackageReference Include="System.IdentityModel.Tokens.Jwt" />
11-
<PackageReference Include="IdentityModel.OidcClient" />
1210
<PackageReference Include="Fractions" />
1311
<PackageReference Include="YamlDotNet" />
1412
</ItemGroup>

0 commit comments

Comments
 (0)