Browse Source

Update the WinForms/WPF samples to allow selecting a preferred service for local server authentication

pull/1940/head
Kévin Chalet 2 years ago
parent
commit
08eadde6ba
  1. 2
      sandbox/OpenIddict.Sandbox.Console.Client/InteractiveService.cs
  2. 21
      sandbox/OpenIddict.Sandbox.WinForms.Client/MainForm.Designer.cs
  3. 139
      sandbox/OpenIddict.Sandbox.WinForms.Client/MainForm.cs
  4. 40
      sandbox/OpenIddict.Sandbox.WinForms.Client/Worker.cs
  5. 5
      sandbox/OpenIddict.Sandbox.Wpf.Client/MainWindow.xaml
  6. 79
      sandbox/OpenIddict.Sandbox.Wpf.Client/MainWindow.xaml.cs
  7. 40
      sandbox/OpenIddict.Sandbox.Wpf.Client/Worker.cs

2
sandbox/OpenIddict.Sandbox.Console.Client/InteractiveService.cs

@ -71,6 +71,7 @@ public class InteractiveService : BackgroundService
// Wait for the user to complete the demand on the other device. // Wait for the user to complete the demand on the other device.
principal = (await _service.AuthenticateWithDeviceAsync(new() principal = (await _service.AuthenticateWithDeviceAsync(new()
{ {
CancellationToken = stoppingToken,
DeviceCode = result.DeviceCode, DeviceCode = result.DeviceCode,
Interval = result.Interval, Interval = result.Interval,
ProviderName = provider, ProviderName = provider,
@ -92,6 +93,7 @@ public class InteractiveService : BackgroundService
// Wait for the user to complete the authorization process. // Wait for the user to complete the authorization process.
principal = (await _service.AuthenticateInteractivelyAsync(new() principal = (await _service.AuthenticateInteractivelyAsync(new()
{ {
CancellationToken = stoppingToken,
Nonce = result.Nonce Nonce = result.Nonce
})).Principal; })).Principal;
} }

21
sandbox/OpenIddict.Sandbox.WinForms.Client/MainForm.Designer.cs

@ -30,13 +30,14 @@ partial class MainForm
{ {
this.LocalLogin = new System.Windows.Forms.Button(); this.LocalLogin = new System.Windows.Forms.Button();
this.GitHubLogin = new System.Windows.Forms.Button(); this.GitHubLogin = new System.Windows.Forms.Button();
this.LocalLoginWithGitHub = new System.Windows.Forms.Button();
this.SuspendLayout(); this.SuspendLayout();
// //
// LocalLogin // LocalLogin
// //
this.LocalLogin.Location = new System.Drawing.Point(258, 93); this.LocalLogin.Location = new System.Drawing.Point(214, 32);
this.LocalLogin.Name = "LocalLogin"; this.LocalLogin.Name = "LocalLogin";
this.LocalLogin.Size = new System.Drawing.Size(283, 83); this.LocalLogin.Size = new System.Drawing.Size(391, 83);
this.LocalLogin.TabIndex = 0; this.LocalLogin.TabIndex = 0;
this.LocalLogin.Text = "Log in using the local server"; this.LocalLogin.Text = "Log in using the local server";
this.LocalLogin.UseVisualStyleBackColor = true; this.LocalLogin.UseVisualStyleBackColor = true;
@ -44,20 +45,31 @@ partial class MainForm
// //
// GitHubLogin // GitHubLogin
// //
this.GitHubLogin.Location = new System.Drawing.Point(258, 258); this.GitHubLogin.Location = new System.Drawing.Point(214, 321);
this.GitHubLogin.Name = "GitHubLogin"; this.GitHubLogin.Name = "GitHubLogin";
this.GitHubLogin.Size = new System.Drawing.Size(283, 83); this.GitHubLogin.Size = new System.Drawing.Size(391, 83);
this.GitHubLogin.TabIndex = 1; this.GitHubLogin.TabIndex = 1;
this.GitHubLogin.Text = "Log in using GitHub"; this.GitHubLogin.Text = "Log in using GitHub";
this.GitHubLogin.UseVisualStyleBackColor = true; this.GitHubLogin.UseVisualStyleBackColor = true;
this.GitHubLogin.Click += new System.EventHandler(this.GitHubLoginButton_Click); this.GitHubLogin.Click += new System.EventHandler(this.GitHubLoginButton_Click);
// //
// LocalLoginWithGitHub
//
this.LocalLoginWithGitHub.Location = new System.Drawing.Point(214, 177);
this.LocalLoginWithGitHub.Name = "LocalLoginWithGitHub";
this.LocalLoginWithGitHub.Size = new System.Drawing.Size(391, 83);
this.LocalLoginWithGitHub.TabIndex = 0;
this.LocalLoginWithGitHub.Text = "Log in using the local server (preferred service: GitHub)";
this.LocalLoginWithGitHub.UseVisualStyleBackColor = true;
this.LocalLoginWithGitHub.Click += new System.EventHandler(this.LocalLoginWithGitHubButton_Click);
//
// MainForm // MainForm
// //
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(800, 450); this.ClientSize = new System.Drawing.Size(800, 450);
this.Controls.Add(this.GitHubLogin); this.Controls.Add(this.GitHubLogin);
this.Controls.Add(this.LocalLoginWithGitHub);
this.Controls.Add(this.LocalLogin); this.Controls.Add(this.LocalLogin);
this.Name = "MainForm"; this.Name = "MainForm";
this.Text = "OpenIddict WinForms client"; this.Text = "OpenIddict WinForms client";
@ -69,4 +81,5 @@ partial class MainForm
private Button LocalLogin; private Button LocalLogin;
private Button GitHubLogin; private Button GitHubLogin;
private Button LocalLoginWithGitHub;
} }

139
sandbox/OpenIddict.Sandbox.WinForms.Client/MainForm.cs

@ -1,4 +1,5 @@
using Dapplo.Microsoft.Extensions.Hosting.WinForms; using Dapplo.Microsoft.Extensions.Hosting.WinForms;
using OpenIddict.Abstractions;
using OpenIddict.Client; using OpenIddict.Client;
using static OpenIddict.Abstractions.OpenIddictConstants; using static OpenIddict.Abstractions.OpenIddictConstants;
using static OpenIddict.Abstractions.OpenIddictExceptions; using static OpenIddict.Abstractions.OpenIddictExceptions;
@ -20,88 +21,112 @@ public partial class MainForm : Form, IWinFormsShell
private async void LocalLoginButton_Click(object sender, EventArgs e) private async void LocalLoginButton_Click(object sender, EventArgs e)
=> await AuthenticateAsync("Local"); => await AuthenticateAsync("Local");
private async void LocalLoginWithGitHubButton_Click(object sender, EventArgs e)
=> await AuthenticateAsync("Local", new()
{
[Parameters.IdentityProvider] = Providers.GitHub
});
private async void GitHubLoginButton_Click(object sender, EventArgs e) private async void GitHubLoginButton_Click(object sender, EventArgs e)
=> await AuthenticateAsync(Providers.GitHub); => await AuthenticateAsync(Providers.GitHub);
private async Task AuthenticateAsync(string provider) private async Task AuthenticateAsync(string provider, Dictionary<string, OpenIddictParameter>? parameters = null)
{ {
using var source = new CancellationTokenSource(delay: TimeSpan.FromSeconds(90)); // Disable the login buttons to prevent concurrent authentication operations.
LocalLogin.Enabled = false;
LocalLoginWithGitHub.Enabled = false;
GitHubLogin.Enabled = false;
try try
{ {
// Ask OpenIddict to initiate the authentication flow (typically, by starting the system browser). using var source = new CancellationTokenSource(delay: TimeSpan.FromSeconds(90));
var result = await _service.ChallengeInteractivelyAsync(new()
{
CancellationToken = source.Token,
ProviderName = provider
});
// Wait for the user to complete the authorization process. try
var principal = (await _service.AuthenticateInteractivelyAsync(new()
{ {
Nonce = result.Nonce // Ask OpenIddict to initiate the authentication flow (typically, by starting the system browser).
})).Principal; var result = await _service.ChallengeInteractivelyAsync(new()
{
AdditionalAuthorizationRequestParameters = parameters,
CancellationToken = source.Token,
ProviderName = provider
});
// Wait for the user to complete the authorization process.
var principal = (await _service.AuthenticateInteractivelyAsync(new()
{
CancellationToken = source.Token,
Nonce = result.Nonce
})).Principal;
#if SUPPORTS_WINFORMS_TASK_DIALOG #if SUPPORTS_WINFORMS_TASK_DIALOG
TaskDialog.ShowDialog(new TaskDialogPage TaskDialog.ShowDialog(new TaskDialogPage
{ {
Caption = "Authentication successful", Caption = "Authentication successful",
Heading = "Authentication successful", Heading = "Authentication successful",
Icon = TaskDialogIcon.ShieldSuccessGreenBar, Icon = TaskDialogIcon.ShieldSuccessGreenBar,
Text = $"Welcome, {principal.FindFirst(Claims.Name)!.Value}." Text = $"Welcome, {principal.FindFirst(Claims.Name)!.Value}."
}); });
#else #else
MessageBox.Show($"Welcome, {principal.FindFirst(Claims.Name)!.Value}.", MessageBox.Show($"Welcome, {principal.FindFirst(Claims.Name)!.Value}.",
"Authentication successful", MessageBoxButtons.OK, MessageBoxIcon.Information); "Authentication successful", MessageBoxButtons.OK, MessageBoxIcon.Information);
#endif #endif
} }
catch (OperationCanceledException) catch (OperationCanceledException)
{
#if SUPPORTS_WINFORMS_TASK_DIALOG
TaskDialog.ShowDialog(new TaskDialogPage
{ {
Caption = "Authentication timed out", #if SUPPORTS_WINFORMS_TASK_DIALOG
Heading = "Authentication timed out", TaskDialog.ShowDialog(new TaskDialogPage
Icon = TaskDialogIcon.Warning, {
Text = "The authentication process was aborted." Caption = "Authentication timed out",
}); Heading = "Authentication timed out",
Icon = TaskDialogIcon.Warning,
Text = "The authentication process was aborted."
});
#else #else
MessageBox.Show("The authentication process was aborted.", MessageBox.Show("The authentication process was aborted.",
"Authentication timed out", MessageBoxButtons.OK, MessageBoxIcon.Warning); "Authentication timed out", MessageBoxButtons.OK, MessageBoxIcon.Warning);
#endif #endif
} }
catch (ProtocolException exception) when (exception.Error is Errors.AccessDenied) catch (ProtocolException exception) when (exception.Error is Errors.AccessDenied)
{
#if SUPPORTS_WINFORMS_TASK_DIALOG
TaskDialog.ShowDialog(new TaskDialogPage
{ {
Caption = "Authorization denied", #if SUPPORTS_WINFORMS_TASK_DIALOG
Heading = "Authorization denied", TaskDialog.ShowDialog(new TaskDialogPage
Icon = TaskDialogIcon.Warning, {
Text = "The authorization was denied by the end user." Caption = "Authorization denied",
}); Heading = "Authorization denied",
Icon = TaskDialogIcon.Warning,
Text = "The authorization was denied by the end user."
});
#else #else
MessageBox.Show("The authorization was denied by the end user.", MessageBox.Show("The authorization was denied by the end user.",
"Authorization denied", MessageBoxButtons.OK, MessageBoxIcon.Warning); "Authorization denied", MessageBoxButtons.OK, MessageBoxIcon.Warning);
#endif #endif
} }
catch catch
{
#if SUPPORTS_WINFORMS_TASK_DIALOG
TaskDialog.ShowDialog(new TaskDialogPage
{ {
Caption = "Authentication failed", #if SUPPORTS_WINFORMS_TASK_DIALOG
Heading = "Authentication failed", TaskDialog.ShowDialog(new TaskDialogPage
Icon = TaskDialogIcon.Error, {
Text = "An error occurred while trying to authenticate the user." Caption = "Authentication failed",
}); Heading = "Authentication failed",
Icon = TaskDialogIcon.Error,
Text = "An error occurred while trying to authenticate the user."
});
#else #else
MessageBox.Show("An error occurred while trying to authenticate the user.", MessageBox.Show("An error occurred while trying to authenticate the user.",
"Authentication failed", MessageBoxButtons.OK, MessageBoxIcon.Error); "Authentication failed", MessageBoxButtons.OK, MessageBoxIcon.Error);
#endif #endif
}
}
finally
{
// Re-enable the login buttons to allow starting a new authentication operation.
LocalLogin.Enabled = true;
LocalLoginWithGitHub.Enabled = true;
GitHubLogin.Enabled = true;
} }
} }
} }

40
sandbox/OpenIddict.Sandbox.WinForms.Client/Worker.cs

@ -20,40 +20,26 @@ public class Worker : IHostedService
var context = scope.ServiceProvider.GetRequiredService<DbContext>(); var context = scope.ServiceProvider.GetRequiredService<DbContext>();
await context.Database.EnsureCreatedAsync(); await context.Database.EnsureCreatedAsync();
RegistryKey? root = null;
// Create the registry entries necessary to handle URI protocol activations. // Create the registry entries necessary to handle URI protocol activations.
// //
// Note: this sample creates the entry under the current user account (as it doesn't
// require administrator rights), but the registration can also be added globally
// in HKEY_CLASSES_ROOT (in this case, it should be added by a dedicated installer).
//
// Alternatively, the application can be packaged and use windows.protocol to // Alternatively, the application can be packaged and use windows.protocol to
// register the protocol handler/custom URI scheme with the operation system. // register the protocol handler/custom URI scheme with the operation system.
try using var root = Registry.CurrentUser.CreateSubKey("SOFTWARE\\Classes\\com.openiddict.sandbox.winforms.client");
{ root.SetValue(string.Empty, "URL:com.openiddict.sandbox.winforms.client");
// Note: this sample creates the entry under the current user account (as it doesn't root.SetValue("URL Protocol", string.Empty);
// require administrator rights), but the registration can also be added globally
// in HKEY_CLASSES_ROOT (in this case, it should be added by a dedicated installer). using var command = root.CreateSubKey("shell\\open\\command");
root = Registry.CurrentUser.OpenSubKey("SOFTWARE\\Classes\\com.openiddict.sandbox.winforms.client"); command.SetValue(string.Empty, string.Format("\"{0}\" \"%1\"",
if (root is null)
{
root = Registry.CurrentUser.CreateSubKey("SOFTWARE\\Classes\\com.openiddict.sandbox.winforms.client");
root.SetValue(string.Empty, "URL:com.openiddict.sandbox.winforms.client");
root.SetValue("URL Protocol", string.Empty);
using var command = root.CreateSubKey("shell\\open\\command");
command.SetValue(string.Empty, string.Format("\"{0}\" \"%1\"",
#if SUPPORTS_ENVIRONMENT_PROCESS_PATH #if SUPPORTS_ENVIRONMENT_PROCESS_PATH
Environment.ProcessPath Environment.ProcessPath
#else #else
Process.GetCurrentProcess().MainModule.FileName Process.GetCurrentProcess().MainModule.FileName
#endif #endif
)); ));
}
}
finally
{
root?.Dispose();
}
} }
public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask; public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;

