From 6217742d368b0fb1f882f1336aca57acdbc678f1 Mon Sep 17 00:00:00 2001 From: Bogdan Gavril Date: Mon, 11 Dec 2023 18:50:18 +0000 Subject: [PATCH] chore: Update .NET sample to use MSAL best practices (#1146) Co-authored-by: Anish Ramasekar --- .../msal-net/akvdotnet/TokenCredential.cs | 31 +++++++++++++++---- 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/examples/msal-net/akvdotnet/TokenCredential.cs b/examples/msal-net/akvdotnet/TokenCredential.cs index d81b11db8..9be73f242 100644 --- a/examples/msal-net/akvdotnet/TokenCredential.cs +++ b/examples/msal-net/akvdotnet/TokenCredential.cs @@ -4,11 +4,14 @@ using Microsoft.Identity.Client; using System.Threading; using System.Threading.Tasks; +using System.Data; // public class MyClientAssertionCredential : TokenCredential { private readonly IConfidentialClientApplication _confidentialClientApp; + private DateTimeOffset _lastRead; + private string _lastJWT = null; public MyClientAssertionCredential() { @@ -17,14 +20,19 @@ public MyClientAssertionCredential() // AZURE_CLIENT_ID with the clientID set in the service account annotation // AZURE_TENANT_ID with the tenantID set in the service account annotation. If not defined, then // the tenantID provided via azure-wi-webhook-config for the webhook will be used. + // AZURE_AUTHORITY_HOST is the Microsoft Entra authority host. It is https://login.microsoftonline.com" for the public cloud. // AZURE_FEDERATED_TOKEN_FILE is the service account token path var clientID = Environment.GetEnvironmentVariable("AZURE_CLIENT_ID"); var tokenPath = Environment.GetEnvironmentVariable("AZURE_FEDERATED_TOKEN_FILE"); var tenantID = Environment.GetEnvironmentVariable("AZURE_TENANT_ID"); + var host = Environment.GetEnvironmentVariable("AZURE_AUTHORITY_HOST"); - _confidentialClientApp = ConfidentialClientApplicationBuilder.Create(clientID) - .WithClientAssertion(ReadJWTFromFS(tokenPath)) - .WithTenantId(tenantID).Build(); + _confidentialClientApp = ConfidentialClientApplicationBuilder + .Create(clientID) + .WithAuthority(host, tenantID) + .WithClientAssertion(() => ReadJWTFromFSOrCache(tokenPath)) // ReadJWTFromFS should always return a non-expired JWT + .WithCacheOptions(CacheOptions.EnableSharedCacheOptions) // cache the the AAD tokens in memory + .Build(); } public override AccessToken GetToken(TokenRequestContext requestContext, CancellationToken cancellationToken) @@ -55,9 +63,20 @@ public override async ValueTask GetTokenAsync(TokenRequestContext r return new AccessToken(result.AccessToken, result.ExpiresOn); } - public string ReadJWTFromFS(string tokenPath) + /// + /// Read the JWT from the file system, but only do this every few minutes to avoid heavy I/O. + /// The JWT lifetime is anywhere from 1 to 24 hours, so we can safely cache the value for a few minutes. + /// + private string ReadJWTFromFSOrCache(string tokenPath) { - string text = System.IO.File.ReadAllText(tokenPath); - return text; + // read only once every 5 minutes + if (_lastJWT == null || + DateTimeOffset.UtcNow.Subtract(_lastRead) > TimeSpan.FromMinutes(5)) + { + _lastRead = DateTimeOffset.UtcNow; + _lastJWT = System.IO.File.ReadAllText(tokenPath); + } + + return _lastJWT; } }