-
Notifications
You must be signed in to change notification settings - Fork 395
Description
Library version used
4.77.0
.NET version
9.0.308
Scenario
PublicClient - mobile app
Is this a new or an existing app?
The app is in production, and I have upgraded to a new version of MSAL
Issue description and reproduction steps
Library version : 4.77.0
<PackageReference Include="Microsoft.Identity.Client" Version="4.77.0" /> <PackageReference Include="Microsoft.Identity.Client.Extensions.Msal" Version="4.77.0" />
.Net Version 9.0.308
Exception:
Exception: MSAL.Xamarin.Android.4.77.0.0.MsalClientException: ErrorCode: authentication_canceled Microsoft.Identity.Client.MsalClientException: User canceled authentication. On an Android device, this could be due to the lack of capabilities, such as custom tabs, for the system browser. See https://aka.ms/msal-net-system-browsers for more information.
StackTrace:
at Microsoft.Identity.Client.Internal.AuthCodeRequestComponent.VerifyAuthorizationResult(AuthorizationResult authorizationResult, String originalState)
at Microsoft.Identity.Client.Internal.AuthCodeRequestComponent.FetchAuthCodeAndPkceInternalAsync(IWebUI webUi, CancellationToken cancellationToken)
at Microsoft.Identity.Client.Internal.AuthCodeRequestComponent.FetchAuthCodeAndPkceVerifierAsync(CancellationToken cancellationToken)
at Microsoft.Identity.Client.Internal.Requests.InteractiveRequest.GetTokenResponseAsync(CancellationToken cancellationToken)
at Microsoft.Identity.Client.Internal.Requests.InteractiveRequest.ExecuteAsync(CancellationToken cancellationToken)
at Microsoft.Identity.Client.Internal.Requests.RequestBase.<>c__DisplayClass11_1.<b__1>d.MoveNext()
--- End of stack trace from previous location ---
at Microsoft.Identity.Client.Utils.StopwatchService.MeasureCodeBlockAsync(Func1 codeBlock) at Microsoft.Identity.Client.Internal.Requests.RequestBase.RunAsync(CancellationToken cancellationToken) at Microsoft.Identity.Client.ApiConfig.Executors.PublicClientExecutor.ExecuteAsync(AcquireTokenCommonParameters commonParameters, AcquireTokenInteractiveParameters interactiveParameters, CancellationToken cancellationToken) at Jacobs.Services.MSAuthService.PerformInteractiveAuthenticationAsync(String[] scopes) at Jacobs.Services.MSAuthService.<>c__DisplayClass5_01.<b__0>d[[Microsoft.Identity.Client.AuthenticationResult, Microsoft.Identity.Client, Version=4.77.0.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae]].MoveNext()
Relevant code snippets
`public async Task<bool> GetAccessToken()
{
// Gets the available accounts
var accounts = await _pca.GetAccountsAsync();
// Stores first account available
Common.Account = accounts.FirstOrDefault();
if (Common.Account != null)
{
try
{
// This call acquires the access token silently without asking for user credentials
var authResult = await _pca.AcquireTokenSilent(Configurations.Scopes, Common.Account).ExecuteAsync();
// Stores the access token
AppConstants.AccessToken = authResult.AccessToken;
return true;
}
catch (MsalUiRequiredException ex)
{
///add exception logging
}
}
// Interactive auth needed - ensure we're on UI thread for the entire operation
var interactiveResult = await InvokeOnMainThreadAsync(() =>
PerformInteractiveAuthenticationAsync(Configurations.Scopes));
if (interactiveResult != null)
{
AppConstants.AccessToken = interactiveResult.AccessToken;
await GetHttpContentWithTokenAsync(interactiveResult.AccessToken);
// Handle iOS cookie capture
if (DeviceInfo.Platform == DevicePlatform.iOS)
{
AppConstants.OauthCookie = _cookieStore.CurrentCookies
.FirstOrDefault(cc => cc.Name == "stsservicecookie");
}
Common.Account = interactiveResult.Account;
return true;
}
return false;
}
// Helper to ensure code runs on UI thread and properly awaits completion
private async Task<T> InvokeOnMainThreadAsync<T>(Func<Task<T>> action)
{
TaskCompletionSource<T> tcs = new TaskCompletionSource<T>();
MainThread.BeginInvokeOnMainThread(async () => {
try
{
var result = await action();
tcs.SetResult(result);
}
catch (Exception ex)
{
if (Common.MSAuthService != null && Common.DataService != null)
{
await Common.DataService.LogToAzureStorage(Common.MSAuthService, Common.LogException("InvokeOnMainThreadAsync", ex.Message, ex.StackTrace));
}
CrashlyticsLogger.LogException("InvokeOnMainThreadAsync", ex);
tcs.SetException(ex);
}
});
return await tcs.Task;
}
private async Task<AuthenticationResult> PerformInteractiveAuthenticationAsync(string[] scopes)
{
try
{
// Get appropriate parent for current platform
object parent = null;
if (DeviceInfo.Platform == DevicePlatform.Android)
{
#if ANDROID
parent = Platform.CurrentActivity;
#endif
}
else if (DeviceInfo.Platform == DevicePlatform.iOS)
{
#if IOS
parent = UIKit.UIApplication.SharedApplication.KeyWindow?.RootViewController;
#endif
}
// Configure and execute the interactive authentication
var builder = _pca.AcquireTokenInteractive(scopes)
.WithParentActivityOrWindow(parent)
.WithUseEmbeddedWebView(true);
// Important: We're already on the UI thread, so no ConfigureAwait(false) here
return await builder.ExecuteAsync();
}
catch (Exception ex)
{
Console.WriteLine($"Interactive auth error: {ex.GetType().Name} - {ex.Message}");
throw;
}
}
`Expected behavior
30% of users facing this issue
Please help me out this one
Identity provider
Microsoft Entra ID (Work and School accounts and Personal Microsoft accounts)
Regression
4.77.0
Solution and workarounds
No response