Skip to content
This repository was archived by the owner on Jul 22, 2024. It is now read-only.
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
32 changes: 27 additions & 5 deletions src/PowerShell/Authenticators/DeviceCodeAuthenticator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ namespace Microsoft.Store.PartnerCenter.PowerShell.Authenticators
/// </summary>
internal class DeviceCodeAuthenticator : DelegatingAuthenticator
{
/// <summary>
/// The message that will be written utilizing the prompt action.
/// </summary>
private string message;

/// <summary>
/// Apply this authenticator to the given authentication parameters.
/// </summary>
Expand All @@ -23,15 +28,32 @@ internal class DeviceCodeAuthenticator : DelegatingAuthenticator
/// <returns>
/// An instance of <see cref="AuthenticationToken" /> that represents the access token generated as result of a successful authenication.
/// </returns>
public override Task<AuthenticationResult> AuthenticateAsync(AuthenticationParameters parameters, Action<string> promptAction = null, CancellationToken cancellationToken = default)
public override async Task<AuthenticationResult> AuthenticateAsync(AuthenticationParameters parameters, Action<string> promptAction = null, CancellationToken cancellationToken = default)
{
IPublicClientApplication app = GetClient(parameters.Account, parameters.Environment).AsPublicClient();

return app.AcquireTokenWithDeviceCode(parameters.Scopes, deviceCodeResult =>
Task<AuthenticationResult> task = Task<AuthenticationResult>.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;
}

/// <summary>
Expand Down
59 changes: 45 additions & 14 deletions src/PowerShell/Authenticators/InteractiveUserAuthenticator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -34,9 +35,13 @@ public override async Task<AuthenticationResult> AuthenticateAsync(Authenticatio
AuthenticationResult authResult;
IClientApplicationBase app;
InteractiveParameters interactiveParameters = parameters as InteractiveParameters;
Task<Task<AuthenticationResult>> task;
TcpListener listener = null;
int count = 0;
string redirectUri = null;

Queue<string> messages;

int port = 8399;

while (++port < 9000)
Expand All @@ -51,36 +56,62 @@ public override async Task<AuthenticationResult> 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<string>();

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<Task<AuthenticationResult>>.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<Task<AuthenticationResult>>.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;
}

Expand Down
13 changes: 9 additions & 4 deletions src/PowerShell/Network/DefaultOsBrowserWebUi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -46,15 +47,19 @@ internal class DefaultOsBrowserWebUi : ICustomWebUi
/// </summary>
private readonly string message;

private readonly Queue<string> messages;

/// <summary>
/// Initializes a new instance of the <see cref="DefaultOsBrowserWebUi" /> class.
/// </summary>
/// <param name="message">The message written to the console.</param>
public DefaultOsBrowserWebUi(string message)
public DefaultOsBrowserWebUi(Queue<string> messages, string message)
{
message.AssertNotEmpty(nameof(message));
messages.AssertNotNull(nameof(messages));

this.message = message;
this.messages = messages;
}

/// <summary>
Expand All @@ -66,14 +71,14 @@ public DefaultOsBrowserWebUi(string message)
/// <returns>The URI returned back from the STS authorization endpoint. This URI contains a code=CODE parameter that MSAL.NET will extract and redeem.</returns>
public async Task<Uri> 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))
{
Expand Down