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

[Bug] Maui Essentials WebAuthenticator not working in WinUI #2702

Closed
1 task
ElliottBrand opened this issue Sep 26, 2021 · 27 comments · Fixed by MicrosoftDocs/xamarin-docs#3455
Closed
1 task
Labels
area-essentials Essentials: Device, Display, Connectivity, Secure Storage, Sensors, App Info p/1 Work that is important, and has been scheduled for release in this or an upcoming sprint platform/windows 🪟 s/verified Verified / Reproducible Issue ready for Engineering Triage t/bug Something isn't working

Comments

@ElliottBrand
Copy link

ElliottBrand commented Sep 26, 2021

I'm attempting to use WebAuthenticator in Maui Essentials to authenticate a WinUI project, but it's having an issue finding the manifest file. It throws an exception saying:

Could not find file 'C:\WINDOWS\system32\AppxManifest.xml'.

If I add a local version of https://github.com/dotnet/maui/blob/main/src/Essentials/src/WebAuthenticator/WebAuthenticator.uwp.cs into my project (I made PlatformAuthenticateAsync public to test it and just passed the url and callbackUrl in the WebAuthenticatorOptions) and adjust line 51 to:

var doc = XDocument.Load($"{AppDomain.CurrentDomain.BaseDirectory}AppxManifest.xml", LoadOptions.None);

It then finds the file, however, a new error is thrown that says:

System.Runtime.InteropServices.COMException: 'There are no remote procedure calls active on this thread. (0x800706BD)'

Additionally, I set the CallbackUrl to:

new Uri("io.identitymodel.native://callback")

and the Url to:

new Uri("https://demo.identityserver.io")
@jamesmontemagno
Copy link
Member

jamesmontemagno commented Sep 26, 2021

Did you add the extension into your Package.appxmanifest?

<Applications>
    <Application Id="App" Executable="$targetnametoken$.exe" EntryPoint="MyApp.App">
        <Extensions>
            <uap:Extension Category="windows.protocol">
            <uap:Protocol Name="myapp">
                <uap:DisplayName>My App</uap:DisplayName>
            </uap:Protocol>
            </uap:Extension>
        </Extensions>
    </Application>
</Applications>

@ElliottBrand
Copy link
Author

ElliottBrand commented Sep 26, 2021

Yeah, here's the Package.appxmanifest file from this sample app.

<?xml version="1.0" encoding="utf-8"?>

<Package
  xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
  xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
  xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
  IgnorableNamespaces="uap rescap">

  <Identity
    Name="DC54F990-A6CD-4BED-B2AE-1942F4470BCC"
    Publisher="CN=User Name"
    Version="1.0.0.0" />

  <Properties>
    <DisplayName>MauiAppSample</DisplayName>
    <PublisherDisplayName>Microsoft</PublisherDisplayName>
    <Logo>Assets\appiconStoreLogo.png</Logo>
  </Properties>

  <Dependencies>
    <TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.17763.0" MaxVersionTested="10.0.19041.0" />
    <TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.17763.0" MaxVersionTested="10.0.19041.0" />
  </Dependencies>

  <Resources>
    <Resource Language="x-generate"/>
  </Resources>

  <Applications>
    <Application Id="App"
      Executable="$targetnametoken$.exe"
      EntryPoint="$targetentrypoint$">
      <uap:VisualElements
        DisplayName="MauiAppSample"
        Description="MauiAppSample"
        BackgroundColor="transparent"
        Square150x150Logo="Assets\appiconMediumTile.png"
        Square44x44Logo="Assets\appiconLogo.png">
        <uap:DefaultTile
          Wide310x150Logo="Assets\appiconWideTile.png"
          Square71x71Logo="Assets\appiconSmallTile.png"
          Square310x310Logo="Assets\appiconLargeTile.png"
          ShortName="MauiAppFileInput">
          <uap:ShowNameOnTiles>
            <uap:ShowOn Tile="square150x150Logo"/>
            <uap:ShowOn Tile="wide310x150Logo"/>
          </uap:ShowNameOnTiles>
        </uap:DefaultTile >
        <uap:SplashScreen Image="Assets\appiconfgSplashScreen.png" />
      </uap:VisualElements>
      <Extensions>
        <uap:Extension Category="windows.protocol">
          <uap:Protocol Name="io.identitymodel.native">
            <uap:DisplayName>MauiAppSample</uap:DisplayName>
          </uap:Protocol>
        </uap:Extension>
      </Extensions>
    </Application>
  </Applications>

  <Capabilities>
    <rescap:Capability Name="runFullTrust" />
  </Capabilities>

