1
- using IdentityModel . OidcClient ;
2
1
using k8s . Exceptions ;
3
- using System . IdentityModel . Tokens . Jwt ;
2
+ using System . Net . Http ;
4
3
using System . Net . Http . Headers ;
4
+ using System . Text ;
5
5
6
6
namespace k8s . Authentication
7
7
{
8
8
public class OidcTokenProvider : ITokenProvider
9
9
{
10
- private readonly OidcClient _oidcClient ;
10
+ private readonly string _clientId ;
11
+ private readonly string _clientSecret ;
12
+ private readonly string _idpIssuerUrl ;
13
+
11
14
private string _idToken ;
12
15
private string _refreshToken ;
13
16
private DateTimeOffset _expiry ;
14
17
15
18
public OidcTokenProvider ( string clientId , string clientSecret , string idpIssuerUrl , string idToken , string refreshToken )
16
19
{
20
+ _clientId = clientId ;
21
+ _clientSecret = clientSecret ;
22
+ _idpIssuerUrl = idpIssuerUrl ;
17
23
_idToken = idToken ;
18
24
_refreshToken = refreshToken ;
19
- _oidcClient = getClient ( clientId , clientSecret , idpIssuerUrl ) ;
20
- _expiry = getExpiryFromToken ( ) ;
25
+
26
+ if ( ! string . IsNullOrEmpty ( _idToken ) )
27
+ {
28
+ _expiry = GetExpiryFromToken ( ) ;
29
+ }
21
30
}
22
31
23
32
public async Task < AuthenticationHeaderValue > GetAuthenticationHeaderAsync ( CancellationToken cancellationToken )
@@ -30,49 +39,78 @@ public async Task<AuthenticationHeaderValue> GetAuthenticationHeaderAsync(Cancel
30
39
return new AuthenticationHeaderValue ( "Bearer" , _idToken ) ;
31
40
}
32
41
33
- private DateTime getExpiryFromToken ( )
42
+ private DateTimeOffset GetExpiryFromToken ( )
34
43
{
35
- long expiry ;
36
- var handler = new JwtSecurityTokenHandler ( ) ;
37
- try
44
+ var parts = _idToken . Split ( '.' ) ;
45
+ if ( parts . Length != 3 )
38
46
{
39
- var token = handler . ReadJwtToken ( _idToken ) ;
40
- expiry = token . Payload . Expiration ?? 0 ;
47
+ throw new ArgumentException ( "Invalid JWT token format." ) ;
41
48
}
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 ) )
43
56
{
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." ) ;
45
64
}
46
-
47
- return DateTimeOffset . FromUnixTimeSeconds ( expiry ) . UtcDateTime ;
48
65
}
49
66
50
- private OidcClient getClient ( string clientId , string clientSecret , string idpIssuerUrl )
67
+ private static byte [ ] Base64UrlDecode ( string input )
51
68
{
52
- OidcClientOptions options = new OidcClientOptions
69
+ var output = input . Replace ( '-' , '+' ) . Replace ( '_' , '/' ) ;
70
+ switch ( output . Length % 4 )
53
71
{
54
- ClientId = clientId ,
55
- ClientSecret = clientSecret ?? "" ,
56
- Authority = idpIssuerUrl ,
57
- } ;
72
+ case 2 : output += "==" ; break ;
73
+ case 3 : output += "=" ; break ;
74
+ }
58
75
59
- return new OidcClient ( options ) ;
76
+ return Convert . FromBase64String ( output ) ;
60
77
}
61
78
62
79
private async Task RefreshToken ( )
63
80
{
64
81
try
65
82
{
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 ( ) ;
67
95
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 ) )
69
100
{
70
- throw new Exception ( result . Error ) ;
101
+ _idToken = idTokenElement . GetString ( ) ;
71
102
}
72
103
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
+ }
76
114
}
77
115
catch ( Exception e )
78
116
{
0 commit comments