in src/JetBrains.Space.AspNetCore.Authentication/Experimental/TokenManagement/SpaceTokenManagementCookieEvents.cs [61:138]
public override async Task ValidatePrincipal(CookieValidatePrincipalContext context)
{
var tokens = context.Properties.GetTokens().ToList();
if (tokens.Count == 0)
{
_logger.LogDebug("No tokens found in cookie properties. SaveTokens must be enabled for automatic token refresh.");
return;
}
var refreshToken = tokens.SingleOrDefault(t => t.Name == "refresh_token");
if (refreshToken == null)
{
_logger.LogWarning("No refresh token found in cookie properties. A refresh token must be requested and SaveTokens must be enabled.");
return;
}
var expiresAt = tokens.SingleOrDefault(t => t.Name == "expires_at");
if (expiresAt == null)
{
_logger.LogWarning("No expires_at value found in cookie properties.");
return;
}
var dtExpires = DateTimeOffset.Parse(expiresAt.Value, CultureInfo.InvariantCulture);
var dtRefresh = dtExpires.Subtract(_options.RefreshBeforeExpiration);
if (dtRefresh < _clock.UtcNow)
{
var shouldRefresh = PendingRefreshTokenRequests.TryAdd(refreshToken.Value, true);
if (shouldRefresh)
{
try
{
var spaceOptions = await GetSpaceOptionsAsync();
var httpClient = _httpClientFactory.CreateClient();
var spaceTokenRequest = new HttpRequestMessage(HttpMethod.Post, spaceOptions.TokenEndpoint)
{
Headers =
{
Authorization = AuthenticationHeaderValue.Parse(
"Basic " + Convert.ToBase64String(Encoding.UTF8.GetBytes($"{spaceOptions.ClientId}:{spaceOptions.ClientSecret}")))
},
Content = new FormUrlEncodedContent(new []
{
new KeyValuePair<string?, string?>("grant_type", "refresh_token"),
new KeyValuePair<string?, string?>("refresh_token", refreshToken.Value),
new KeyValuePair<string?, string?>("scope", string.Join(" ", spaceOptions.Scope))
})
}.WithClientAndSdkHeaders(SdkInfo.Version);
var spaceTokenResponse = await httpClient.SendAsync(spaceTokenRequest);
if (!spaceTokenResponse.IsSuccessStatusCode)
{
_logger.LogWarning("Error refreshing token: {StatusCode} {Message}", spaceTokenResponse.StatusCode, spaceTokenResponse.ReasonPhrase);
context.RejectPrincipal();
return;
}
using var spaceTokenDocument = await JsonDocument.ParseAsync(await spaceTokenResponse.Content.ReadAsStreamAsync());
var spaceToken = spaceTokenDocument.RootElement;
context.Properties.UpdateTokenValue("access_token", spaceToken.GetStringValue("access_token")!);
context.Properties.UpdateTokenValue("refresh_token", spaceToken.GetStringValue("refresh_token") ?? refreshToken.Value);
var newExpiresAt = DateTime.UtcNow + TimeSpan.FromSeconds(spaceToken.GetInt32Value("expires_in"));
context.Properties.UpdateTokenValue("expires_at", newExpiresAt.ToString("o", CultureInfo.InvariantCulture));
await context.HttpContext.SignInAsync(context.Principal!, context.Properties);
}
finally
{
PendingRefreshTokenRequests.TryRemove(refreshToken.Value, out _);
}
}
}
}