</Package>

@ElliottBrand
Copy link
Author

ElliottBrand commented Sep 26, 2021

In MainPage.xaml.cs I have a using directive for OidcClient (and a few others that can be easily resolved in the editor)

using IdentityModel.OidcClient

Variables

OidcClient _oidcClient;
LoginResult _loginResult;

and a method to handle a login button click:

async void OnLoginButtonClicked(object sender, EventArgs e)
{
    var options = new OidcClientOptions
    {
        Authority = "https://demo.identityserver.io",
        ClientId = "interactive.public",
        Scope = "openid profile email api offline_access",
        RedirectUri = "io.identitymodel.native://callback",
        Browser = new Browser()
    };

    _oidcClient = new OidcClient(options);
    _loginResult = await _oidcClient.LoginAsync(new LoginRequest());
    if (_loginResult.IsError)
    {
        Console.WriteLine("ERROR: {0}", _loginResult.Error);
        return;
    }
}

And the Browser.cs file. As I mentioned initially, I'm calling WebAuthenticator.PlatformAuthenticateAsync(authenticatorOptions) in my local file. Except for a few namespace adjustments to prevent conflicts and the adjustment to line 51, it's the same file as what's used in Maui Essentials.

using IdentityModel.OidcClient.Browser;
using System;
using System.Threading;
using System.Threading.Tasks;

namespace MauiAppSample
{
    public class Browser : IBrowser
    {
        public async Task<BrowserResult> InvokeAsync(BrowserOptions options, CancellationToken cancellationToken = default)
        {
            var authenticatorOptions = new Microsoft.Maui.Essentials.WebAuthenticatorOptions
            {
                CallbackUrl = new Uri("io.identitymodel.native://callback"),
                Url = new Uri("https://demo.identityserver.io")
            };

           // WebAuthenticator.PlatformAuthenticateAsync is in a local file that's altered to locate the 
           // AppxManifest.xml file, as mentioned in my initial post.
            Microsoft.Maui.Essentials.WebAuthenticatorResult authResult =
                await WebAuthenticator.PlatformAuthenticateAsync(authenticatorOptions);
            return new BrowserResult()
            {
                Response = ParseAuthenticatorResult(authResult)
            };
        }

        string ParseAuthenticatorResult(Microsoft.Maui.Essentials.WebAuthenticatorResult result)
        {
            string code = result?.Properties["code"];
            string scope = result?.Properties["scope"];
            string state = result?.Properties["state"];
            string sessionState = result?.Properties["session_state"];
            return $"{"io.identitymodel.native://callback"}#code={code}&scope={scope}&state={state}&session_state={sessionState}";
        }
    }
}

@Eilon Eilon added the area-essentials Essentials: Device, Display, Connectivity, Secure Storage, Sensors, App Info label Sep 27, 2021
@jsuarezruiz jsuarezruiz added the t/bug Something isn't working label Oct 22, 2021
@Berets
Copy link

Berets commented Nov 1, 2021

Any news about this? Same error here

@ElliottBrand
Copy link
Author

Sorry, no news. I'm still experiencing it too.

@hexjourney
Copy link

Same error

@RichardCain
Copy link

Same here

@samifahmy
Copy link

any news i have same error too?

@enisn
Copy link

enisn commented Feb 20, 2022

same error

@enisn
Copy link

enisn commented Feb 21, 2022

I've tried on a couple of different computers but the reason is the same. Also, I've tried clean installation of Maui & workloads, and tried on cleanly installed windows but nothing changed.

It tries to find C:\WINDOWS\system32\AppxManifest.xml file but it doesn't exist in system32 folder in any windows machine.

Then, I've tried to check my Package.appxmanifest file, I think AppxManifest can't be generated for application and I see it can't be opened in Visual Studio

image

I think there might be an error in Package.appxmanifest file on MAUI project template. Unfortunetely I'm not pro on Win apps, so I can't see what is wrong with that file.

Note: I've also added windows.protocol extension in that file with VSCode


Could anyone get over this issue?

@wenwen60
Copy link

wenwen60 commented Mar 7, 2022

Reproduce on windows with VS2022 17.2.0 Preview 2.0 [32215.517.main], attach repro sample
2702.zip.

