Skip to content

Commit b1fa4da

Browse files
committed
Add GcpIdToken cache
1 parent 869314d commit b1fa4da

File tree

1 file changed

+23
-7
lines changed

1 file changed

+23
-7
lines changed

src/api/Elastic.Documentation.Api.Infrastructure/Gcp/GcpIdTokenProvider.cs

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,30 @@
22
// Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
33
// See the LICENSE file in the project root for more information
44

5+
using System.Collections.Concurrent;
56
using System.Security.Cryptography;
67
using System.Text;
78
using System.Text.Json;
89
using System.Text.Json.Serialization;
9-
using Elastic.Documentation.Api.Infrastructure.Aws;
10-
using Microsoft.Extensions.Configuration;
11-
using Microsoft.Extensions.Options;
1210

1311
namespace Elastic.Documentation.Api.Infrastructure.Gcp;
1412

1513
// This is a custom implementation to create an ID token for GCP.
1614
// Because Google.Api.Auth.OAuth2 is not compatible with AOT
1715
public class GcpIdTokenProvider(HttpClient httpClient)
1816
{
17+
// Cache tokens by target audience to avoid regenerating them on every request
18+
private readonly ConcurrentDictionary<string, CachedToken> _tokenCache = new();
19+
20+
private record CachedToken(string Token, DateTimeOffset ExpiresAt);
21+
1922
public async Task<string> GenerateIdTokenAsync(string serviceAccount, string targetAudience, Cancel cancellationToken = default)
2023
{
24+
// Check if we have a valid cached token
25+
if (_tokenCache.TryGetValue(targetAudience, out var cachedToken) &&
26+
cachedToken.ExpiresAt > DateTimeOffset.UtcNow.AddMinutes(1)) // Refresh 1 minute before expiry
27+
return cachedToken.Token;
28+
2129
// Read and parse service account key file using System.Text.Json source generation (AOT compatible)
2230
var serviceAccountJson = JsonSerializer.Deserialize(serviceAccount, GcpJsonContext.Default.ServiceAccountKey);
2331

@@ -27,13 +35,14 @@ public async Task<string> GenerateIdTokenAsync(string serviceAccount, string tar
2735
var headerBase64 = Base64UrlEncode(Encoding.UTF8.GetBytes(headerJson));
2836

2937
// Create JWT payload
30-
var now = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
38+
var now = DateTimeOffset.UtcNow;
39+
var expirationTime = now.AddHours(1);
3140
var payload = new JwtPayload(
3241
serviceAccountJson.ClientEmail,
3342
serviceAccountJson.ClientEmail,
3443
"https://oauth2.googleapis.com/token",
35-
now,
36-
now + 300, // 5 minutes
44+
now.ToUnixTimeSeconds(),
45+
expirationTime.ToUnixTimeSeconds(),
3746
targetAudience
3847
);
3948

@@ -61,7 +70,14 @@ public async Task<string> GenerateIdTokenAsync(string serviceAccount, string tar
6170
var jwt = $"{message}.{signatureBase64}";
6271

6372
// Exchange JWT for ID token
64-
return await ExchangeJwtForIdToken(jwt, targetAudience, cancellationToken);
73+
var idToken = await ExchangeJwtForIdToken(jwt, targetAudience, cancellationToken);
74+
75+
var expiresAt = expirationTime.Subtract(TimeSpan.FromMinutes(1));
76+
_ = _tokenCache.AddOrUpdate(targetAudience,
77+
new CachedToken(idToken, expiresAt),
78+
(_, _) => new CachedToken(idToken, expiresAt));
79+
80+
return idToken;
6581
}
6682

6783

0 commit comments

Comments
 (0)