5
sandbox/OpenIddict.Sandbox.Wpf.Client/MainWindow.xaml

@ -6,7 +6,8 @@
mc:Ignorable="d" mc:Ignorable="d"
Title="OpenIddict WPF client" Height="450" Width="800"> Title="OpenIddict WPF client" Height="450" Width="800">
<Grid> <Grid>
<Button Content="Log in using the local server" HorizontalAlignment="Center" VerticalAlignment="Top" Click="LocalLoginButton_Click" Height="64" Width="252" FontSize="20" Margin="0,130,0,0" /> <Button Name="LocalLogin" Content="Log in using the local server" HorizontalAlignment="Center" VerticalAlignment="Top" Click="LocalLoginButton_Click" Height="64" Width="540" FontSize="20" Margin="0,50,0,0" />
<Button Content="Log in using GitHub" HorizontalAlignment="Center" VerticalAlignment="Top" Click="GitHubLoginButton_Click" Height="64" Width="252" FontSize="20" Margin="0,234,0,0" /> <Button Name="LocalLoginWithGitHub" Content="Log in using the local server (preferred service: GitHub)" HorizontalAlignment="Center" VerticalAlignment="Center" Click="LocalLoginWithGitHubButton_Click" Height="64" Width="539" FontSize="20" />
<Button Name="GitHubLogin" Content="Log in using GitHub" HorizontalAlignment="Center" VerticalAlignment="Bottom" Click="GitHubLoginButton_Click" Height="64" Width="539" FontSize="20" Margin="0,0,0,50" />
</Grid> </Grid>
</Window> </Window>