@wenwen60 wenwen60 added the s/verified Verified / Reproducible Issue ready for Engineering Triage label Mar 7, 2022
@mattleibow mattleibow added this to the 6.0.300-rc.2 milestone Mar 22, 2022
@Redth Redth modified the milestones: 6.0.300-rc.2, 6.0.300-rc.3 Apr 20, 2022
@mattleibow mattleibow added s/needs-verification Indicates that this issue needs initial verification before further triage will happen and removed s/verified Verified / Reproducible Issue ready for Engineering Triage labels Apr 22, 2022
@mattleibow
Copy link
Member

I believe there was a bug in the packaging process before. @Eilon I think there was an issue at some point where the targets were copying the Package.appxmanifest into the root of the package and this caused some issues.

I can't seem to locate the issue I am thinking of, but I felt it was maybe the blazor content copying being too happy and grabbing all files or something?

@jfversluis jfversluis removed the s/needs-verification Indicates that this issue needs initial verification before further triage will happen label Apr 26, 2022
@Redth Redth modified the milestones: 6.0.300-rc.3, 6.0.300 Apr 27, 2022
@davidortinau davidortinau added the p/0 Work that we can't release without label Apr 27, 2022
@shv07
Copy link

shv07 commented May 2, 2022

Any updates on the issue? I have been facing the exactly same issue in windows with the latest Release Candidate.

@samhouts samhouts added the s/verified Verified / Reproducible Issue ready for Engineering Triage label May 2, 2022
@dotMorten
Copy link
Contributor

Here's one implementation that can probably be more or less lifted as is into MAUI: https://github.com/dotMorten/WinUIEx/blob/main/src/WinUIEx/WebAuthenticator.cs
You might want to change the use of the module initializer and instead do it on app launch constructor

@Redth Redth added p/1 Work that is important, and has been scheduled for release in this or an upcoming sprint and removed p/0 Work that we can't release without labels May 3, 2022
@shv07
Copy link

shv07 commented May 4, 2022

Here's one implementation that can probably be more or less lifted as is into MAUI: https://github.com/dotMorten/WinUIEx/blob/main/src/WinUIEx/WebAuthenticator.cs You might want to change the use of the module initializer and instead do it on app launch constructor

Thanks for the repo, @dotMorten . I tried using this class as replacement. It is now able to open the browser, initiate the authentication and redirect back to the application, but immediately after that, throws a "invalid state" error within the oidc client. On closer inspection, the value for the "state" key in the returned WebAuthenticatorResult.Properties (from AuthenticateAsync) seems quite different from what is received on a similar android project.

@dotMorten
Copy link
Contributor

dotMorten commented May 4, 2022

