|
7 | 7 | using GitCredentialManager;
|
8 | 8 | using GitCredentialManager.Authentication;
|
9 | 9 | using GitCredentialManager.Authentication.OAuth;
|
| 10 | +using GitCredentialManager.UI; |
| 11 | +using GitHub.UI.ViewModels; |
| 12 | +using GitHub.UI.Views; |
10 | 13 |
|
11 | 14 | namespace GitHub
|
12 | 15 | {
|
@@ -86,15 +89,69 @@ public async Task<AuthenticationPromptResult> GetAuthenticationAsync(Uri targetU
|
86 | 89 |
|
87 | 90 | ThrowIfUserInteractionDisabled();
|
88 | 91 |
|
89 |
| - if (Context.Settings.IsGuiPromptsEnabled && Context.SessionManager.IsDesktopSession && |
90 |
| - TryFindHelperCommand(out string command, out string args)) |
| 92 | + if (Context.Settings.IsGuiPromptsEnabled && Context.SessionManager.IsDesktopSession) |
91 | 93 | {
|
92 |
| - return await GetAuthenticationViaHelperAsync(targetUri, userName, modes, command, args); |
| 94 | + if (TryFindHelperCommand(out string command, out string args)) |
| 95 | + { |
| 96 | + return await GetAuthenticationViaHelperAsync(targetUri, userName, modes, command, args); |
| 97 | + } |
| 98 | + |
| 99 | + return await GetAuthenticationViaUiAsync(targetUri, userName, modes); |
93 | 100 | }
|
94 | 101 |
|
95 | 102 | return GetAuthenticationViaTty(targetUri, userName, modes);
|
96 | 103 | }
|
97 | 104 |
|
| 105 | + private async Task<AuthenticationPromptResult> GetAuthenticationViaUiAsync( |
| 106 | + Uri targetUri, string userName, AuthenticationModes modes) |
| 107 | + { |
| 108 | + var viewModel = new CredentialsViewModel(Context.Environment, Context.ProcessManager) |
| 109 | + { |
| 110 | + ShowBrowserLogin = (modes & AuthenticationModes.Browser) != 0, |
| 111 | + ShowDeviceLogin = (modes & AuthenticationModes.Device) != 0, |
| 112 | + ShowTokenLogin = (modes & AuthenticationModes.Pat) != 0, |
| 113 | + ShowBasicLogin = (modes & AuthenticationModes.Basic) != 0, |
| 114 | + }; |
| 115 | + |
| 116 | + if (!GitHubHostProvider.IsGitHubDotCom(targetUri)) |
| 117 | + { |
| 118 | + viewModel.EnterpriseUrl = targetUri.ToString(); |
| 119 | + } |
| 120 | + |
| 121 | + if (!string.IsNullOrWhiteSpace(userName)) |
| 122 | + { |
| 123 | + viewModel.UserName = userName; |
| 124 | + } |
| 125 | + |
| 126 | + await AvaloniaUi.ShowViewAsync<CredentialsView>(viewModel, GetParentWindowHandle(), CancellationToken.None); |
| 127 | + |
| 128 | + ThrowIfWindowCancelled(viewModel); |
| 129 | + |
| 130 | + switch (viewModel.SelectedMode) |
| 131 | + { |
| 132 | + case AuthenticationModes.Basic: |
| 133 | + return new AuthenticationPromptResult( |
| 134 | + AuthenticationModes.Basic, |
| 135 | + new GitCredential(viewModel.UserName, viewModel.Password) |
| 136 | + ); |
| 137 | + |
| 138 | + case AuthenticationModes.Browser: |
| 139 | + return new AuthenticationPromptResult(AuthenticationModes.Browser); |
| 140 | + |
| 141 | + case AuthenticationModes.Device: |
| 142 | + return new AuthenticationPromptResult(AuthenticationModes.Device); |
| 143 | + |
| 144 | + case AuthenticationModes.Pat: |
| 145 | + return new AuthenticationPromptResult( |
| 146 | + AuthenticationModes.Pat, |
| 147 | + new GitCredential(userName, viewModel.Password) |
| 148 | + ); |
| 149 | + |
| 150 | + default: |
| 151 | + throw new ArgumentOutOfRangeException(); |
| 152 | + } |
| 153 | + } |
| 154 | + |
98 | 155 | private AuthenticationPromptResult GetAuthenticationViaTty(Uri targetUri, string userName, AuthenticationModes modes)
|
99 | 156 | {
|
100 | 157 | ThrowIfTerminalPromptsDisabled();
|
@@ -229,15 +286,33 @@ public async Task<string> GetTwoFactorCodeAsync(Uri targetUri, bool isSms)
|
229 | 286 | {
|
230 | 287 | ThrowIfUserInteractionDisabled();
|
231 | 288 |
|
232 |
| - if (Context.Settings.IsGuiPromptsEnabled && Context.SessionManager.IsDesktopSession && |
233 |
| - TryFindHelperCommand(out string command, out string args)) |
| 289 | + if (Context.Settings.IsGuiPromptsEnabled && Context.SessionManager.IsDesktopSession) |
234 | 290 | {
|
235 |
| - return await GetTwoFactorCodeViaHelperAsync(isSms, args, command); |
| 291 | + if (TryFindHelperCommand(out string command, out string args)) |
| 292 | + { |
| 293 | + return await GetTwoFactorCodeViaHelperAsync(isSms, args, command); |
| 294 | + } |
| 295 | + |
| 296 | + return await GetTwoFactorCodeViaUiAsync(targetUri, isSms); |
236 | 297 | }
|
237 | 298 |
|
238 | 299 | return GetTwoFactorCodeViaTty(isSms);
|
239 | 300 | }
|
240 | 301 |
|
| 302 | + private async Task<string> GetTwoFactorCodeViaUiAsync(Uri targetUri, bool isSms) |
| 303 | + { |
| 304 | + var viewModel = new TwoFactorViewModel(Context.Environment, Context.ProcessManager) |
| 305 | + { |
| 306 | + IsSms = isSms |
| 307 | + }; |
| 308 | + |
| 309 | + await AvaloniaUi.ShowViewAsync<TwoFactorView>(viewModel, GetParentWindowHandle(), CancellationToken.None); |
| 310 | + |
| 311 | + ThrowIfWindowCancelled(viewModel); |
| 312 | + |
| 313 | + return viewModel.Code; |
| 314 | + } |
| 315 | + |
241 | 316 | private string GetTwoFactorCodeViaTty(bool isSms)
|
242 | 317 | {
|
243 | 318 | ThrowIfTerminalPromptsDisabled();
|
@@ -314,14 +389,15 @@ public async Task<OAuth2TokenResult> GetOAuthTokenViaDeviceCodeAsync(Uri targetU
|
314 | 389 | OAuth2DeviceCodeResult dcr = await oauthClient.GetDeviceCodeAsync(scopes, CancellationToken.None);
|
315 | 390 |
|
316 | 391 | // If we have a desktop session show the device code in a dialog
|
317 |
| - if (Context.Settings.IsGuiPromptsEnabled && Context.SessionManager.IsDesktopSession && |
318 |
| - TryFindHelperCommand(out string command, out string args)) |
| 392 | + if (Context.Settings.IsGuiPromptsEnabled && Context.SessionManager.IsDesktopSession) |
319 | 393 | {
|
320 | 394 | var promptCts = new CancellationTokenSource();
|
321 | 395 | var tokenCts = new CancellationTokenSource();
|
322 | 396 |
|
323 | 397 | // Show the dialog with the device code but don't await its closure
|
324 |
| - Task promptTask = ShowDeviceCodeViaHelperAsync(dcr, command, args, promptCts.Token); |
| 398 | + Task promptTask = TryFindHelperCommand(out string command, out string args) |
| 399 | + ? ShowDeviceCodeViaHelperAsync(dcr, command, args, promptCts.Token) |
| 400 | + : ShowDeviceCodeViaUiAsync(dcr, promptCts.Token); |
325 | 401 |
|
326 | 402 | // Start the request for an OAuth token but don't wait
|
327 | 403 | Task<OAuth2TokenResult> tokenTask = oauthClient.GetTokenByDeviceCodeAsync(dcr, tokenCts.Token);
|
@@ -354,6 +430,17 @@ public async Task<OAuth2TokenResult> GetOAuthTokenViaDeviceCodeAsync(Uri targetU
|
354 | 430 | return await GetOAuthTokenViaDeviceCodeViaTtyAsync(oauthClient, dcr);
|
355 | 431 | }
|
356 | 432 |
|
| 433 | + private Task ShowDeviceCodeViaUiAsync(OAuth2DeviceCodeResult dcr, CancellationToken ct) |
| 434 | + { |
| 435 | + var viewModel = new DeviceCodeViewModel(Context.Environment) |
| 436 | + { |
| 437 | + UserCode = dcr.UserCode, |
| 438 | + VerificationUrl = dcr.VerificationUri.ToString(), |
| 439 | + }; |
| 440 | + |
| 441 | + return AvaloniaUi.ShowViewAsync<DeviceCodeView>(viewModel, GetParentWindowHandle(), ct); |
| 442 | + } |
| 443 | + |
357 | 444 | private async Task<OAuth2TokenResult> GetOAuthTokenViaDeviceCodeViaTtyAsync(GitHubOAuth2Client oauthClient, OAuth2DeviceCodeResult dcr)
|
358 | 445 | {
|
359 | 446 | ThrowIfTerminalPromptsDisabled();
|
|
0 commit comments