2
2
// Licensed under the MIT license.
3
3
using System ;
4
4
using System . Threading . Tasks ;
5
+ using System . Collections . Generic ;
6
+ using System . IO ;
7
+ using System . Reflection ;
5
8
using Microsoft . Git . CredentialManager ;
9
+ using Microsoft . Git . CredentialManager . Authentication ;
6
10
7
11
namespace GitHub
8
12
{
@@ -13,58 +17,98 @@ public interface IGitHubAuthentication
13
17
Task < string > GetAuthenticationCodeAsync ( Uri targetUri , bool isSms ) ;
14
18
}
15
19
16
- public class TtyGitHubPromptAuthentication : IGitHubAuthentication
20
+ public class GitHubAuthentication : AuthenticationBase , IGitHubAuthentication
17
21
{
18
- private readonly ICommandContext _context ;
22
+ public GitHubAuthentication ( ICommandContext context )
23
+ : base ( context ) { }
19
24
20
- public TtyGitHubPromptAuthentication ( ICommandContext context )
25
+ public async Task < ICredential > GetCredentialsAsync ( Uri targetUri )
21
26
{
22
- EnsureArgument . NotNull ( context , nameof ( context ) ) ;
27
+ string userName , password ;
23
28
24
- _context = context ;
25
- }
29
+ if ( TryFindHelperExecutablePath ( out string helperPath ) )
30
+ {
31
+ IDictionary < string , string > resultDict = await InvokeHelperAsync ( helperPath , "--prompt userpass" , null ) ;
26
32
27
- public Task < ICredential > GetCredentialsAsync ( Uri targetUri )
28
- {
29
- EnsureTerminalPromptsEnabled ( ) ;
33
+ if ( ! resultDict . TryGetValue ( "username" , out userName ) )
34
+ {
35
+ throw new Exception ( "Missing username in response" ) ;
36
+ }
37
+
38
+ if ( ! resultDict . TryGetValue ( "password" , out password ) )
39
+ {
40
+ throw new Exception ( "Missing password in response" ) ;
41
+ }
42
+ }
43
+ else
44
+ {
45
+ EnsureTerminalPromptsEnabled ( ) ;
30
46
31
- _context . Terminal . WriteLine ( "Enter credentials for '{0}'..." , targetUri ) ;
47
+ Context . Terminal . WriteLine ( "Enter credentials for '{0}'..." , targetUri ) ;
32
48
33
- string userName = _context . Terminal . Prompt ( "Username" ) ;
34
- string password = _context . Terminal . PromptSecret ( "Password" ) ;
49
+ userName = Context . Terminal . Prompt ( "Username" ) ;
50
+ password = Context . Terminal . PromptSecret ( "Password" ) ;
51
+ }
35
52
36
- return Task . FromResult < ICredential > ( new GitCredential ( userName , password ) ) ;
53
+ return new GitCredential ( userName , password ) ;
37
54
}
38
-
39
- public Task < string > GetAuthenticationCodeAsync ( Uri targetUri , bool isSms )
55
+ public async Task < string > GetAuthenticationCodeAsync ( Uri targetUri , bool isSms )
40
56
{
41
- EnsureTerminalPromptsEnabled ( ) ;
57
+ if ( TryFindHelperExecutablePath ( out string helperPath ) )
58
+ {
59
+ IDictionary < string , string > resultDict = await InvokeHelperAsync ( helperPath , "--prompt authcode" , null ) ;
42
60
43
- _context . Terminal . WriteLine ( "Two-factor authentication is enabled and an authentication code is required." ) ;
61
+ if ( ! resultDict . TryGetValue ( "authcode" , out string authCode ) )
62
+ {
63
+ throw new Exception ( "Missing authentication code in response" ) ;
64
+ }
44
65
45
- if ( isSms )
46
- {
47
- _context . Terminal . WriteLine ( "An SMS containing the authentication code has been sent to your registered device." ) ;
66
+ return authCode ;
48
67
}
49
68
else
50
69
{
51
- _context . Terminal . WriteLine ( "Use your registered authentication app to generate an authentication code." ) ;
52
- }
70
+ EnsureTerminalPromptsEnabled ( ) ;
71
+
72
+ Context . Terminal . WriteLine ( "Two-factor authentication is enabled and an authentication code is required." ) ;
53
73
54
- string authCode = _context . Terminal . Prompt ( "Authentication code" ) ;
74
+ if ( isSms )
75
+ {
76
+ Context . Terminal . WriteLine ( "An SMS containing the authentication code has been sent to your registered device." ) ;
77
+ }
78
+ else
79
+ {
80
+ Context . Terminal . WriteLine ( "Use your registered authentication app to generate an authentication code." ) ;
81
+ }
55
82
56
- return Task . FromResult ( authCode ) ;
83
+ return Context . Terminal . Prompt ( "Authentication code" ) ;
84
+ }
57
85
}
58
86
59
- private void EnsureTerminalPromptsEnabled ( )
87
+ private bool TryFindHelperExecutablePath ( out string path )
60
88
{
61
- if ( _context . TryGetEnvironmentVariable ( Constants . EnvironmentVariables . GitTerminalPrompts , out string envarPrompts )
62
- && envarPrompts == "0" )
89
+ string helperName = GitHubConstants . AuthHelperName ;
90
+
91
+ if ( PlatformUtils . IsWindows ( ) )
63
92
{
64
- _context . Trace . WriteLine ( $ "{ Constants . EnvironmentVariables . GitTerminalPrompts } is 0; terminal prompts have been disabled.") ;
93
+ helperName += ".exe" ;
94
+ }
95
+
96
+ string executableDirectory = Path . GetDirectoryName ( Assembly . GetExecutingAssembly ( ) . Location ) ;
97
+ path = Path . Combine ( executableDirectory , helperName ) ;
98
+ if ( ! Context . FileSystem . FileExists ( path ) )
99
+ {
100
+ Context . Trace . WriteLine ( $ "Did not find helper '{ helperName } ' in '{ executableDirectory } '") ;
65
101
66
- throw new InvalidOperationException ( "Cannot show GitHub credential prompt because terminal prompts have been disabled." ) ;
102
+ // We currently only have a helper on Windows. If we failed to find the helper we should warn the user.
103
+ if ( PlatformUtils . IsWindows ( ) )
104
+ {
105
+ Context . StdError . WriteLine ( $ "warning: missing '{ helperName } ' from installation.") ;
106
+ }
107
+
108
+ return false ;
67
109
}
110
+
111
+ return true ;
68
112
}
69
113
}
70
114
}
0 commit comments