Skip to content

Commit cadfcce

Browse files
Added CHANGELOG.md
1 parent 2a948bc commit cadfcce

File tree

3 files changed

+99
-45
lines changed

3 files changed

+99
-45
lines changed

CHANGELOG.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,21 @@
1+
- **Breaking Change**: Ydb.Sdk.Yc.Auth version < 0.1.0 is not compatible with newer versions.
2+
- Added IAuthClient to fetch auth token.
3+
- Added the `TokenManagerCredentialsProvider` class, which streamlines token lifecycle management.
4+
- **Breaking Change**: Deleted `AnonymousProvider`. Now users don't need to do anything for anonymous authentication.
5+
Migration guide:
6+
```c#
7+
var config = new DriverConfig(...); // Using AnonymousProvider if Credentials property is null
8+
```
9+
- **Breaking Change**: Deleted `StaticCredentialsProvider`. Users are now recommended to use the `User` and `Password`
10+
properties in `DriverConfig` for configuring authentication. Migration guide:
11+
```c#
12+
var config = new DriverConfig(...)
13+
{
14+
User = "your_username",
15+
Password = "your_password"
16+
};
17+
```
18+
119
## v0.15.4
220

321
- Added `KeepAlivePingTimeout`, with a default value of 10 seconds.

src/Ydb.Sdk/src/Auth/TokenManagerCredentialsProvider.cs

Lines changed: 61 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
1-
using System.Runtime.CompilerServices;
21
using Microsoft.Extensions.Logging;
32
using Microsoft.Extensions.Logging.Abstractions;
43

5-
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")]
6-
74
namespace Ydb.Sdk.Auth;
85

96
public class TokenManagerCredentialsProvider : ICredentialsProvider
@@ -13,7 +10,7 @@ public class TokenManagerCredentialsProvider : ICredentialsProvider
1310

1411
private ILogger<TokenManagerCredentialsProvider> Logger { get; }
1512

16-
private volatile IHolderState _holderState;
13+
private volatile ITokenState _tokenState;
1714

