From f31cfb67e539fe2c70e1bdf61fc6475fcddfb297 Mon Sep 17 00:00:00 2001 From: Isaiah Williams Date: Tue, 26 Nov 2019 19:23:30 -0600 Subject: [PATCH] Updating the way users are prompted to take action --- .../Authenticators/DeviceCodeAuthenticator.cs | 32 ++++++++-- .../InteractiveUserAuthenticator.cs | 59 ++++++++++++++----- .../Network/DefaultOsBrowserWebUi.cs | 13 ++-- 3 files changed, 81 insertions(+), 23 deletions(-) diff --git a/src/PowerShell/Authenticators/DeviceCodeAuthenticator.cs b/src/PowerShell/Authenticators/DeviceCodeAuthenticator.cs index cc6e40d5..b1b52e8e 100644 --- a/src/PowerShell/Authenticators/DeviceCodeAuthenticator.cs +++ b/src/PowerShell/Authenticators/DeviceCodeAuthenticator.cs @@ -14,6 +14,11 @@ namespace Microsoft.Store.PartnerCenter.PowerShell.Authenticators /// internal class DeviceCodeAuthenticator : DelegatingAuthenticator { + /// + /// The message that will be written utilizing the prompt action. + /// + private string message; + /// /// Apply this authenticator to the given authentication parameters. /// @@ -23,15 +28,32 @@ internal class DeviceCodeAuthenticator : DelegatingAuthenticator /// /// An instance of that represents the access token generated as result of a successful authenication. /// - public override Task AuthenticateAsync(AuthenticationParameters parameters, Action promptAction = null, CancellationToken cancellationToken = default) + public override async Task AuthenticateAsync(AuthenticationParameters parameters, Action promptAction = null, CancellationToken cancellationToken = default) { IPublicClientApplication app = GetClient(parameters.Account, parameters.Environment).AsPublicClient(); - return app.AcquireTokenWithDeviceCode(parameters.Scopes, deviceCodeResult => + Task task = Task.Factory.StartNew(() => + app.AcquireTokenWithDeviceCode(parameters.Scopes, deviceCodeResult => + { + message = deviceCodeResult.Message; + return Task.CompletedTask; + }).ExecuteAsync(cancellationToken).GetAwaiter().GetResult()); + + while (true) { - Console.WriteLine(deviceCodeResult.Message); - return Task.CompletedTask; - }).ExecuteAsync(cancellationToken); + if (!string.IsNullOrEmpty(message)) + { + promptAction(message); + break; + } + + cancellationToken.ThrowIfCancellationRequested(); + Thread.Sleep(1000); + } + + await Task.CompletedTask; + + return task.Result; } /// diff --git a/src/PowerShell/Authenticators/InteractiveUserAuthenticator.cs b/src/PowerShell/Authenticators/InteractiveUserAuthenticator.cs index ca005f1d..67b1b2d2 100644 --- a/src/PowerShell/Authenticators/InteractiveUserAuthenticator.cs +++ b/src/PowerShell/Authenticators/InteractiveUserAuthenticator.cs @@ -4,6 +4,7 @@ namespace Microsoft.Store.PartnerCenter.PowerShell.Authenticators { using System; + using System.Collections.Generic; using System.Collections.Specialized; using System.Net; using System.Net.Sockets; @@ -34,9 +35,13 @@ public override async Task AuthenticateAsync(Authenticatio AuthenticationResult authResult; IClientApplicationBase app; InteractiveParameters interactiveParameters = parameters as InteractiveParameters; + Task> task; TcpListener listener = null; + int count = 0; string redirectUri = null; + Queue messages; + int port = 8399; while (++port < 9000) @@ -51,36 +56,62 @@ public override async Task AuthenticateAsync(Authenticatio } catch (Exception ex) { - Console.WriteLine($"Port {port} is taken with exception '{ex.Message}'; trying to connect to the next port."); + promptAction($"Port {port} is taken with exception '{ex.Message}'; trying to connect to the next port."); listener?.Stop(); } } app = GetClient(parameters.Account, parameters.Environment, redirectUri); + messages = new Queue(); if (app is IConfidentialClientApplication) { - ICustomWebUi customWebUi = new DefaultOsBrowserWebUi(interactiveParameters.Message); + ICustomWebUi customWebUi = new DefaultOsBrowserWebUi(messages, interactiveParameters.Message); - Uri authCodeUrl = await customWebUi.AcquireAuthorizationCodeAsync( - await app.AsConfidentialClient().GetAuthorizationRequestUrl(parameters.Scopes).ExecuteAsync(cancellationToken).ConfigureAwait(false), - new Uri(redirectUri), - cancellationToken).ConfigureAwait(false); + task = Task>.Factory.StartNew(async () => + { + Uri authCodeUrl = await customWebUi.AcquireAuthorizationCodeAsync( + await app.AsConfidentialClient().GetAuthorizationRequestUrl(parameters.Scopes).ExecuteAsync(cancellationToken).ConfigureAwait(false), + new Uri(redirectUri), + cancellationToken).ConfigureAwait(false); - NameValueCollection queryStringParameters = HttpUtility.ParseQueryString(authCodeUrl.Query); + NameValueCollection queryStringParameters = HttpUtility.ParseQueryString(authCodeUrl.Query); - authResult = await app.AsConfidentialClient().AcquireTokenByAuthorizationCode( - parameters.Scopes, - queryStringParameters["code"]).ExecuteAsync(cancellationToken).ConfigureAwait(false); + return await app.AsConfidentialClient().AcquireTokenByAuthorizationCode( + parameters.Scopes, + queryStringParameters["code"]).ExecuteAsync(cancellationToken).ConfigureAwait(false); + }); } else { - authResult = await app.AsPublicClient().AcquireTokenInteractive(parameters.Scopes) - .WithCustomWebUi(new DefaultOsBrowserWebUi(interactiveParameters.Message)) - .WithPrompt(Prompt.ForceLogin) - .ExecuteAsync(cancellationToken).ConfigureAwait(false); + task = Task>.Factory.StartNew(async () => + { + return await app.AsPublicClient().AcquireTokenInteractive(parameters.Scopes) + .WithCustomWebUi(new DefaultOsBrowserWebUi(messages, interactiveParameters.Message)) + .WithPrompt(Prompt.ForceLogin) + .ExecuteAsync(cancellationToken).ConfigureAwait(false); + }); + } + + while (true) + { + while (messages.Count > 0) + { + promptAction(messages.Dequeue()); + count++; + } + + if (count >= 2) + { + break; + } + + cancellationToken.ThrowIfCancellationRequested(); + Thread.Sleep(1000); } + authResult = await task.Result.ConfigureAwait(false); + return authResult; } diff --git a/src/PowerShell/Network/DefaultOsBrowserWebUi.cs b/src/PowerShell/Network/DefaultOsBrowserWebUi.cs index 9b0eaf41..4aef0505 100644 --- a/src/PowerShell/Network/DefaultOsBrowserWebUi.cs +++ b/src/PowerShell/Network/DefaultOsBrowserWebUi.cs @@ -4,6 +4,7 @@ namespace Microsoft.Store.PartnerCenter.PowerShell.Network { using System; + using System.Collections.Generic; using System.Collections.Specialized; using System.Diagnostics; using System.Globalization; @@ -46,15 +47,19 @@ internal class DefaultOsBrowserWebUi : ICustomWebUi /// private readonly string message; + private readonly Queue messages; + /// /// Initializes a new instance of the class. /// /// The message written to the console. - public DefaultOsBrowserWebUi(string message) + public DefaultOsBrowserWebUi(Queue messages, string message) { message.AssertNotEmpty(nameof(message)); + messages.AssertNotNull(nameof(messages)); this.message = message; + this.messages = messages; } /// @@ -66,14 +71,14 @@ public DefaultOsBrowserWebUi(string message) /// The URI returned back from the STS authorization endpoint. This URI contains a code=CODE parameter that MSAL.NET will extract and redeem. public async Task AcquireAuthorizationCodeAsync(Uri authorizationUri, Uri redirectUri, CancellationToken cancellationToken) { - Console.WriteLine("Attempting to launch a browser for authorization code login."); + messages.Enqueue("Attempting to launch a browser for authorization code login."); if (!OpenBrowser(authorizationUri.ToString())) { - Console.WriteLine("Unable to launch a browser for authorization code login. Reverting to device code login."); + messages.Enqueue("Unable to launch a browser for authorization code login. Reverting to device code login."); } - Console.WriteLine(message); + messages.Enqueue(message); using (SingleMessageTcpListener listener = new SingleMessageTcpListener(redirectUri.Port)) {