Skip to content

[Bug] Android: White space appears above keyboard when tapping input fields in embedded WebView authentication #5746

@szviktor94

Description

@szviktor94

Library version used

4.78.0

.NET version

Platform: Android (MAUI)
Target Framework: net9.0-android

Scenario

Other - please specify

Is this a new or an existing app?

The app is in production, I haven't upgraded MSAL, but started seeing this issue

Issue description and reproduction steps

When using AcquireTokenInteractive with an embedded WebView (WithUseEmbeddedWebView(true)) on Android, tapping on input fields (username/password) causes the keyboard to appear with a large white/blank space above it. The white space height is approximately equal to the keyboard height.

Steps to Reproduce
1. Configure MSAL with embedded WebView:
result = await _pca.AcquireTokenInteractive(scopes)
.WithParentActivityOrWindow(parentWindow)
.WithUseEmbeddedWebView(true)
.ExecuteAsync();
2. Launch the interactive authentication flow
3. When the login page appears, tap on the username or password input field
Observe the keyboard appearing with a large white space above it
The white space is scrollable with the page itself. When the keyboard is hidden then the white gap disappears.

Test phones used:

  • Samsung Galaxy A56
  • Pixel 4a

Image

Image

Relevant code snippets

Expected behavior

Expected Behavior
The keyboard should appear normally without any white space between the keyboard and the WebView content. The WebView should resize or pan correctly to accommodate the keyboard.
Actual Behavior
A white/blank space appears above the keyboard. The space height is approximately equal to the keyboard height, suggesting the bottom inset is being applied twice.

Identity provider

Azure B2C Custom Policy

Regression

We used the same 4.78.0 version but we didn't notice this behavior before

Solution and workarounds

After investigation, the issue appears to be in how AuthenticationAgentActivity handles window insets. The activity uses edge-to-edge layout with SetDecorFitsSystemWindows(false) and implements OnApplyWindowInsetsListener.
When the keyboard (IME) appears, both system bar insets and IME insets are reported. The current implementation appears to cause double-application of the bottom inset:
• System bar bottom inset (navigation bar height, ~48dp)
• IME bottom inset (keyboard height, ~300dp)
When combined or applied sequentially, this results in excessive bottom spacing.

Workaround
We implemented a workaround by overriding the WebView's inset listener via ActivityLifecycleCallbacks:
// In MainApplication.cs
protected override MauiApp CreateMauiApp()
{
RegisterActivityLifecycleCallbacks(new MsalActivityLifecycleCallbacks());
// ...
}
private class MsalActivityLifecycleCallbacks : Java.Lang.Object, Android.App.Application.IActivityLifecycleCallbacks
{
private const string MsalActivityName = "AuthenticationAgentActivity";
public void OnActivityResumed(Activity activity)
{
if (activity.GetType().Name.Contains(MsalActivityName) && activity.Window != null)
{
var webView = FindWebView(activity.Window.DecorView);
if (webView is not null)
{
ViewCompat.SetOnApplyWindowInsetsListener(webView, new MsalWebViewInsetsListener());
}
}
}
// Other interface methods...

private static Android.Webkit.WebView? FindWebView(Android.Views.View? view)
{
    if (view == null) return null;
    if (view is Android.Webkit.WebView webView) return webView;

if (view is ViewGroup viewGroup)
{
for (int i = 0; i < viewGroup.ChildCount; i++)
{
var result = FindWebView(viewGroup.GetChildAt(i));
if (result is not null) return result;
}
}
return null;
}
}
private class MsalWebViewInsetsListener : Java.Lang.Object, IOnApplyWindowInsetsListener
{
public WindowInsetsCompat OnApplyWindowInsets(Android.Views.View? view, WindowInsetsCompat? insets)
{
if (view == null || insets == null)
return insets ?? new WindowInsetsCompat.Builder().Build();
var systemBars = insets.GetInsets(WindowInsetsCompat.Type.SystemBars());
var ime = insets.GetInsets(WindowInsetsCompat.Type.Ime());
var isKeyboardVisible = insets.IsVisible(WindowInsetsCompat.Type.Ime());
// Key fix: use only ONE source for bottom inset, not both
int bottomMargin = isKeyboardVisible ? ime.Bottom : systemBars.Bottom;
if (view.LayoutParameters is ViewGroup.MarginLayoutParams marginParams)
{
marginParams.LeftMargin = systemBars.Left;
marginParams.TopMargin = systemBars.Top;
marginParams.RightMargin = systemBars.Right;
marginParams.BottomMargin = bottomMargin;
view.LayoutParameters = marginParams;
}
return WindowInsetsCompat.Consumed;
}
}
The key fix is using only the IME bottom inset when the keyboard is visible, or only the system bar bottom inset when it's hidden - never combining both.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions