@@ -57,35 +57,77 @@ public async Task<OAuthAuthenticationModes> GetAuthenticationModeAsync(
57
57
return modes ;
58
58
}
59
59
60
- ThrowIfTerminalPromptsDisabled ( ) ;
61
-
62
- switch ( modes )
60
+ if ( Context . Settings . IsGuiPromptsEnabled && Context . SessionManager . IsDesktopSession &&
61
+ TryFindHelperCommand ( out string command , out string args ) )
62
+ {
63
+ var promptArgs = new StringBuilder ( args ) ;
64
+ promptArgs . Append ( "oauth" ) ;
65
+
66
+ if ( ! string . IsNullOrWhiteSpace ( resource ) )
67
+ {
68
+ promptArgs . AppendFormat ( " --resource {0}" , QuoteCmdArg ( resource ) ) ;
69
+ }
70
+
71
+ if ( ( modes & OAuthAuthenticationModes . Browser ) != 0 )
72
+ {
73
+ promptArgs . Append ( " --browser" ) ;
74
+ }
75
+
76
+ if ( ( modes & OAuthAuthenticationModes . DeviceCode ) != 0 )
77
+ {
78
+ promptArgs . Append ( " --device-code" ) ;
79
+ }
80
+
81
+ IDictionary < string , string > resultDict = await InvokeHelperAsync ( command , promptArgs . ToString ( ) ) ;
82
+
83
+ if ( ! resultDict . TryGetValue ( "mode" , out string responseMode ) )
84
+ {
85
+ throw new Exception ( "Missing 'mode' in response" ) ;
86
+ }
87
+
88
+ switch ( responseMode . ToLowerInvariant ( ) )
89
+ {
90
+ case "browser" :
91
+ return OAuthAuthenticationModes . Browser ;
92
+
93
+ case "devicecode" :
94
+ return OAuthAuthenticationModes . DeviceCode ;
95
+
96
+ default :
97
+ throw new Exception ( $ "Unknown mode value in response '{ responseMode } '") ;
98
+ }
99
+ }
100
+ else
63
101
{
64
- case OAuthAuthenticationModes . Browser :
65
- return OAuthAuthenticationModes . Browser ;
102
+ ThrowIfTerminalPromptsDisabled ( ) ;
66
103
67
- case OAuthAuthenticationModes . DeviceCode :
68
- return OAuthAuthenticationModes . DeviceCode ;
104
+ switch ( modes )
105
+ {
106
+ case OAuthAuthenticationModes . Browser :
107
+ return OAuthAuthenticationModes . Browser ;
69
108
70
- default :
71
- var menuTitle = $ "Select an authentication method for '{ resource } '";
72
- var menu = new TerminalMenu ( Context . Terminal , menuTitle ) ;
109
+ case OAuthAuthenticationModes . DeviceCode :
110
+ return OAuthAuthenticationModes . DeviceCode ;
73
111
74
- TerminalMenuItem browserItem = null ;
75
- TerminalMenuItem deviceItem = null ;
112
+ default :
113
+ var menuTitle = $ "Select an authentication method for '{ resource } '";
114
+ var menu = new TerminalMenu ( Context . Terminal , menuTitle ) ;
76
115
77
- if ( ( modes & OAuthAuthenticationModes . Browser ) != 0 ) browserItem = menu . Add ( "Web browser" ) ;
78
- if ( ( modes & OAuthAuthenticationModes . DeviceCode ) != 0 ) deviceItem = menu . Add ( "Device code" ) ;
116
+ TerminalMenuItem browserItem = null ;
117
+ TerminalMenuItem deviceItem = null ;
79
118
80
- // Default to the 'first' choice in the menu
81
- TerminalMenuItem choice = menu . Show ( 0 ) ;
119
+ if ( ( modes & OAuthAuthenticationModes . Browser ) != 0 ) browserItem = menu . Add ( "Web browser" ) ;
120
+ if ( ( modes & OAuthAuthenticationModes . DeviceCode ) != 0 ) deviceItem = menu . Add ( "Device code" ) ;
82
121
83
- if ( choice == browserItem ) goto case OAuthAuthenticationModes . Browser ;
84
- if ( choice == deviceItem ) goto case OAuthAuthenticationModes . DeviceCode ;
122
+ // Default to the 'first' choice in the menu
123
+ TerminalMenuItem choice = menu . Show ( 0 ) ;
85
124
86
- throw new Exception ( ) ;
125
+ if ( choice == browserItem ) goto case OAuthAuthenticationModes . Browser ;
126
+ if ( choice == deviceItem ) goto case OAuthAuthenticationModes . DeviceCode ;
127
+
128
+ throw new Exception ( ) ;
129
+ }
87
130
}
88
-
89
131
}
90
132
91
133
public async Task < OAuth2TokenResult > GetTokenByBrowserAsync ( OAuth2Client client , string [ ] scopes )
@@ -110,14 +152,68 @@ public async Task<OAuth2TokenResult> GetTokenByDeviceCodeAsync(OAuth2Client clie
110
152
111
153
OAuth2DeviceCodeResult dcr = await client . GetDeviceCodeAsync ( scopes , CancellationToken . None ) ;
112
154
113
- ThrowIfTerminalPromptsDisabled ( ) ;
155
+ // If we have a desktop session show the device code in a dialog
156
+ if ( Context . Settings . IsGuiPromptsEnabled && Context . SessionManager . IsDesktopSession &&
157
+ TryFindHelperCommand ( out string command , out string args ) )
158
+ {
159
+ var promptArgs = new StringBuilder ( args ) ;
160
+ promptArgs . Append ( "device" ) ;
161
+ promptArgs . AppendFormat ( " --code {0} " , QuoteCmdArg ( dcr . UserCode ) ) ;
162
+ promptArgs . AppendFormat ( " --url {0}" , QuoteCmdArg ( dcr . VerificationUri . ToString ( ) ) ) ;
163
+
164
+ var promptCts = new CancellationTokenSource ( ) ;
165
+ var tokenCts = new CancellationTokenSource ( ) ;
166
+
167
+ // Show the dialog with the device code but don't await its closure
168
+ Task promptTask = InvokeHelperAsync ( command , promptArgs . ToString ( ) , null , promptCts . Token ) ;
169
+
170
+ // Start the request for an OAuth token but don't wait
171
+ Task < OAuth2TokenResult > tokenTask = client . GetTokenByDeviceCodeAsync ( dcr , tokenCts . Token ) ;
172
+
173
+ Task t = await Task . WhenAny ( promptTask , tokenTask ) ;
174
+
175
+ // If the dialog was closed the user wishes to cancel the request
176
+ if ( t == promptTask )
177
+ {
178
+ tokenCts . Cancel ( ) ;
179
+ }
180
+
181
+ OAuth2TokenResult tokenResult ;
182
+ try
183
+ {
184
+ tokenResult = await tokenTask ;
185
+ }
186
+ catch ( OperationCanceledException )
187
+ {
188
+ throw new Exception ( "User canceled device code authentication" ) ;
189
+ }
190
+
191
+ // Close the dialog
192
+ promptCts . Cancel ( ) ;
193
+
194
+ return tokenResult ;
195
+ }
196
+ else
197
+ {
198
+ ThrowIfTerminalPromptsDisabled ( ) ;
114
199
115
- string deviceMessage = $ "To complete authentication please visit { dcr . VerificationUri } and enter the following code:" +
116
- Environment . NewLine +
117
- dcr . UserCode ;
118
- Context . Terminal . WriteLine ( deviceMessage ) ;
200
+ string deviceMessage = $ "To complete authentication please visit { dcr . VerificationUri } and enter the following code:" +
201
+ Environment . NewLine +
202
+ dcr . UserCode ;
203
+ Context . Terminal . WriteLine ( deviceMessage ) ;
119
204
120
- return await client . GetTokenByDeviceCodeAsync ( dcr , CancellationToken . None ) ;
205
+ return await client . GetTokenByDeviceCodeAsync ( dcr , CancellationToken . None ) ;
206
+ }
207
+ }
208
+
209
+ private bool TryFindHelperCommand ( out string command , out string args )
210
+ {
211
+ return TryFindHelperCommand (
212
+ Constants . EnvironmentVariables . GcmUiHelper ,
213
+ Constants . GitConfiguration . Credential . UiHelper ,
214
+ Constants . DefaultUiHelper ,
215
+ out command ,
216
+ out args ) ;
121
217
}
122
218
}
123
219
}
0 commit comments