1815
public TokenManagerCredentialsProvider(
1916
IAuthClient authClient,
@@ -22,8 +19,8 @@ public TokenManagerCredentialsProvider(
2219
{
2320
_clock = new SystemClock();
2421
_authClient = authClient;
25-
_holderState = new SyncState(this);
26-
_holderState.Init();
22+
_tokenState = new SyncState(this);
23+
_tokenState.Init();
2724

2825
loggerFactory ??= new NullLoggerFactory();
2926
Logger = loggerFactory.CreateLogger<TokenManagerCredentialsProvider>();
@@ -35,32 +32,32 @@ internal TokenManagerCredentialsProvider(IAuthClient authClient, IClock clock) :
3532
}
3633

3734
public async ValueTask<string> GetAuthInfoAsync() =>
38-
(await _holderState.Validate(_clock.UtcNow)).TokenResponse.Token;
35+
(await _tokenState.Validate(_clock.UtcNow)).TokenResponse.Token;
3936

4037
private ValueTask<TokenResponse> FetchToken() => _authClient.FetchToken();
4138

42-
private IHolderState UpdateState(IHolderState current, IHolderState next)
39+
private ITokenState UpdateState(ITokenState current, ITokenState next)
4340
{
44-
if (Interlocked.CompareExchange(ref _holderState, next, current) == current)
41+
if (Interlocked.CompareExchange(ref _tokenState, next, current) == current)
4542
{
4643
next.Init();
4744
}
4845

49-
return _holderState;
46+
return _tokenState;
5047
}
5148

52-
private interface IHolderState
49+
private interface ITokenState
5350
{
5451
TokenResponse TokenResponse { get; }
5552

56-
ValueTask<IHolderState> Validate(DateTime now);
53+
ValueTask<ITokenState> Validate(DateTime now);
5754

5855
void Init()
5956
{
6057
}
6158
}
6259

63-
private class ActiveState : IHolderState
60+
private class ActiveState : ITokenState
6461
{
6562
private readonly TokenManagerCredentialsProvider _tokenManagerCredentialsProvider;
6663

@@ -72,25 +69,31 @@ public ActiveState(TokenResponse tokenResponse, TokenManagerCredentialsProvider
7269

7370
public TokenResponse TokenResponse { get; }
7471

75-
public async ValueTask<IHolderState> Validate(DateTime now)
72+
public async ValueTask<ITokenState> Validate(DateTime now)
7673
{
77-
if (now >= TokenResponse.ExpiresAt)
74+
if (now < TokenResponse.ExpiredAt)
7875
{
79-
return await _tokenManagerCredentialsProvider
80-
.UpdateState(this, new SyncState(_tokenManagerCredentialsProvider))
81-
.Validate(now);
76+
return now >= TokenResponse.RefreshAt
77+
? _tokenManagerCredentialsProvider.UpdateState(
78+
this,
79+
new BackgroundState(TokenResponse, _tokenManagerCredentialsProvider)
80+
)
81+
: this;
8282
}
8383

84-
return now >= TokenResponse.RefreshAt
85-
? _tokenManagerCredentialsProvider.UpdateState(
86-
this,
87-
new BackgroundState(TokenResponse, _tokenManagerCredentialsProvider)
88-
)
89-
: this;
84+
_tokenManagerCredentialsProvider.Logger.LogWarning(
85+
"Token has expired. ExpiredAt: {ExpiredAt}, CurrentTime: {CurrentTime}. " +
86+
"Switching to synchronous state to fetch a new token",
87+
TokenResponse.ExpiredAt, now
88+
);
89+
90+
return await _tokenManagerCredentialsProvider
91+
.UpdateState(this, new SyncState(_tokenManagerCredentialsProvider))
92+
.Validate(now);
9093
}
9194
}
9295

93-
private class SyncState : IHolderState
96+
private class SyncState : ITokenState
9497
{
9598
private readonly TokenManagerCredentialsProvider _tokenManagerCredentialsProvider;
9699

@@ -104,14 +107,19 @@ public SyncState(TokenManagerCredentialsProvider tokenManagerCredentialsProvider
104107
public TokenResponse TokenResponse =>
105108
throw new InvalidOperationException("Get token for unfinished sync state");
106109

107-
public async ValueTask<IHolderState> Validate(DateTime now)
110+
public async ValueTask<ITokenState> Validate(DateTime now)
108111
{
109112
try
110113
{
111-
var tokenHolder = await _fetchTokenTask;
114+
var tokenResponse = await _fetchTokenTask;
115+
116+
_tokenManagerCredentialsProvider.Logger.LogDebug(
117+
"Successfully fetched token at {Timestamp}. ExpiredAt: {ExpiredAt}, RefreshAt: {RefreshAt}",
118+
DateTime.Now, tokenResponse.ExpiredAt, tokenResponse.RefreshAt
119+
);
112120

113121
return _tokenManagerCredentialsProvider.UpdateState(this,
114-
new ActiveState(tokenHolder, _tokenManagerCredentialsProvider));
122+
new ActiveState(tokenResponse, _tokenManagerCredentialsProvider));
115123
}
116124
catch (Exception e)
117125
{
@@ -125,7 +133,7 @@ public async ValueTask<IHolderState> Validate(DateTime now)
125133
public void Init() => _fetchTokenTask = _tokenManagerCredentialsProvider.FetchToken().AsTask();
126134
}
127135

128-
private class BackgroundState : IHolderState
136+
private class BackgroundState : ITokenState
129137
{
130138
private readonly TokenManagerCredentialsProvider _tokenManagerCredentialsProvider;
131139

@@ -140,11 +148,16 @@ public BackgroundState(TokenResponse tokenResponse,
140148

141149
public TokenResponse TokenResponse { get; }
142150

143-
public async ValueTask<IHolderState> Validate(DateTime now)
151+
public async ValueTask<ITokenState> Validate(DateTime now)
144152
{
145153
if (_fetchTokenTask.IsCanceled || _fetchTokenTask.IsFaulted)
146154
{
147-
return now > TokenResponse.ExpiresAt
155+
_tokenManagerCredentialsProvider.Logger.LogWarning(
156+
"Fetching token task failed. Status: {Status}, Retrying login...",
157+
_fetchTokenTask.IsCanceled ? "Canceled" : "Faulted"
158+
);
159+
160+
return now >= TokenResponse.ExpiredAt
148161
? _tokenManagerCredentialsProvider
149162
.UpdateState(this, new SyncState(_tokenManagerCredentialsProvider))
150163
: _tokenManagerCredentialsProvider
@@ -157,17 +170,22 @@ public async ValueTask<IHolderState> Validate(DateTime now)
157170
.UpdateState(this, new ActiveState(await _fetchTokenTask, _tokenManagerCredentialsProvider));
158171
}
159172

160-
if (now < TokenResponse.ExpiresAt)
173+
if (now < TokenResponse.ExpiredAt)
161174
{
162175
return this;
163176
}
164177

165178
try
166179
{
167-
var tokenHolder = await _fetchTokenTask;
180+
var tokenResponse = await _fetchTokenTask;
181+
182+
_tokenManagerCredentialsProvider.Logger.LogDebug(
183+
"Successfully fetched token. ExpiredAt: {ExpiredAt}, RefreshAt: {RefreshAt}",
184+
tokenResponse.ExpiredAt, tokenResponse.RefreshAt
185+
);
168186

169187
return _tokenManagerCredentialsProvider.UpdateState(this,
170-
new ActiveState(tokenHolder, _tokenManagerCredentialsProvider));
188+
new ActiveState(tokenResponse, _tokenManagerCredentialsProvider));
171189
}
172190
catch (Exception e)
173191
{
@@ -181,7 +199,7 @@ public async ValueTask<IHolderState> Validate(DateTime now)
181199
public void Init() => _fetchTokenTask = _tokenManagerCredentialsProvider.FetchToken().AsTask();
182200
}
183201

184-
private class ErrorState : IHolderState
202+
private class ErrorState : ITokenState
185203
{
186204
private readonly Exception _exception;
187205
private readonly TokenManagerCredentialsProvider _managerCredentialsProvider;
@@ -194,7 +212,7 @@ public ErrorState(Exception exception, TokenManagerCredentialsProvider managerCr
194212

195213
public TokenResponse TokenResponse => throw _exception;
196214

197-
public ValueTask<IHolderState> Validate(DateTime now) => _managerCredentialsProvider
215+
public ValueTask<ITokenState> Validate(DateTime now) => _managerCredentialsProvider
198216
.UpdateState(this, new SyncState(_managerCredentialsProvider))
199217
.Validate(now);
200218
}
@@ -204,21 +222,21 @@ public class TokenResponse
204222
{
205223
private const double RefreshInterval = 0.5;
206224

207-
public TokenResponse(string token, DateTime expiresAt, DateTime? refreshAt = null)
225+
public TokenResponse(string token, DateTime expiredAt, DateTime? refreshAt = null)
208226
{
209227
var now = DateTime.UtcNow;
210228

211229
Token = token;
212-
ExpiresAt = expiresAt.ToUniversalTime();
213-
RefreshAt = refreshAt?.ToUniversalTime() ?? now + (ExpiresAt - now) * RefreshInterval;
230+
ExpiredAt = expiredAt.ToUniversalTime();
231+
RefreshAt = refreshAt?.ToUniversalTime() ?? now + (ExpiredAt - now) * RefreshInterval;
214232
}
215233

216-
internal string Token { get; }
217-
internal DateTime ExpiresAt { get; }
218-
internal DateTime RefreshAt { get; }
234+
public string Token { get; }
235+
public DateTime ExpiredAt { get; }
236+
public DateTime RefreshAt { get; }
219237
}
220238

221-
internal interface IClock
239+
public interface IClock
222240
{
223241
DateTime UtcNow { get; }
224242
}

src/Ydb.Sdk/tests/Auth/TokenManagerCredentialsProviderTests.cs

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ public async Task SyncState_To_ErrorState_To_SyncState_To_ActiveState()
2424
)
2525
.ReturnsAsync(new TokenResponse(Token, now.Add(TimeSpan.FromSeconds(2))));
2626
_mockClock.Setup(clock => clock.UtcNow).Returns(now);
27-
2827
var credentialsProvider = new TokenManagerCredentialsProvider(_mockAuthClient.Object, _mockClock.Object);
2928

3029
await Assert.ThrowsAsync<StatusUnsuccessfulException>(() => credentialsProvider.GetAuthInfoAsync().AsTask());
@@ -53,7 +52,6 @@ public async Task SyncState_To_ActiveState_To_BackgroundState_To_ActiveState()
5352
.Returns(now.Add(TimeSpan.FromSeconds(3)))
5453
.Returns(now.Add(TimeSpan.FromSeconds(3)))
5554
.Returns(now.Add(TimeSpan.FromSeconds(4)));
56-
5755
var credentialsProvider = new TokenManagerCredentialsProvider(_mockAuthClient.Object, _mockClock.Object);
5856

5957
Assert.Equal(Token, await credentialsProvider.GetAuthInfoAsync());
@@ -67,4 +65,24 @@ public async Task SyncState_To_ActiveState_To_BackgroundState_To_ActiveState()
6765

6866
_mockAuthClient.Verify(authClient => authClient.FetchToken(), Times.Exactly(3));
6967
}
68+
69+
[Fact]
70+
public async Task SyncState_To_ActiveState_To_SyncState_To_ActiveState()
71+
{
72+
var now = DateTime.UtcNow;
73+
74+
_mockAuthClient.SetupSequence(authClient => authClient.FetchToken())
75+
.ReturnsAsync(new TokenResponse(Token, now.Add(TimeSpan.FromSeconds(2))))
76+
.ReturnsAsync(new TokenResponse(Token + "1", now.Add(TimeSpan.FromSeconds(10))));
77+
_mockClock.SetupSequence(clock => clock.UtcNow)
78+
.Returns(now)
79+
.Returns(now.Add(TimeSpan.FromSeconds(4)))
80+
.Returns(now.Add(TimeSpan.FromSeconds(4)));
81+
var credentialsProvider = new TokenManagerCredentialsProvider(_mockAuthClient.Object, _mockClock.Object);
82+
83+
Assert.Equal(Token, await credentialsProvider.GetAuthInfoAsync());
84+
Assert.Equal(Token + "1", await credentialsProvider.GetAuthInfoAsync());
85+
Assert.Equal(Token + "1", await credentialsProvider.GetAuthInfoAsync());
86+
_mockAuthClient.Verify(authClient => authClient.FetchToken(), Times.Exactly(2));
87+
}
7088
}

0 commit comments

Comments
 (0)