Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/Maestro/Maestro.Common/AppCredentials/AppCredential.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ public static AppCredential CreateUserCredential(string appId, string userScope
public static AppCredential CreateUserCredential(string appId, TokenRequestContext requestContext)
{
var authRecordPath = Path.Combine(AUTH_CACHE, $"{AUTH_RECORD_PREFIX}-{appId}");
var credential = GetInteractiveCredential(appId, authRecordPath);
var interactiveCredential = GetInteractiveCredential(appId, authRecordPath);
var credential = new ChainedTokenCredential(interactiveCredential, new AzureCliCredential());

Comment thread
premun marked this conversation as resolved.
Outdated
return new AppCredential(credential, requestContext);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,54 @@ public override AccessToken GetToken(TokenRequestContext requestContext, Cancell
{
CacheAuthenticationRecord(requestContext, cancellationToken);

try
{
return GetTokenCore(requestContext, cancellationToken);
}
catch (Exception e) when (IsMsalCachePersistenceException(e))
{
RecreateCredentialsWithoutPersistence();
try
{
return GetTokenCore(requestContext, cancellationToken);
}
catch (AuthenticationFailedException retryEx)
{
// After persistence fallback, if interactive auth still fails, signal credential unavailability
// so ChainedTokenCredential can try the next credential (e.g. AzureCliCredential)
throw new CredentialUnavailableException(
"Interactive authentication failed after token cache persistence fallback. "
+ "Ensure a browser or device code flow is available, or use 'az login' as a fallback.", retryEx);
}
Comment thread
lewing marked this conversation as resolved.
}
}

public override async ValueTask<AccessToken> GetTokenAsync(TokenRequestContext requestContext, CancellationToken cancellationToken)
{
CacheAuthenticationRecord(requestContext, cancellationToken);

try
{
return await GetTokenCoreAsync(requestContext, cancellationToken);
}
catch (Exception e) when (IsMsalCachePersistenceException(e))
{
RecreateCredentialsWithoutPersistence();
try
{
return await GetTokenCoreAsync(requestContext, cancellationToken);
}
catch (AuthenticationFailedException retryEx)
{
throw new CredentialUnavailableException(
"Interactive authentication failed after token cache persistence fallback. "
+ "Ensure a browser or device code flow is available, or use 'az login' as a fallback.", retryEx);
}
}
}

private AccessToken GetTokenCore(TokenRequestContext requestContext, CancellationToken cancellationToken)
{
if (Volatile.Read(ref _isDeviceCodeFallback) == 1)
{
return _deviceCodeCredential.GetToken(requestContext, cancellationToken);
Expand All @@ -68,10 +116,8 @@ public override AccessToken GetToken(TokenRequestContext requestContext, Cancell
}
}

public override async ValueTask<AccessToken> GetTokenAsync(TokenRequestContext requestContext, CancellationToken cancellationToken)
private async ValueTask<AccessToken> GetTokenCoreAsync(TokenRequestContext requestContext, CancellationToken cancellationToken)
{
CacheAuthenticationRecord(requestContext, cancellationToken);

if (Volatile.Read(ref _isDeviceCodeFallback) == 1)
{
return await _deviceCodeCredential.GetTokenAsync(requestContext, cancellationToken);
Expand Down Expand Up @@ -109,9 +155,6 @@ private void CacheAuthenticationRecord(TokenRequestContext requestContext, Cance
Directory.CreateDirectory(authRecordDir);
}

static bool IsMsalCachePersistenceException(Exception e) =>
e is MsalCachePersistenceException || (e.InnerException is not null && IsMsalCachePersistenceException(e.InnerException));

AuthenticationRecord authRecord;
try
{
Expand All @@ -121,16 +164,7 @@ static bool IsMsalCachePersistenceException(Exception e) =>
catch (Exception e) when (IsMsalCachePersistenceException(e))
{
// If we cannot persist the token cache, fall back to interactive authentication without persistence
_browserCredential = new InteractiveBrowserCredential(new InteractiveBrowserCredentialOptions()
{
TenantId = _options.TenantId,
ClientId = _options.ClientId,
});
_deviceCodeCredential = new DeviceCodeCredential(new()
{
TenantId = _options.TenantId,
ClientId = _options.ClientId,
});
RecreateCredentialsWithoutPersistence();
authRecord = Authenticate(requestContext, cancellationToken);
}

Expand All @@ -153,4 +187,21 @@ private AuthenticationRecord Authenticate(TokenRequestContext requestContext, Ca
return _deviceCodeCredential.Authenticate(requestContext, cancellationToken);
}
}

private void RecreateCredentialsWithoutPersistence()
{
_browserCredential = new InteractiveBrowserCredential(new InteractiveBrowserCredentialOptions()
{
TenantId = _options.TenantId,
ClientId = _options.ClientId,
});
_deviceCodeCredential = new DeviceCodeCredential(new()
{
TenantId = _options.TenantId,
ClientId = _options.ClientId,
});
}
Comment thread
lewing marked this conversation as resolved.

private static bool IsMsalCachePersistenceException(Exception e) =>
e is MsalCachePersistenceException || (e.InnerException is not null && IsMsalCachePersistenceException(e.InnerException));
}
Loading