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 ;
56using System . Security . Cryptography ;
67using System . Text ;
78using System . Text . Json ;
89using System . Text . Json . Serialization ;
9- using Elastic . Documentation . Api . Infrastructure . Aws ;
10- using Microsoft . Extensions . Configuration ;
11- using Microsoft . Extensions . Options ;
1210
1311namespace 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
1715public 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