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

Remove non-functioning code and instead throw a more helpful platform-not-supported error #23617

Merged
merged 4 commits into from
Aug 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
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
12 changes: 8 additions & 4 deletions src/Essentials/src/WebAuthenticator/WebAuthenticator.shared.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,9 @@ public interface IWebAuthenticator
/// <param name="webAuthenticatorOptions">A <see cref="WebAuthenticatorOptions"/> instance containing additional configuration for this authentication call.</param>
/// <returns>A <see cref="WebAuthenticatorResult"/> object with the results of this operation.</returns>
/// <exception cref="TaskCanceledException">Thrown when the user canceled the authentication flow.</exception>
/// <exception cref="HttpRequestException">Windows: Thrown when a HTTP Request error occured.</exception>
/// <exception cref="Exception">Windows: Thrown when a unexpected HTTP response was received.</exception>
/// <exception cref="PlatformNotSupportedException">Windows: Thrown when called on Windows.</exception>
/// <exception cref="FeatureNotSupportedException">iOS/macOS: Thrown when iOS version is less than 13 is used or macOS less than 13.1 is used.</exception>
/// <exception cref="InvalidOperationException">
/// <para>Windows: Thrown when the callback custom URL scheme is not registered in the AppxManifest.xml file.</para>
/// <para>Android: Thrown when the no IntentFilter has been created for the callback URL.</para>
/// </exception>
Task<WebAuthenticatorResult> AuthenticateAsync(WebAuthenticatorOptions webAuthenticatorOptions);
Expand Down Expand Up @@ -76,13 +74,19 @@ public static class WebAuthenticator
/// <summary>Begin an authentication flow by navigating to the specified url and waiting for a callback/redirect to the callbackUrl scheme.</summary>
/// <param name="url"> Url to navigate to, beginning the authentication flow.</param>
/// <param name="callbackUrl"> Expected callback url that the navigation flow will eventually redirect to.</param>
/// <returns>Returns a result parsed out from the callback url.</returns>
/// <returns>Returns a result parsed out from the callback url.</returns>
#if !NETSTANDARD
[System.Runtime.Versioning.UnsupportedOSPlatform("windows")]
#endif
public static Task<WebAuthenticatorResult> AuthenticateAsync(Uri url, Uri callbackUrl)
=> Current.AuthenticateAsync(url, callbackUrl);

/// <summary>Begin an authentication flow by navigating to the specified url and waiting for a callback/redirect to the callbackUrl scheme.The start url and callbackUrl are specified in the webAuthenticatorOptions.</summary>
/// <param name="webAuthenticatorOptions">Options to configure the authentication request.</param>
/// <returns>Returns a result parsed out from the callback url.</returns>
#if !NETSTANDARD
[System.Runtime.Versioning.UnsupportedOSPlatform("windows")]
#endif
public static Task<WebAuthenticatorResult> AuthenticateAsync(WebAuthenticatorOptions webAuthenticatorOptions)
=> Current.AuthenticateAsync(webAuthenticatorOptions);

Expand Down
55 changes: 2 additions & 53 deletions src/Essentials/src/WebAuthenticator/WebAuthenticator.uwp.cs
Original file line number Diff line number Diff line change
@@ -1,64 +1,13 @@
using System;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using System.Xml;
using System.Xml.Linq;
using System.Xml.XPath;
using Microsoft.Maui.ApplicationModel;
using Microsoft.Maui.Storage;
using Windows.Security.Authentication.Web;

