Skip to content

Commit

Permalink
Remove non-functioning code and instead throw a more helpful platform…
Browse files Browse the repository at this point in the history
…-not-supported error (#23617)

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

Also mark the APIs not supported on Windows.

* Fix build failures

* Update tests for new webauth behavior
  • Loading branch information
dotMorten authored and Redth committed Aug 22, 2024
1 parent 8eb003f commit c588d31
Show file tree
Hide file tree
Showing 3 changed files with 26 additions and 60 deletions.
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.");
}
}
}
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

0 comments on commit c588d31

Please sign in to comment.