79
sandbox/OpenIddict.Sandbox.Wpf.Client/MainWindow.xaml.cs

@ -1,5 +1,6 @@
using System.Windows; using System.Windows;
using Dapplo.Microsoft.Extensions.Hosting.Wpf; using Dapplo.Microsoft.Extensions.Hosting.Wpf;
using OpenIddict.Abstractions;
using OpenIddict.Client; using OpenIddict.Client;
using static OpenIddict.Abstractions.OpenIddictConstants; using static OpenIddict.Abstractions.OpenIddictConstants;
using static OpenIddict.Abstractions.OpenIddictExceptions; using static OpenIddict.Abstractions.OpenIddictExceptions;
@ -21,48 +22,72 @@ public partial class MainWindow : Window, IWpfShell
private async void LocalLoginButton_Click(object sender, RoutedEventArgs e) private async void LocalLoginButton_Click(object sender, RoutedEventArgs e)
=> await AuthenticateAsync("Local"); => await AuthenticateAsync("Local");
private async void LocalLoginWithGitHubButton_Click(object sender, RoutedEventArgs e)
=> await AuthenticateAsync("Local", new()
{
[Parameters.IdentityProvider] = Providers.GitHub
});
private async void GitHubLoginButton_Click(object sender, RoutedEventArgs e) private async void GitHubLoginButton_Click(object sender, RoutedEventArgs e)
=> await AuthenticateAsync(Providers.GitHub); => await AuthenticateAsync(Providers.GitHub);
private async Task AuthenticateAsync(string provider) private async Task AuthenticateAsync(string provider, Dictionary<string, OpenIddictParameter>? parameters = null)
{ {
using var source = new CancellationTokenSource(delay: TimeSpan.FromSeconds(90)); // Disable the login buttons to prevent concurrent authentication operations.
LocalLogin.IsEnabled = false;
LocalLoginWithGitHub.IsEnabled = false;
GitHubLogin.IsEnabled = false;
try try
{ {
// Ask OpenIddict to initiate the authentication flow (typically, by starting the system browser). using var source = new CancellationTokenSource(delay: TimeSpan.FromSeconds(90));
var result = await _service.ChallengeInteractivelyAsync(new()
{
CancellationToken = source.Token,
ProviderName = provider
});
// Wait for the user to complete the authorization process. try
var principal = (await _service.AuthenticateInteractivelyAsync(new()
{ {
Nonce = result.Nonce // Ask OpenIddict to initiate the authentication flow (typically, by starting the system browser).
})).Principal; var result = await _service.ChallengeInteractivelyAsync(new()
{
AdditionalAuthorizationRequestParameters = parameters,
CancellationToken = source.Token,
ProviderName = provider
});
MessageBox.Show($"Welcome, {principal.FindFirst(Claims.Name)!.Value}.", // Wait for the user to complete the authorization process.
"Authentication successful", MessageBoxButton.OK, MessageBoxImage.Information); var principal = (await _service.AuthenticateInteractivelyAsync(new()
} {
CancellationToken = source.Token,
Nonce = result.Nonce
})).Principal;
catch (OperationCanceledException) MessageBox.Show($"Welcome, {principal.FindFirst(Claims.Name)!.Value}.",
{ "Authentication successful", MessageBoxButton.OK, MessageBoxImage.Information);
MessageBox.Show("The authentication process was aborted.", }
"Authentication timed out", MessageBoxButton.OK, MessageBoxImage.Warning);
}
catch (ProtocolException exception) when (exception.Error is Errors.AccessDenied) catch (OperationCanceledException)
{ {
MessageBox.Show("The authorization was denied by the end user.", MessageBox.Show("The authentication process was aborted.",
"Authorization denied", MessageBoxButton.OK, MessageBoxImage.Warning); "Authentication timed out", MessageBoxButton.OK, MessageBoxImage.Warning);
}
catch (ProtocolException exception) when (exception.Error is Errors.AccessDenied)
{
MessageBox.Show("The authorization was denied by the end user.",
"Authorization denied", MessageBoxButton.OK, MessageBoxImage.Warning);
}
catch
{
MessageBox.Show("An error occurred while trying to authenticate the user.",
"Authentication failed", MessageBoxButton.OK, MessageBoxImage.Error);
}
} }
catch finally
{ {
MessageBox.Show("An error occurred while trying to authenticate the user.", // Re-enable the login buttons to allow starting a new authentication operation.
"Authentication failed", MessageBoxButton.OK, MessageBoxImage.Error); LocalLogin.IsEnabled = true;
LocalLoginWithGitHub.IsEnabled = true;
GitHubLogin.IsEnabled = true;
} }
} }
} }

