Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: Update .NET sample to use MSAL best practices #1146

Merged
merged 4 commits into from
Dec 11, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
31 changes: 25 additions & 6 deletions examples/msal-net/akvdotnet/TokenCredential.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,14 @@
using Microsoft.Identity.Client;
using System.Threading;
using System.Threading.Tasks;
using System.Data;
// <directives>

public class MyClientAssertionCredential : TokenCredential
{
private readonly IConfidentialClientApplication _confidentialClientApp;
private DateTimeOffset _lastRead;
private string _lastJWT = null;

public MyClientAssertionCredential()
{
Expand All @@ -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)
Expand Down Expand Up @@ -55,9 +63,20 @@ public override async ValueTask<AccessToken> GetTokenAsync(TokenRequestContext r
return new AccessToken(result.AccessToken, result.ExpiresOn);
}

public string ReadJWTFromFS(string tokenPath)
/// <summary>
/// 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.
/// </summary>
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;
}
}