@shv07 Mind opening an issue over in the WinUIEx repo and we can discuss there, but I'm guessing you're adding a State parameter to the authorize url? (there's a fix incoming for that: dotMorten/WinUIEx@e3118c4)

@samhouts samhouts modified the milestones: 6.0.300, 6.0.300-servicing May 6, 2022
@jayveedee
Copy link

jayveedee commented May 8, 2022

On a slightly different note but still related to the WebAuthenticator. On Android the documentation for Xamarin Essentials, it still only supports Android 11 or SDK 30. Will this be updated? Because if you target Android 12 as of right now and use the WebAuthenticator you will get an error which says "java crashed with exit code 1"
Think this should probably be fixed if Android 12 will be targeted by default on a fresh install and essentials will be in the Maui framework by default.

@shv07
Copy link

shv07 commented May 8, 2022

@jayveedee I was able to fix that error by setting Exported = true in the Activity class. You get this information if you see the build logs after setting it to more verbose from the VS settings.

My final Activity was like this:-

// MainActivity.cs
[Activity(NoHistory = true, LaunchMode = LaunchMode.SingleTop, Exported = true)] 
[IntentFilter(new[] { Android.Content.Intent.ActionView },
        Categories = new[] { Android.Content.Intent.CategoryDefault, Android.Content.Intent.CategoryBrowsable },
        DataScheme = "myapp")]
public class WebAuthenticationCallbackActivity : WebAuthenticatorCallbackActivity
 {
 }

@jamesmontemagno
Copy link
Member

@shv07 can you send a PR to the docs?

@shv07
Copy link

shv07 commented May 9, 2022

@dotMorten
Copy link
Contributor

dotMorten commented May 10, 2022

@davidbritch did you mean to close this with a doc pr?

@davidbritch
Copy link
Contributor

@dotMorten No, apologies!

@davidbritch davidbritch reopened this May 10, 2022
@enisn
Copy link

enisn commented May 31, 2022

Status update:
AppxManifest.xml file problem has been resolved with the latest release but still OpenID Connect login doesn't work.

The following issue can be tracked:
microsoft/WindowsAppSDK#441

@osnipezzini
Copy link

Any news about this ?

@ElliottBrand
Copy link
Author

ElliottBrand commented Jul 3, 2022

I was able to get my MAUI WinUI app to log in successfully, primarily with the help of the code provided up above by @dotMorten.

Here's one implementation that can probably be more or less lifted as is into MAUI: https://github.com/dotMorten/WinUIEx/blob/main/src/WinUIEx/WebAuthenticator.cs You might want to change the use of the module initializer and instead do it on app launch constructor

Just copy that code into a file called something like WebAuthenticator.cs and drop it into the Platforms > Windows folder of your MAUI project (or wherever works best for you), then reference it instead of the current Essentials Web Authenticator.

I did have one issue with an exception throwing in my OIDC Client Browser that said "Invalid state". It turned out being that the state information being added in the new Web Authenticator class (appInstanceId & signinId) wasn't expected while building the BrowserResult, so this is what I did.

using IdentityModel.OidcClient.Browser;
using System.Diagnostics;

namespace MyAwesomeMauiApp.WinUI;

public class Browser : IdentityModel.OidcClient.Browser.IBrowser
{
    public async Task<BrowserResult> InvokeAsync(BrowserOptions options, CancellationToken cancellationToken = default)
    {
        try
        {
            Microsoft.Maui.Authentication.WebAuthenticatorResult authResult =
                await WinUIEx.WebAuthenticator.AuthenticateAsync(new Uri(options.StartUrl), new Uri("myapp://callback")); // Add your callback URL 

            var authorizeResponse = ToRawIdentityUrl(options.EndUrl, authResult);

            return new BrowserResult
            {
                Response = authorizeResponse
            };
        }
        catch (TaskCanceledException ex)
        {
            Debug.WriteLine(ex);
            return new BrowserResult
            {
                ResultType = BrowserResultType.UserCancel
            };
        }
        catch (Exception ex)
        {
            Debug.WriteLine(ex);
            return new BrowserResult()
            {
                ResultType = BrowserResultType.UnknownError,
                Error = ex.ToString()
            };
        }
    }

    public string ToRawIdentityUrl(string redirectUrl, WebAuthenticatorResult result)
    {
        try
        {
            IEnumerable<string> parameters = result.Properties.Select(pair => $"{pair.Key}={pair.Value}");
            var modifiedParameters = parameters.ToList();

            var stateParameter = modifiedParameters
                .FirstOrDefault(p => p.StartsWith("state", StringComparison.OrdinalIgnoreCase));

            if (!string.IsNullOrWhiteSpace(stateParameter))
            {
                // Remove the state key added by WebAuthenticator that includes appInstanceId
                modifiedParameters = modifiedParameters.Where(p => !p.StartsWith("state", StringComparison.OrdinalIgnoreCase)).ToList();
                
                stateParameter = System.Web.HttpUtility.UrlDecode(stateParameter).Split('&').Last();
                modifiedParameters.Add(stateParameter);
            }
            var values = string.Join("&", modifiedParameters);
            return $"{redirectUrl}#{values}";
        }
        catch (Exception ex)
        {
            Debug.WriteLine(ex);
            throw;
        }
    }
}

@mattleibow
Copy link
Member

Closing this as it is a bug in the Windows App SDK and is being tracked there: microsoft/WindowsAppSDK#441

@mattleibow mattleibow closed this as not planned Won't fix, can't repro, duplicate, stale Aug 3, 2022
@mattleibow mattleibow removed their assignment Aug 3, 2022
@mattleibow mattleibow removed this from the .NET 7 Planning milestone Aug 3, 2022
@ghost ghost locked as resolved and limited conversation to collaborators Sep 2, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-essentials Essentials: Device, Display, Connectivity, Secure Storage, Sensors, App Info p/1 Work that is important, and has been scheduled for release in this or an upcoming sprint platform/windows 🪟 s/verified Verified / Reproducible Issue ready for Engineering Triage t/bug Something isn't working
Projects
None yet
Development

Successfully merging a pull request may close this issue.