diff --git a/src/Tgstation.Server.Host/Utils/GitHub/GitHubClientFactory.cs b/src/Tgstation.Server.Host/Utils/GitHub/GitHubClientFactory.cs
index 2a1554f0e9..00a99c6330 100644
--- a/src/Tgstation.Server.Host/Utils/GitHub/GitHubClientFactory.cs
+++ b/src/Tgstation.Server.Host/Utils/GitHub/GitHubClientFactory.cs
@@ -30,6 +30,11 @@ namespace Tgstation.Server.Host.Utils.GitHub
/// Set to app installation token lifetime, which is the lowest. See https://docs.github.com/en/apps/creating-github-apps/authenticating-with-a-github-app/generating-an-installation-access-token-for-a-github-app.
const uint ClientCacheHours = 1;
+ ///
+ /// Minutes before tokens expire before not using them.
+ ///
+ const uint AppTokenExpiryGraceMinutes = 15;
+
///
/// The used in place of when accessing a configuration-based client with no token set in .
///
@@ -56,9 +61,9 @@ namespace Tgstation.Server.Host.Utils.GitHub
readonly GeneralConfiguration generalConfiguration;
///
- /// Cache of created s and last used times, keyed by access token.
+ /// Cache of created s and last used/expiry times, keyed by access token.
///
- readonly Dictionary clientCache;
+ readonly Dictionary clientCache;
///
/// The used to guard access to .
@@ -83,7 +88,7 @@ namespace Tgstation.Server.Host.Utils.GitHub
this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
generalConfiguration = generalConfigurationOptions?.Value ?? throw new ArgumentNullException(nameof(generalConfigurationOptions));
- clientCache = new Dictionary();
+ clientCache = new Dictionary();
clientCacheSemaphore = new SemaphoreSlim(1, 1);
}
@@ -137,13 +142,20 @@ namespace Tgstation.Server.Host.Utils.GitHub
else
cacheKey = accessString;
- cacheHit = clientCache.TryGetValue(cacheKey, out var tuple);
-
var now = DateTimeOffset.UtcNow;
- if (!cacheHit)
+ cacheHit = clientCache.TryGetValue(cacheKey, out var tuple);
+ var tokenValid = cacheHit && (!tuple.Expiry.HasValue || tuple.Expiry.Value <= now);
+ if (!tokenValid)
{
+ if (cacheHit)
+ {
+ logger.LogDebug("Previously cached GitHub token has expired!");
+ clientCache.Remove(cacheKey);
+ }
+
logger.LogTrace("Creating new GitHubClient...");
+ DateTimeOffset? expiry = null;
if (accessString != null)
{
if (accessString.StartsWith(RepositorySettings.TgsAppPrivateKeyPrefix))
@@ -177,6 +189,7 @@ namespace Tgstation.Server.Host.Utils.GitHub
var installToken = await client.GitHubApps.CreateInstallationToken(installation.Id);
client.Credentials = new Credentials(installToken.Token);
+ expiry = installToken.ExpiresAt.AddMinutes(-AppTokenExpiryGraceMinutes);
}
catch (Exception ex)
{
@@ -193,7 +206,7 @@ namespace Tgstation.Server.Host.Utils.GitHub
else
client = CreateUnauthenticatedClient();
- clientCache.Add(cacheKey, (Client: client, LastUsed: now));
+ clientCache.Add(cacheKey, (Client: client, LastUsed: now, Expiry: expiry));
lastUsed = null;
}
else
@@ -213,7 +226,7 @@ namespace Tgstation.Server.Host.Utils.GitHub
continue; // save the hash lookup
tuple = clientCache[key];
- if (tuple.LastUsed <= purgeAfter)
+ if (tuple.LastUsed <= purgeAfter || (tuple.Expiry.HasValue && tuple.Expiry.Value <= now))
{
clientCache.Remove(key);
++purgeCount;