-
Notifications
You must be signed in to change notification settings - Fork 50
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
47ef0f9
commit 6f15b4c
Showing
20 changed files
with
1,773 additions
and
211 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
using Microsoft.Windows.AppLifecycle; | ||
using Windows.ApplicationModel.Activation; | ||
|
||
namespace Auth0.OidcClient.Platforms.Windows | ||
{ | ||
public sealed class Activator | ||
{ | ||
internal static bool RedirectActivationCheck; | ||
|
||
/// <summary> | ||
/// Performs an protocol activation check and redirects activation to the correct application instance. | ||
/// </summary> | ||
public static bool CheckRedirectionActivation() | ||
{ | ||
var activatedEventArgs = AppInstance.GetCurrent()?.GetActivatedEventArgs(); | ||
|
||
RedirectActivationCheck = true; | ||
|
||
if (activatedEventArgs is null || activatedEventArgs.Kind != ExtendedActivationKind.Protocol || activatedEventArgs.Data is not IProtocolActivatedEventArgs protocolArgs) | ||
{ | ||
return false; | ||
} | ||
|
||
var ctx = RedirectionContextManager.GetRedirectionContext(activatedEventArgs.Data as IProtocolActivatedEventArgs); | ||
|
||
if (ctx is not null && ctx.AppInstanceKey is not null && ctx.TaskId is not null) | ||
{ | ||
var instance = AppInstance.GetInstances().FirstOrDefault(i => i.Key == ctx.AppInstanceKey); | ||
|
||
if (instance is not null && !instance.IsCurrent) | ||
{ | ||
instance.RedirectActivationToAsync(activatedEventArgs).AsTask().Wait(); | ||
|
||
System.Diagnostics.Process.GetCurrentProcess().Kill(); | ||
|
||
return true; | ||
} | ||
} | ||
else | ||
{ | ||
var instance = AppInstance.GetCurrent(); | ||
|
||
if (string.IsNullOrEmpty(instance.Key)) | ||
{ | ||
AppInstance.FindOrRegisterForKey(Guid.NewGuid().ToString()); | ||
} | ||
} | ||
return false; | ||
} | ||
|
||
} | ||
} |
50 changes: 50 additions & 0 deletions
50
Auth0.OidcClient.MAUI.Platforms.Windows/AppInstanceProxy.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
using System.Diagnostics.CodeAnalysis; | ||
using Microsoft.Windows.AppLifecycle; | ||
|
||
namespace Auth0.OidcClient.Platforms.Windows; | ||
|
||
internal interface IAppInstanceProxy | ||
{ | ||
event EventHandler<IAppActivationArguments> Activated; | ||
string GetCurrentAppKey(); | ||
} | ||
|
||
internal interface IAppActivationArguments | ||
{ | ||
ExtendedActivationKind Kind { get; set; } | ||
object Data { get; set; } | ||
} | ||
|
||
internal class AppActivationArguments : IAppActivationArguments | ||
{ | ||
public ExtendedActivationKind Kind { get; set; } | ||
public object Data { get; set; } | ||
} | ||
|
||
/// <summary> | ||
/// Excludes from Code Coverage because of the integration with AppInstance.GetCurrent() | ||
/// </summary> | ||
[ExcludeFromCodeCoverage] | ||
internal class AppInstanceProxy : IAppInstanceProxy | ||
{ | ||
public AppInstanceProxy() | ||
{ | ||
AppInstance.GetCurrent().Activated += OnActivated; | ||
} | ||
|
||
public event EventHandler<IAppActivationArguments> Activated; | ||
|
||
protected virtual void OnActivated(object? sender, Microsoft.Windows.AppLifecycle.AppActivationArguments e) | ||
{ | ||
Activated?.Invoke(this, new AppActivationArguments | ||
{ | ||
Kind = e.Kind, | ||
Data = e.Data | ||
}); | ||
} | ||
|
||
public virtual string GetCurrentAppKey() | ||
{ | ||
return AppInstance.GetCurrent().Key; | ||
} | ||
} |
16 changes: 16 additions & 0 deletions
16
Auth0.OidcClient.MAUI.Platforms.Windows/Auth0.OidcClient.MAUI.Platforms.Windows.csproj
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
|
||
<PropertyGroup> | ||
<TargetFrameworks>net6.0-windows10.0.19041.0</TargetFrameworks> | ||
<!-- Uncomment to also build the tizen app. You will need to install tizen by following this: https://github.com/Samsung/Tizen.NET --> | ||
<!-- <TargetFrameworks>$(TargetFrameworks);net7.0-tizen</TargetFrameworks> --> | ||
<UseMaui>true</UseMaui> | ||
<SingleProject>true</SingleProject> | ||
<ImplicitUsings>enable</ImplicitUsings> | ||
|
||
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'windows'">10.0.17763.0</SupportedOSPlatformVersion> | ||
<TargetPlatformMinVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'windows'">10.0.17763.0</TargetPlatformMinVersion> | ||
<AssemblyOriginatorKeyFile>..\build\Auth0OidcClientStrongName.snk</AssemblyOriginatorKeyFile> | ||
</PropertyGroup> | ||
|
||
</Project> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
using System.Runtime.InteropServices; | ||
using System.Xml; | ||
using System.Xml.Linq; | ||
using System.Xml.XPath; | ||
|
||
namespace Auth0.OidcClient.Platforms.Windows | ||
{ | ||
internal interface IHelpers | ||
{ | ||
bool IsAppPackaged { get; } | ||
bool IsUriProtocolDeclared(string scheme); | ||
void OpenBrowser(Uri uri); | ||
} | ||
internal class Helpers : IHelpers | ||
{ | ||
#pragma warning disable SA1203 // Constants should appear before fields | ||
private const long AppModelErrorNoPackage = 15700L; | ||
#pragma warning restore SA1203 // Constants should appear before fields | ||
|
||
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)] | ||
private static extern int GetCurrentPackageFullName(ref int packageFullNameLength, System.Text.StringBuilder packageFullName); | ||
|
||
public bool IsAppPackaged | ||
{ | ||
get | ||
{ | ||
try | ||
{ | ||
// Application is MSIX packaged if it has an identity: https://learn.microsoft.com/en-us/windows/msix/detect-package-identity | ||
int length = 0; | ||
var sb = new System.Text.StringBuilder(0); | ||
int result = GetCurrentPackageFullName(ref length, sb); | ||
return result != AppModelErrorNoPackage; | ||
} | ||
catch | ||
{ | ||
return false; | ||
} | ||
} | ||
} | ||
|
||
public bool IsUriProtocolDeclared(string scheme) | ||
{ | ||
if (global::Windows.ApplicationModel.Package.Current is null) | ||
return false; | ||
var docPath = Path.Combine(global::Windows.ApplicationModel.Package.Current.InstalledLocation.Path, "AppxManifest.xml"); | ||
var doc = XDocument.Load(docPath, LoadOptions.None); | ||
var reader = doc.CreateReader(); | ||
var namespaceManager = new XmlNamespaceManager(reader.NameTable); | ||
namespaceManager.AddNamespace("x", "http://schemas.microsoft.com/appx/manifest/foundation/windows10"); | ||
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(); | ||
} | ||
|
||
public void OpenBrowser(Uri uri) | ||
{ | ||
var process = new System.Diagnostics.Process(); | ||
process.StartInfo.FileName = "rundll32.exe"; | ||
process.StartInfo.Arguments = $"url.dll,FileProtocolHandler \"{uri.ToString().Replace("\"", "%22")}\""; | ||
process.StartInfo.UseShellExecute = true; | ||
process.Start(); | ||
} | ||
} | ||
} |
36 changes: 36 additions & 0 deletions
36
Auth0.OidcClient.MAUI.Platforms.Windows/RedirectionContext.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
using System.Collections.Specialized; | ||
using System.Text.Json.Nodes; | ||
|
||
namespace Auth0.OidcClient.Platforms.Windows | ||
{ | ||
internal class RedirectionContext | ||
{ | ||
internal string TaskId { get; set; } | ||
internal string AppInstanceKey { get; set; } | ||
|
||
internal static RedirectionContext New(IAppInstanceProxy appInstanceProxy) | ||
{ | ||
return new RedirectionContext | ||
{ | ||
TaskId = Guid.NewGuid().ToString(), | ||
AppInstanceKey = appInstanceProxy.GetCurrentAppKey() | ||
}; | ||
} | ||
|
||
internal JsonObject ToJsonObject(NameValueCollection query) | ||
{ | ||
var jsonObject = new JsonObject | ||
{ | ||
{ "appInstanceKey", AppInstanceKey }, | ||
{ "taskId", TaskId } | ||
}; | ||
|
||
if (query["state"] is string oldState && !string.IsNullOrEmpty(oldState)) | ||
{ | ||
jsonObject["state"] = oldState; | ||
} | ||
|
||
return jsonObject; | ||
} | ||
} | ||
} |
53 changes: 53 additions & 0 deletions
53
Auth0.OidcClient.MAUI.Platforms.Windows/RedirectionContextManager.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
using System.Runtime.CompilerServices; | ||
using System.Text.Json.Nodes; | ||
using Windows.ApplicationModel.Activation; | ||
|
||
[assembly: InternalsVisibleTo("Auth0.OidcClient.MAUI.Platforms.Windows.UnitTests")] | ||
namespace Auth0.OidcClient.Platforms.Windows | ||
{ | ||
internal class RedirectionContextManager | ||
{ | ||
internal static RedirectionContext? GetRedirectionContext(IProtocolActivatedEventArgs protocolArgs) | ||
{ | ||
var vals = System.Web.HttpUtility.ParseQueryString(protocolArgs.Uri.Query); | ||
var state = vals["state"]; | ||
JsonObject jsonObject = null; | ||
|
||
if (!string.IsNullOrEmpty(state)) | ||
{ | ||
|
||
try | ||
{ | ||
jsonObject = JsonNode.Parse(state) as JsonObject; | ||
} | ||
catch | ||
{ | ||
jsonObject = JsonNode.Parse(Uri.UnescapeDataString(state)) as JsonObject; | ||
} | ||
} | ||
|
||
if (jsonObject is not null) | ||
{ | ||
return new RedirectionContext | ||
{ | ||
AppInstanceKey = TryGetJsonValue(jsonObject, "appInstanceKey"), | ||
TaskId = TryGetJsonValue(jsonObject, "taskId") | ||
}; | ||
} | ||
else | ||
{ | ||
return null; | ||
} | ||
} | ||
|
||
private static string? TryGetJsonValue(JsonObject jsonObject, string key) | ||
{ | ||
if (jsonObject.ContainsKey(key) && jsonObject[key] is JsonValue jValue && jValue.TryGetValue(out string value)) | ||
{ | ||
return value; | ||
} | ||
|
||
return null; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
using System.Text.Json.Nodes; | ||
|
||
namespace Auth0.OidcClient.Platforms.Windows; | ||
|
||
internal class StateModifier | ||
{ | ||
internal static Uri MoveStateToReturnTo(Uri uri) | ||
{ | ||
var query = System.Web.HttpUtility.ParseQueryString(uri.Query); | ||
// The state QueryString as generated by WinUIEx | ||
var state = query["state"]; | ||
// The original returnTo as configured externally | ||
var returnTo = query["returnTo"]; | ||
|
||
|
||
UriBuilder returnToBuilder = new UriBuilder(returnTo); | ||
|
||
// Get the original returnTo querystring params, so we can append state to it | ||
var returnToQuery = System.Web.HttpUtility.ParseQueryString(new Uri(returnTo).Query); | ||
// Append state as a querystring parameter to returnTo | ||
// We need to escape it for it to be accepted | ||
returnToQuery["state"] = Uri.EscapeDataString(state); | ||
// Set the query again on the returnTo url | ||
returnToBuilder.Query = returnToQuery.ToString(); | ||
|
||
// Update returnTo in the original query so that it now includes state | ||
query["returnTo"] = returnToBuilder.Uri.ToString(); | ||
|
||
UriBuilder logoutUrlBuilder = new UriBuilder(uri); | ||
// Set the query again on the logout url | ||
logoutUrlBuilder.Query = query.ToString(); | ||
|
||
// Return the Uri so it can be used internally by WinUIEx to start the process and open the browser | ||
return logoutUrlBuilder.Uri; | ||
} | ||
|
||
internal static Uri ResetRawState(Uri uri) | ||
{ | ||
var query = System.Web.HttpUtility.ParseQueryString(uri.Query); | ||
|
||
var state = query["state"]; | ||
|
||
JsonObject jsonObject; | ||
try | ||
{ | ||
jsonObject = JsonNode.Parse(state ?? "{}") as JsonObject; | ||
|
||
} | ||
catch (Exception ex) | ||
{ | ||
|
||
jsonObject = JsonNode.Parse(Uri.UnescapeDataString(state)) as JsonObject; | ||
} | ||
|
||
var originalState = jsonObject["state"]; | ||
|
||
if (originalState is not null) | ||
{ | ||
query["state"] = originalState.ToString(); | ||
} | ||
else | ||
{ | ||
query.Remove("state"); | ||
} | ||
|
||
|
||
UriBuilder uriBuilder = new UriBuilder(uri); | ||
uriBuilder.Query = query.ToString(); | ||
return uriBuilder.Uri; | ||
|
||
} | ||
} |
Oops, something went wrong.