Skip to content

Commit 9c9a4ea

Browse files
authored
Cache tokens for service principal (#20193)
1 parent c57bde6 commit 9c9a4ea

File tree

4 files changed

+16
-7
lines changed

4 files changed

+16
-7
lines changed

src/Accounts/Accounts/ChangeLog.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
-->
2020

2121
## Upcoming Release
22+
* Enabled caching tokens when logging in with a service principal. This could reduce network traffic and improve performance.
2223

2324
## Version 2.10.3
2425
* Updated `Get-AzSubscription` to retrieve subscription by Id rather than listed all the subscriptions from server if subscription Id is provided. [#19115]

src/Accounts/Authentication.Test/AuthenticatorsTest/ServicePrincipalAuthenticatorTests.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ public async Task ServicePrincipalSecretAuthenticationTest()
7171
//Setup
7272
var mockAzureCredentialFactory = new Mock<AzureCredentialFactory>();
7373
mockAzureCredentialFactory.Setup(f => f.CreateClientSecretCredential(
74-
It.IsAny<string>(), It.IsAny<string>(), It.IsAny<SecureString>(), It.IsAny<ClientCertificateCredentialOptions>())).Returns(() => new TokenCredentialMock());
74+
It.IsAny<string>(), It.IsAny<string>(), It.IsAny<SecureString>(), It.IsAny<ClientSecretCredentialOptions>())).Returns(() => new TokenCredentialMock());
7575

7676
AzureSession.Instance.RegisterComponent(nameof(AzureCredentialFactory), () => mockAzureCredentialFactory.Object, true);
7777
InMemoryTokenCacheProvider cacheProvider = new InMemoryTokenCacheProvider();
@@ -101,7 +101,7 @@ public async Task ServicePrincipalSecretAuthenticationTest()
101101
var token = await authenticator.Authenticate(parameter);
102102

103103
//Verify
104-
mockAzureCredentialFactory.Verify(f => f.CreateClientSecretCredential(TestTenantId, accountId, securePassword, It.IsAny<ClientCertificateCredentialOptions>()), Times.Once());
104+
mockAzureCredentialFactory.Verify(f => f.CreateClientSecretCredential(TestTenantId, accountId, securePassword, It.IsAny<ClientSecretCredentialOptions>()), Times.Once());
105105
Assert.Equal(fakeToken, token.AccessToken);
106106
Assert.Equal(TestTenantId, token.TenantId);
107107
}

src/Accounts/Authenticators/Factories/AzureCredentialFactory.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ public virtual TokenCredential CreateManagedIdentityCredential(string clientId)
2727
return new ManagedIdentityCredential(clientId);
2828
}
2929

30-
public virtual TokenCredential CreateClientSecretCredential(string tenantId, string clientId, SecureString secret, ClientCertificateCredentialOptions options)
30+
public virtual TokenCredential CreateClientSecretCredential(string tenantId, string clientId, SecureString secret, ClientSecretCredentialOptions options)
3131
{
3232
return new ClientSecretCredential(tenantId, clientId, secret.ConvertToString(), options);
3333
}

src/Accounts/Authenticators/ServicePrincipalAuthenticator.cs

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ public class ServicePrincipalAuthenticator : DelegatingAuthenticator
3030
{
3131
private const string AuthenticationFailedMessage = "No certificate thumbprint or secret provided for the given service principal '{0}'.";
3232

33-
//MSAL doesn't cache Service Principal into msal.cache
33+
// MSAL doesn't cache the secret of Service Principal, but it caches access tokens
3434
public override Task<IAccessToken> Authenticate(AuthenticationParameters parameters, CancellationToken cancellationToken)
3535
{
3636
var spParameters = parameters as ServicePrincipalParameters;
@@ -43,10 +43,12 @@ public override Task<IAccessToken> Authenticate(AuthenticationParameters paramet
4343
var authority = spParameters.Environment.ActiveDirectoryAuthority;
4444

4545
var requestContext = new TokenRequestContext(scopes);
46+
var tokenCachePersistenceOptions = spParameters.TokenCacheProvider.GetTokenCachePersistenceOptions();
4647
AzureSession.Instance.TryGetComponent(nameof(AzureCredentialFactory), out AzureCredentialFactory azureCredentialFactory);
4748

4849
var options = new ClientCertificateCredentialOptions()
4950
{
51+
TokenCachePersistenceOptions = tokenCachePersistenceOptions, // allows MSAL to cache access tokens
5052
AuthorityHost = new Uri(authority),
5153
SendCertificateChain = spParameters.SendCertificateChain ?? default(bool)
5254
};
@@ -63,10 +65,15 @@ public override Task<IAccessToken> Authenticate(AuthenticationParameters paramet
6365
else if (spParameters.Secret != null)
6466
{
6567
//Service principal with secret
66-
tokenCredential = azureCredentialFactory.CreateClientSecretCredential(tenantId, spParameters.ApplicationId, spParameters.Secret, options);
67-
parametersLog = $"- ApplicationId:'{spParameters.ApplicationId}', TenantId:'{tenantId}', Scopes:'{string.Join(",", scopes)}', AuthorityHost:'{options.AuthorityHost}'";
68+
var csOptions = new ClientSecretCredentialOptions()
69+
{
70+
TokenCachePersistenceOptions = tokenCachePersistenceOptions, // allows MSAL to cache access tokens
71+
AuthorityHost = new Uri(authority)
72+
};
73+
tokenCredential = azureCredentialFactory.CreateClientSecretCredential(tenantId, spParameters.ApplicationId, spParameters.Secret, csOptions);
74+
parametersLog = $"- ApplicationId:'{spParameters.ApplicationId}', TenantId:'{tenantId}', Scopes:'{string.Join(",", scopes)}', AuthorityHost:'{csOptions.AuthorityHost}'";
6875
}
69-
else if(!string.IsNullOrEmpty(spParameters.CertificatePath))
76+
else if (!string.IsNullOrEmpty(spParameters.CertificatePath))
7077
{
7178
if (spParameters.CertificateSecret != null)
7279
{
@@ -86,6 +93,7 @@ public override Task<IAccessToken> Authenticate(AuthenticationParameters paramet
8693
{
8794
throw new MsalException(MsalError.AuthenticationFailed, string.Format(AuthenticationFailedMessage, clientId));
8895
}
96+
8997
return MsalAccessToken.GetAccessTokenAsync(
9098
nameof(ServicePrincipalAuthenticator),
9199
parametersLog,

0 commit comments

Comments
 (0)