40
sandbox/OpenIddict.Sandbox.Wpf.Client/Worker.cs

@ -20,40 +20,26 @@ public class Worker : IHostedService
var context = scope.ServiceProvider.GetRequiredService<DbContext>(); var context = scope.ServiceProvider.GetRequiredService<DbContext>();
await context.Database.EnsureCreatedAsync(); await context.Database.EnsureCreatedAsync();
RegistryKey? root = null;
// Create the registry entries necessary to handle URI protocol activations. // Create the registry entries necessary to handle URI protocol activations.
// //
// Note: this sample creates the entry under the current user account (as it doesn't
// require administrator rights), but the registration can also be added globally
// in HKEY_CLASSES_ROOT (in this case, it should be added by a dedicated installer).
//
// Alternatively, the application can be packaged and use windows.protocol to // Alternatively, the application can be packaged and use windows.protocol to
// register the protocol handler/custom URI scheme with the operation system. // register the protocol handler/custom URI scheme with the operation system.
try using var root = Registry.CurrentUser.CreateSubKey("SOFTWARE\\Classes\\com.openiddict.sandbox.wpf.client");
{ root.SetValue(string.Empty, "URL:com.openiddict.sandbox.wpf.client");
// Note: this sample creates the entry under the current user account (as it doesn't root.SetValue("URL Protocol", string.Empty);
// require administrator rights), but the registration can also be added globally
// in HKEY_CLASSES_ROOT (in this case, it should be added by a dedicated installer). using var command = root.CreateSubKey("shell\\open\\command");
root = Registry.CurrentUser.OpenSubKey("SOFTWARE\\Classes\\com.openiddict.sandbox.wpf.client"); command.SetValue(string.Empty, string.Format("\"{0}\" \"%1\"",
if (root is null)
{
root = Registry.CurrentUser.CreateSubKey("SOFTWARE\\Classes\\com.openiddict.sandbox.wpf.client");
root.SetValue(string.Empty, "URL:com.openiddict.sandbox.wpf.client");
root.SetValue("URL Protocol", string.Empty);
using var command = root.CreateSubKey("shell\\open\\command");
command.SetValue(string.Empty, string.Format("\"{0}\" \"%1\"",
#if SUPPORTS_ENVIRONMENT_PROCESS_PATH #if SUPPORTS_ENVIRONMENT_PROCESS_PATH
Environment.ProcessPath Environment.ProcessPath
#else #else
Process.GetCurrentProcess().MainModule.FileName Process.GetCurrentProcess().MainModule.FileName
#endif #endif
)); ));
}
}
finally
{
root?.Dispose();
}
} }
public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask; public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;

Loading…
Cancel
Save