Application that persists User Access and Refresh token works with .Net SDK v10.1.0, fails with :
"The given key 'Retry-After' was not present in the dictionary" error
at System.ThrowHelper.ThrowKeyNotFoundException[T](T key)
at System.Collections.Generic.Dictionary`2.get_Item(TKey key)
at Box.Sdk.Gen.BoxRetryStrategy.RetryAfter(FetchOptions fetchOptions, FetchResponse fetchResponse, Int32 attemptNumber)
at Box.Sdk.Gen.Internal.BoxNetworkClient.<Box-Sdk-Gen-INetworkClient-FetchAsync>d__3.MoveNext()
at Box.Sdk.Gen.Managers.FoldersManager.<GetFolderByIdAsync>d__8.MoveNext()
at Program.<<Main>$>d__0.MoveNext() in C:\Users\xxxxx\source\repos\BoxTestTokenStore\Program.cs:line 28
A sample console test app code fragments to recreate:
Simple Model for token persistence:
namespace BoxTestTokenStore
{
internal class TokenData
{
public string AccessToken { get; set; } = string.Empty;
public string RefreshToken { get; set; } = string.Empty;
}
}
Custom Token store code:
using Box.Sdk.Gen;
using Box.Sdk.Gen.Schemas;
using System.Text.Json;
using File = System.IO.File;
using Task = System.Threading.Tasks.Task;
namespace BoxTestTokenStore
{
internal class JsonFileTokenStore(string filePath) : ITokenStorage
{
private bool activeLoaded = false;
private TokenData ActiveToken { get; set; } = new();
private AccessToken FromActiveTokenData { get => new() { AccessTokenField = ActiveToken.AccessToken,RefreshToken = ActiveToken.RefreshToken }; }
public async Task ClearAsync()
{
ActiveToken = new();
await StoreAsync(FromActiveTokenData);
}
private async Task<bool> LoadAsync()
{
if (activeLoaded)
return true;
var json = await File.ReadAllTextAsync(filePath);
var tokenData = JsonSerializer.Deserialize<TokenData>(json);
if (tokenData is null)
return false;
ActiveToken = tokenData;
activeLoaded = true;
return true;
}
public async Task<AccessToken?> GetAsync()
{
if (!activeLoaded)
{
// first time load
var checkLoad = await LoadAsync();
if (checkLoad == false)
throw new Exception("Load failed");
}
return FromActiveTokenData;
}
public async Task StoreAsync(AccessToken token)
{
ActiveToken = new() { AccessToken = token.AccessTokenField ?? string.Empty, RefreshToken = token.RefreshToken ?? string.Empty };
var json = JsonSerializer.Serialize(ActiveToken, new JsonSerializerOptions { WriteIndented = true });
await File.WriteAllTextAsync(filePath, json);
}
}
}
Json file:
{
"AccessToken": "VALID_ACCESS_TOKEN",
"RefreshToken": "VALID_REFRESH_TOKEN"
}
Program.cs (top level):
using Box.Sdk.Gen;
using BoxTestTokenStore;
using System.Text.Json;
string clientId = "YOUR_CLIENT_ID";
string clientSecret = "YOUR_CLIENT_SECRET";
string tokenFilePath = "box_oauth_token.json";
string tokenFilePath = "box_oauth_token.json";
var tokenStorage = new JsonFileTokenStore(tokenFilePath);
var config = new OAuthConfig(clientId: clientId, clientSecret: clientSecret, tokenStorage: tokenStorage);
var auth = new BoxOAuth(config: config);
var client = new BoxClient(auth);
// trigger token store token management - proof that tokens are valid will error on v10.2.0 if JSON token is stale.
var root = await client.Folders.GetFolderByIdAsync("0");
// generous guess for Access token expires in , could be already stale
long expiresIn =4800;
// Wait for access token to expire (plus a 5 min buffer - to be sure)
await Task.Delay(TimeSpan.FromSeconds(expiresIn + (60*5)));
// repeat trigger token store token management => this fail v10.2.0, passes v10.1.0
root = await client.Folders.GetFolderByIdAsync("0");
Console.WriteLine("Done");
