@@ -27,22 +27,30 @@ class LoginManager : ILoginManager
2727 private readonly string clientSecret ;
2828 private readonly string authorizationNote ;
2929 private readonly string fingerprint ;
30+ private readonly IProcessManager processManager ;
31+ private readonly ITaskManager taskManager ;
32+ private readonly NPath loginTool ;
3033
3134 /// <summary>
3235 /// Initializes a new instance of the <see cref="LoginManager"/> class.
3336 /// </summary>
34- /// <param name="loginCache">The cache in which to store login details.</param>
35- /// <param name="twoFactorChallengeHandler">The handler for 2FA challenges.</param>
37+ /// <param name="keychain"></param>
3638 /// <param name="clientId">The application's client API ID.</param>
3739 /// <param name="clientSecret">The application's client API secret.</param>
3840 /// <param name="authorizationNote">An note to store with the authorization.</param>
3941 /// <param name="fingerprint">The machine fingerprint.</param>
42+ /// <param name="processManager"></param>
43+ /// <param name="taskManager"></param>
44+ /// <param name="loginTool"></param>
45+ /// <param name="loginCache">The cache in which to store login details.</param>
46+ /// <param name="twoFactorChallengeHandler">The handler for 2FA challenges.</param>
4047 public LoginManager (
4148 IKeychain keychain ,
4249 string clientId ,
4350 string clientSecret ,
4451 string authorizationNote = null ,
45- string fingerprint = null )
52+ string fingerprint = null ,
53+ IProcessManager processManager = null , ITaskManager taskManager = null , NPath loginTool = null )
4654 {
4755 Guard . ArgumentNotNull ( keychain , nameof ( keychain ) ) ;
4856 Guard . ArgumentNotNullOrWhiteSpace ( clientId , nameof ( clientId ) ) ;
@@ -53,6 +61,9 @@ public LoginManager(
5361 this . clientSecret = clientSecret ;
5462 this . authorizationNote = authorizationNote ;
5563 this . fingerprint = fingerprint ;
64+ this . processManager = processManager ;
65+ this . taskManager = taskManager ;
66+ this . loginTool = loginTool ;
5667 }
5768
5869 /// <inheritdoc/>
@@ -83,9 +94,8 @@ public async Task<LoginResultData> Login(
8394
8495 try
8596 {
86- logger . Info ( "Login Username:{0}" , username ) ;
87-
88- auth = await CreateAndDeleteExistingApplicationAuthorization ( client , newAuth , null ) ;
97+ //auth = await CreateAndDeleteExistingApplicationAuthorization(client, newAuth, null);
98+ auth = await TryLogin ( client , host , username , password ) ;
8999 EnsureNonNullAuthorization ( auth ) ;
90100 }
91101 catch ( TwoFactorAuthorizationException e )
@@ -148,15 +158,17 @@ public async Task<LoginResultData> ContinueLogin(LoginResultData loginResultData
148158 var client = loginResultData . Client ;
149159 var newAuth = loginResultData . NewAuth ;
150160 var host = loginResultData . Host ;
151-
161+ var k = keychain . Connect ( host ) ;
162+ var username = k . Credential . Username ;
163+ var password = k . Credential . Token ;
152164 try
153165 {
154166 logger . Trace ( "2FA Continue" ) ;
155-
156- var auth = await CreateAndDeleteExistingApplicationAuthorization (
157- client ,
158- newAuth ,
159- twofacode ) ;
167+ var auth = await TryContinueLogin ( client , host , username , password , twofacode ) ;
168+ // var auth = await CreateAndDeleteExistingApplicationAuthorization(
169+ // client,
170+ // newAuth,
171+ // twofacode);
160172 EnsureNonNullAuthorization ( auth ) ;
161173
162174 keychain . SetToken ( host , auth . Token ) ;
@@ -207,6 +219,7 @@ private async Task<ApplicationAuthorization> CreateAndDeleteExistingApplicationA
207219 {
208220 if ( twoFactorAuthenticationCode == null )
209221 {
222+
210223 result = await client . Authorization . GetOrCreateApplicationAuthentication (
211224 clientId ,
212225 clientSecret ,
@@ -237,6 +250,98 @@ private async Task<ApplicationAuthorization> CreateAndDeleteExistingApplicationA
237250 return result ;
238251 }
239252
253+ private async Task < ApplicationAuthorization > TryLogin (
254+ IGitHubClient client ,
255+ UriString host ,
256+ string username ,
257+ string password
258+ )
259+ {
260+ logger . Info ( "Login Username:{0}" , username ) ;
261+
262+ ApplicationAuthorization auth = null ;
263+ var loginTask = new SimpleListProcessTask ( taskManager . Token , loginTool , $ "login --host={ host } ") ;
264+ loginTask . Configure ( processManager , true ) ;
265+ loginTask . OnStartProcess += proc =>
266+ {
267+ proc . StandardInput . WriteLine ( username ) ;
268+ proc . StandardInput . WriteLine ( password ) ;
269+ } ;
270+ var ret = await loginTask . StartAwait ( ) ;
271+ if ( ret . Count == 0 )
272+ {
273+ throw new Exception ( "Authentication failed" ) ;
274+ }
275+ // success
276+ else if ( ret . Count == 1 )
277+ {
278+ auth = new ApplicationAuthorization ( ret [ 0 ] ) ;
279+ }
280+ else
281+ {
282+ if ( ret [ 0 ] == "2fa" )
283+ {
284+ keychain . SetToken ( host , ret [ 1 ] ) ;
285+ await keychain . Save ( host ) ;
286+ throw new TwoFactorRequiredException ( TwoFactorType . Unknown ) ;
287+ }
288+ else if ( ret [ 0 ] == "locked" )
289+ {
290+ throw new LoginAttemptsExceededException ( null , null ) ;
291+ }
292+ else
293+ throw new Exception ( "Authentication failed" ) ;
294+ }
295+ return auth ;
296+ }
297+
298+ private async Task < ApplicationAuthorization > TryContinueLogin (
299+ IGitHubClient client ,
300+ UriString host ,
301+ string username ,
302+ string password ,
303+ string code
304+ )
305+ {
306+ logger . Info ( "Continue Username:{0}" , username ) ;
307+
308+ ApplicationAuthorization auth = null ;
309+ var loginTask = new SimpleListProcessTask ( taskManager . Token , loginTool , $ "login --host={ host } --2fa") ;
310+ loginTask . Configure ( processManager , true ) ;
311+ loginTask . OnStartProcess += proc =>
312+ {
313+ proc . StandardInput . WriteLine ( username ) ;
314+ proc . StandardInput . WriteLine ( password ) ;
315+ proc . StandardInput . WriteLine ( code ) ;
316+ } ;
317+ var ret = await loginTask . StartAwait ( ) ;
318+ if ( ret . Count == 0 )
319+ {
320+ throw new Exception ( "Authentication failed" ) ;
321+ }
322+ // success
323+ else if ( ret . Count == 1 )
324+ {
325+ auth = new ApplicationAuthorization ( ret [ 0 ] ) ;
326+ }
327+ else
328+ {
329+ if ( ret [ 0 ] == "2fa" )
330+ {
331+ keychain . SetToken ( host , ret [ 1 ] ) ;
332+ await keychain . Save ( host ) ;
333+ throw new TwoFactorRequiredException ( TwoFactorType . Unknown ) ;
334+ }
335+ else if ( ret [ 0 ] == "locked" )
336+ {
337+ throw new LoginAttemptsExceededException ( null , null ) ;
338+ }
339+ else
340+ throw new Exception ( "Authentication failed" ) ;
341+ }
342+ return auth ;
343+ }
344+
240345 ApplicationAuthorization EnsureNonNullAuthorization ( ApplicationAuthorization auth )
241346 {
242347 // If a mock IGitHubClient is not set up correctly, it can return null from
0 commit comments