namespace Microsoft.Maui.Authentication
{
partial class WebAuthenticatorImplementation : IWebAuthenticator
{
public async Task<WebAuthenticatorResult> AuthenticateAsync(WebAuthenticatorOptions webAuthenticatorOptions)
public Task<WebAuthenticatorResult> AuthenticateAsync(WebAuthenticatorOptions webAuthenticatorOptions)
{
var url = webAuthenticatorOptions?.Url;
var callbackUrl = webAuthenticatorOptions?.CallbackUrl;

if (!IsUriProtocolDeclared(callbackUrl.Scheme))
throw new InvalidOperationException($"You need to declare the windows.protocol usage of the protocol/scheme `{callbackUrl.Scheme}` in your AppxManifest.xml file");

try
{
var r = await WebAuthenticationBroker.AuthenticateAsync(WebAuthenticationOptions.None, url, callbackUrl);

switch (r.ResponseStatus)
{
case WebAuthenticationStatus.Success:
// For GET requests this is a URI:
var resultUri = new Uri(r.ResponseData.ToString());
return new WebAuthenticatorResult(resultUri, webAuthenticatorOptions?.ResponseDecoder);
case WebAuthenticationStatus.UserCancel:
throw new TaskCanceledException();
case WebAuthenticationStatus.ErrorHttp:
throw new HttpRequestException("Error: " + r.ResponseErrorDetail);
default:
throw new Exception("Response: " + r.ResponseData.ToString() + "\nStatus: " + r.ResponseStatus);
}
}
catch (FileNotFoundException)
{
throw new TaskCanceledException();
}
}

static bool IsUriProtocolDeclared(string scheme)
{
var docPath = FileSystemUtils.PlatformGetFullAppPackageFilePath(PlatformUtils.AppManifestFilename);
var doc = XDocument.Load(docPath, LoadOptions.None);
var reader = doc.CreateReader();
var namespaceManager = new XmlNamespaceManager(reader.NameTable);
namespaceManager.AddNamespace("x", PlatformUtils.AppManifestXmlns);
namespaceManager.AddNamespace("uap", "http://schemas.microsoft.com/appx/manifest/uap/windows10");

// Check if the protocol was declared
var decl = doc.Root.XPathSelectElements($"//uap:Extension[@Category='windows.protocol']/uap:Protocol[@Name='{scheme}']", namespaceManager);

return decl != null && decl.Any();
throw new PlatformNotSupportedException("This implementation of WebAuthenticator does not support Windows. See https://github.com/microsoft/WindowsAppSDK/issues/441 for more details.");
Copy link
Contributor Author

@dotMorten dotMorten Jul 15, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let me know if you have a better location to direct people to?
You could direct to https://learn.microsoft.com/en-us/dotnet/maui/platform-integration/communication/authentication?view=net-maui-8.0&tabs=windows#get-started, but that just directs to a closed maui issue which is just as confusing, and in turn that just directs to the above referenced issue, which is where the problem really needs to be addressed first.

The doc could be the better place to direct people to, but in that case the doc needs improvements.

}
}
}
19 changes: 16 additions & 3 deletions src/Essentials/test/DeviceTests/Tests/WebAuthenticator_Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,21 @@ public class WebAuthenticator_Tests
[Trait(Traits.InteractionType, Traits.InteractionTypes.Human)]
public async Task Redirect(string urlBase, string callbackScheme, string accessToken, string refreshToken, int expires)
{
var r = await WebAuthenticator.AuthenticateAsync(
#pragma warning disable CA1416 // Validate platform compatibility: Not supported on Windows
var authenticationTask = WebAuthenticator.AuthenticateAsync(
new Uri($"{urlBase}?access_token={accessToken}&refresh_token={refreshToken}&expires={expires}"),
new Uri($"{callbackScheme}://"));
#pragma warning restore CA1416 // Validate platform compatibility

#if WINDOWS
var exception = await Assert.ThrowsAsync<PlatformNotSupportedException>(async () => await authenticationTask);
#else
var r = await authenticationTask;
Assert.Equal(accessToken, r?.AccessToken);
Assert.Equal(refreshToken, r?.RefreshToken);
Assert.NotNull(r?.ExpiresIn);
Assert.True(r?.ExpiresIn > DateTime.UtcNow);
#endif
}

[Theory]
Expand All @@ -42,18 +49,24 @@ public async Task Redirect(string urlBase, string callbackScheme, string accessT
public async Task RedirectWithResponseDecoder(string urlBase, string callbackScheme, string accessToken, string refreshToken, int expires)
{
var responseDecoder = new TestResponseDecoder();
var r = await WebAuthenticator.AuthenticateAsync(new WebAuthenticatorOptions
#pragma warning disable CA1416 // Validate platform compatibility: Not supported on Windows
var authenticationTask = WebAuthenticator.AuthenticateAsync(new WebAuthenticatorOptions
{
Url = new Uri($"{urlBase}?access_token={accessToken}&refresh_token={refreshToken}&expires={expires}"),
CallbackUrl = new Uri($"{callbackScheme}://"),
ResponseDecoder = responseDecoder
});

#pragma warning restore CA1416 // Validate platform compatibility
#if WINDOWS
var exception = await Assert.ThrowsAsync<PlatformNotSupportedException>(async () => await authenticationTask);
#else
var r = await authenticationTask;
Assert.Equal(accessToken, r?.AccessToken);
Assert.Equal(refreshToken, r?.RefreshToken);
Assert.NotNull(r?.ExpiresIn);
Assert.True(r?.ExpiresIn > DateTime.UtcNow);
Assert.Equal(1, responseDecoder.CallCount);
#endif
}

[Theory]
Expand Down
Loading