Skip to content

Commit 6702935

Browse files
committed
oauth-ui: Add shared VMs and commands for OAuth
Add shared view models and commands for the OAuth GUI prompts. Two new commands and VMs are added, one for the initial 'mode' selection, and another to display the device code.
1 parent e323c83 commit 6702935

File tree

4 files changed

+273
-0
lines changed

4 files changed

+273
-0
lines changed
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
using System;
2+
using System.CommandLine;
3+
using System.CommandLine.Invocation;
4+
using System.Threading;
5+
using System.Threading.Tasks;
6+
using GitCredentialManager.UI.ViewModels;
7+
8+
namespace GitCredentialManager.UI.Commands
9+
{
10+
public abstract class DeviceCodeCommand : HelperCommand
11+
{
12+
protected DeviceCodeCommand(ICommandContext context)
13+
: base(context, "device", "Show device code prompt.")
14+
{
15+
AddOption(
16+
new Option<string>("--code", "User code.")
17+
);
18+
19+
AddOption(
20+
new Option<string>("--url", "Verification URL.")
21+
);
22+
23+
AddOption(
24+
new Option("--no-logo", "Hide the Git Credential Manager logo and logotype.")
25+
);
26+
27+
Handler = CommandHandler.Create<string, string, bool>(ExecuteAsync);
28+
}
29+
30+
private async Task<int> ExecuteAsync(string code, string url, bool noLogo)
31+
{
32+
var viewModel = new DeviceCodeViewModel(Context.Environment)
33+
{
34+
UserCode = code,
35+
VerificationUrl = url,
36+
};
37+
38+
viewModel.ShowProductHeader = !noLogo;
39+
40+
await ShowAsync(viewModel, CancellationToken.None);
41+
42+
if (!viewModel.WindowResult)
43+
{
44+
throw new Exception("User cancelled dialog.");
45+
}
46+
47+
return 0;
48+
}
49+
50+
protected abstract Task ShowAsync(DeviceCodeViewModel viewModel, CancellationToken ct);
51+
}
52+
}
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.CommandLine;
4+
using System.CommandLine.Invocation;
5+
using System.Threading;
6+
using System.Threading.Tasks;
7+
using GitCredentialManager.Authentication;
8+
using GitCredentialManager.UI.ViewModels;
9+
10+
namespace GitCredentialManager.UI.Commands
11+
{
12+
public abstract class OAuthCommand : HelperCommand
13+
{
14+
protected OAuthCommand(ICommandContext context)
15+
: base(context, "oauth", "Show OAuth authentication prompt.")
16+
{
17+
AddOption(
18+
new Option<string>("--title", "Window title (optional).")
19+
);
20+
21+
AddOption(
22+
new Option<string>("--resource", "Resource name or URL (optional).")
23+
);
24+
25+
AddOption(
26+
new Option("--browser", "Show browser authentication option.")
27+
);
28+
29+
AddOption(
30+
new Option("--device-code", "Show device code authentication option.")
31+
);
32+
33+
AddOption(
34+
new Option("--no-logo", "Hide the Git Credential Manager logo and logotype.")
35+
);
36+
37+
Handler = CommandHandler.Create<CommandOptions>(ExecuteAsync);
38+
}
39+
40+
private class CommandOptions
41+
{
42+
public string Title { get; set; }
43+
public string Resource { get; set; }
44+
public bool Browser { get; set; }
45+
public bool DeviceCode { get; set; }
46+
public bool NoLogo { get; set; }
47+
}
48+
49+
private async Task<int> ExecuteAsync(CommandOptions options)
50+
{
51+
var viewModel = new OAuthViewModel();
52+
53+
viewModel.Title = !string.IsNullOrWhiteSpace(options.Title)
54+
? options.Title
55+
: "Git Credential Manager";
56+
57+
viewModel.Description = !string.IsNullOrWhiteSpace(options.Resource)
58+
? $"Sign in to '{options.Resource}'"
59+
: "Select a sign-in option";
60+
61+
viewModel.ShowBrowserLogin = options.Browser;
62+
viewModel.ShowDeviceCodeLogin = options.DeviceCode;
63+
viewModel.ShowProductHeader = !options.NoLogo;
64+
65+
await ShowAsync(viewModel, CancellationToken.None);
66+
67+
if (!viewModel.WindowResult)
68+
{
69+
throw new Exception("User cancelled dialog.");
70+
}
71+
72+
var result = new Dictionary<string, string>();
73+
switch (viewModel.SelectedMode)
74+
{
75+
case OAuthAuthenticationModes.Browser:
76+
result["mode"] = "browser";
77+
break;
78+
79+
case OAuthAuthenticationModes.DeviceCode:
80+
result["mode"] = "devicecode";
81+
break;
82+
83+
default:
84+
throw new ArgumentOutOfRangeException();
85+
}
86+
87+
WriteResult(result);
88+
return 0;
89+
}
90+
91+
protected abstract Task ShowAsync(OAuthViewModel viewModel, CancellationToken ct);
92+
}
93+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
using System.Windows.Input;
2+
3+
namespace GitCredentialManager.UI.ViewModels
4+
{
5+
public class DeviceCodeViewModel : WindowViewModel
6+
{
7+
private readonly IEnvironment _environment;
8+
9+
private ICommand _verificationUrlCommand;
10+
private string _verificationUrl;
11+
private string _userCode;
12+
private bool _showProductHeader;
13+
14+
public DeviceCodeViewModel()
15+
{
16+
// Constructor the XAML designer
17+
}
18+
19+
public DeviceCodeViewModel(IEnvironment environment)
20+
{
21+
EnsureArgument.NotNull(environment, nameof(environment));
22+
23+
_environment = environment;
24+
25+
Title = "Device code authentication";
26+
VerificationUrlCommand = new RelayCommand(OpenVerificationUrl);
27+
}
28+
29+
private void OpenVerificationUrl()
30+
{
31+
BrowserUtils.OpenDefaultBrowser(_environment, VerificationUrl);
32+
}
33+
34+
public string UserCode
35+
{
36+
get => _userCode;
37+
set => SetAndRaisePropertyChanged(ref _userCode, value);
38+
}
39+
40+
public string VerificationUrl
41+
{
42+
get => _verificationUrl;
43+
set => SetAndRaisePropertyChanged(ref _verificationUrl, value);
44+
}
45+
46+
public ICommand VerificationUrlCommand
47+
{
48+
get => _verificationUrlCommand;
49+
set => SetAndRaisePropertyChanged(ref _verificationUrlCommand, value);
50+
}
51+
52+
public bool ShowProductHeader
53+
{
54+
get => _showProductHeader;
55+
set => SetAndRaisePropertyChanged(ref _showProductHeader, value);
56+
}
57+
}
58+
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
using GitCredentialManager.Authentication;
2+
3+
namespace GitCredentialManager.UI.ViewModels
4+
{
5+
public class OAuthViewModel : WindowViewModel
6+
{
7+
private string _description;
8+
private bool _showProductHeader;
9+
private bool _showBrowserLogin;
10+
private bool _showDeviceCodeLogin;
11+
private RelayCommand _signInBrowserCommand;
12+
private RelayCommand _signInDeviceCodeCommand;
13+
14+
public OAuthViewModel()
15+
{
16+
SignInBrowserCommand = new RelayCommand(SignInWithBrowser);
17+
SignInDeviceCodeCommand = new RelayCommand(SignInWithDeviceCode);
18+
}
19+
20+
private void SignInWithBrowser()
21+
{
22+
SelectedMode = OAuthAuthenticationModes.Browser;
23+
Accept();
24+
}
25+
26+
private void SignInWithDeviceCode()
27+
{
28+
SelectedMode = OAuthAuthenticationModes.DeviceCode;
29+
Accept();
30+
}
31+
32+
public string Description
33+
{
34+
get => _description;
35+
set => SetAndRaisePropertyChanged(ref _description, value);
36+
}
37+
38+
public bool ShowProductHeader
39+
{
40+
get => _showProductHeader;
41+
set => SetAndRaisePropertyChanged(ref _showProductHeader, value);
42+
}
43+
44+
public bool ShowBrowserLogin
45+
{
46+
get => _showBrowserLogin;
47+
set => SetAndRaisePropertyChanged(ref _showBrowserLogin, value);
48+
}
49+
50+
public bool ShowDeviceCodeLogin
51+
{
52+
get => _showDeviceCodeLogin;
53+
set => SetAndRaisePropertyChanged(ref _showDeviceCodeLogin, value);
54+
}
55+
56+
public RelayCommand SignInBrowserCommand
57+
{
58+
get => _signInBrowserCommand;
59+
set => SetAndRaisePropertyChanged(ref _signInBrowserCommand, value);
60+
}
61+
62+
public RelayCommand SignInDeviceCodeCommand
63+
{
64+
get => _signInDeviceCodeCommand;
65+
set => SetAndRaisePropertyChanged(ref _signInDeviceCodeCommand, value);
66+
}
67+
68+
public OAuthAuthenticationModes SelectedMode { get; private set; }
69+
}
70+
}

0 commit comments

Comments
 (0)