@@ -44,8 +44,8 @@ private async Task<int> Run()
44
44
Console . WriteLine ( "Example OAuth PKCE Application" ) ;
45
45
DropboxCertHelper . InitializeCertPinning ( ) ;
46
46
47
- var uid = await AcquireOAuthTokens ( null , IncludeGrantedScopes . None ) ;
48
- if ( string . IsNullOrEmpty ( uid ) )
47
+ string accessToken = await GetOAuthTokens ( null , IncludeGrantedScopes . None ) ;
48
+ if ( string . IsNullOrEmpty ( accessToken ) )
49
49
{
50
50
return 1 ;
51
51
}
@@ -68,11 +68,8 @@ private async Task<int> Run()
68
68
} ;
69
69
70
70
var client = new DropboxClient ( Settings . Default . RefreshToken , Settings . Default . ApiKey , config ) ;
71
+ await GetCurrentAccount ( client ) ; // This call should succeed since the correct scope has been acquired
71
72
72
- // This call should succeed since the correct scope has been acquired
73
- await GetCurrentAccount ( client ) ;
74
-
75
- Console . WriteLine ( "Oauth PKCE Test Complete!" ) ;
76
73
Console . WriteLine ( "Exit with any key" ) ;
77
74
Console . ReadKey ( ) ;
78
75
}
@@ -90,6 +87,7 @@ private async Task<int> Run()
90
87
return 0 ;
91
88
}
92
89
90
+
93
91
/// <summary>
94
92
/// Handles the redirect from Dropbox server. Because we are using token flow, the local
95
93
/// http server cannot directly receive the URL fragment. We need to return a HTML page with
@@ -135,9 +133,7 @@ private async Task<Uri> HandleJSRedirect(HttpListener http)
135
133
context = await http . GetContextAsync ( ) ;
136
134
}
137
135
138
- var redirectUri = new Uri ( context . Request . QueryString [ "url_with_fragment" ] ) ;
139
-
140
- return redirectUri ;
136
+ return new Uri ( context . Request . QueryString [ "url_with_fragment" ] ) ;
141
137
}
142
138
143
139
/// <summary>
@@ -148,8 +144,8 @@ private async Task<Uri> HandleJSRedirect(HttpListener http)
148
144
/// displayed to authorize the user.
149
145
/// </para>
150
146
/// </summary>
151
- /// <returns>A valid uid if a token was acquired or null.</returns>
152
- private async Task < string > AcquireOAuthTokens ( string [ ] scopeList , IncludeGrantedScopes includeGrantedScopes )
147
+ /// <returns>A valid access token if successful otherwise null.</returns>
148
+ private async Task < string > GetOAuthTokens ( string [ ] scopeList , IncludeGrantedScopes includeGrantedScopes )
153
149
{
154
150
Settings . Default . Upgrade ( ) ;
155
151
Console . Write ( "Reset settings (Y/N) " ) ;
@@ -159,32 +155,34 @@ private async Task<string> AcquireOAuthTokens(string[] scopeList, IncludeGranted
159
155
}
160
156
Console . WriteLine ( ) ;
161
157
162
- string accessToken = Settings . Default . AccessToken ;
163
- string uid = Settings . Default . Uid ;
164
-
165
- if ( string . IsNullOrEmpty ( accessToken ) )
158
+ if ( string . IsNullOrEmpty ( Settings . Default . AccessToken ) )
166
159
{
167
160
string apiKey = GetApiKey ( ) ;
168
161
169
162
using var http = new HttpListener ( ) ;
170
163
try
171
164
{
172
- Console . WriteLine ( "Waiting for credentials." ) ;
173
165
string state = Guid . NewGuid ( ) . ToString ( "N" ) ;
174
166
var OAuthFlow = new PKCEOAuthFlow ( ) ;
175
- var authorizeUri = OAuthFlow . GetAuthorizeUri ( OAuthResponseType . Code , apiKey , RedirectUri . ToString ( ) , state : state , tokenAccessType : TokenAccessType . Offline , scopeList : scopeList , includeGrantedScopes : includeGrantedScopes ) ;
167
+ var authorizeUri = OAuthFlow . GetAuthorizeUri (
168
+ OAuthResponseType . Code , apiKey , RedirectUri . ToString ( ) ,
169
+ state : state , tokenAccessType : TokenAccessType . Offline ,
170
+ scopeList : scopeList , includeGrantedScopes : includeGrantedScopes ) ;
171
+
176
172
http . Prefixes . Add ( LoopbackHost ) ;
177
173
178
174
http . Start ( ) ;
179
175
176
+ // Use StartInfo to ensure default browser launches.
180
177
ProcessStartInfo startInfo = new ProcessStartInfo (
181
- authorizeUri . ToString ( ) ) { UseShellExecute = true } ;
178
+ authorizeUri . ToString ( ) )
179
+ { UseShellExecute = true } ;
182
180
183
181
try
184
182
{
185
183
// open browser for authentication
184
+ Console . WriteLine ( "Waiting for credentials and authorization." ) ;
186
185
Process . Start ( startInfo ) ;
187
- Console . WriteLine ( "Waiting for authentication..." ) ;
188
186
}
189
187
catch ( Exception )
190
188
{
@@ -199,33 +197,24 @@ private async Task<string> AcquireOAuthTokens(string[] scopeList, IncludeGranted
199
197
200
198
http . Stop ( ) ;
201
199
202
- Console . WriteLine ( "Exchanging code for token" ) ;
203
- var tokenResult = await OAuthFlow . ProcessCodeFlowAsync ( redirectUri , apiKey , RedirectUri . ToString ( ) , state ) ;
204
- Console . WriteLine ( "Finished Exchanging Code for Token" ) ;
205
- // Bring console window to the front.
206
- SetForegroundWindow ( GetConsoleWindow ( ) ) ;
207
- accessToken = tokenResult . AccessToken ;
208
- string refreshToken = tokenResult . RefreshToken ;
209
- uid = tokenResult . Uid ;
210
- Console . WriteLine ( "Uid: {0}" , uid ) ;
211
- Console . WriteLine ( "AccessToken: {0}" , accessToken ) ;
212
- if ( tokenResult . RefreshToken != null )
200
+ // Exchanging code for token
201
+ var result = await OAuthFlow . ProcessCodeFlowAsync (
202
+ redirectUri , apiKey , RedirectUri . ToString ( ) , state ) ;
203
+ if ( result . State != state )
213
204
{
214
- Console . WriteLine ( "RefreshToken: {0}" , refreshToken ) ;
215
- Settings . Default . RefreshToken = refreshToken ;
205
+ // NOTE: Rightly or wrongly?, state is not returned or else
206
+ // we would return null here.
207
+ // See issue https://github.com/dropbox/dropbox-sdk-dotnet/issues/248
208
+ Console . WriteLine ( "The state in the response doesn't match the state in the request." ) ;
216
209
}
217
- if ( tokenResult . ExpiresAt != null )
218
- {
219
- Console . WriteLine ( "ExpiresAt: {0}" , tokenResult . ExpiresAt ) ;
220
- }
221
- if ( tokenResult . ScopeList != null )
222
- {
223
- Console . WriteLine ( "Scopes: {0}" , String . Join ( " " , tokenResult . ScopeList ) ) ;
224
- }
225
- Settings . Default . AccessToken = accessToken ;
226
- Settings . Default . Uid = uid ;
227
- Settings . Default . Save ( ) ;
228
- Settings . Default . Reload ( ) ;
210
+ Console . WriteLine ( "OAuth token aquire complete" ) ;
211
+
212
+ // Bring console window to the front.
213
+ SetForegroundWindow ( GetConsoleWindow ( ) ) ;
214
+
215
+ DisplayOAuthResult ( result ) ;
216
+
217
+ UpdateSettings ( result ) ;
229
218
}
230
219
catch ( Exception e )
231
220
{
@@ -234,7 +223,32 @@ private async Task<string> AcquireOAuthTokens(string[] scopeList, IncludeGranted
234
223
}
235
224
}
236
225
237
- return uid ;
226
+ return Settings . Default . AccessToken ;
227
+ }
228
+
229
+ private static void UpdateSettings ( OAuth2Response result )
230
+ {
231
+ // Foreach Settting, save off the value retrieved from the result.
232
+ foreach ( System . Configuration . SettingsProperty item in Settings . Default . Properties )
233
+ {
234
+ if ( typeof ( OAuth2Response ) . GetProperty ( item . Name ) is System . Reflection . PropertyInfo property )
235
+ {
236
+ Settings . Default [ item . Name ] = property . GetValue ( result ) ;
237
+ }
238
+ }
239
+
240
+ Settings . Default . Save ( ) ;
241
+ Settings . Default . Reload ( ) ;
242
+ }
243
+
244
+ private static void DisplayOAuthResult ( OAuth2Response result )
245
+ {
246
+ Console . WriteLine ( "OAuth Result:" ) ;
247
+ Console . WriteLine ( "\t Uid: {0}" , result . Uid ) ;
248
+ Console . WriteLine ( "\t AccessToken: {0}" , result . AccessToken ) ;
249
+ Console . WriteLine ( "\t RefreshToken: {0}" , result . RefreshToken ) ;
250
+ Console . WriteLine ( "\t ExpiresAt: {0}" , result . ExpiresAt ) ;
251
+ Console . WriteLine ( "\t Scopes: {0}" , string . Join ( " " , result . ScopeList ?? new string [ 0 ] ) ) ;
238
252
}
239
253
240
254
/// <summary>
@@ -273,41 +287,33 @@ private static string GetApiKey()
273
287
/// </summary>
274
288
/// <param name="client">The Dropbox client.</param>
275
289
/// <returns>An asynchronous task.</returns>
276
- private async Task GetCurrentAccount ( DropboxClient client )
290
+ static private async Task GetCurrentAccount ( DropboxClient client )
277
291
{
278
- try
292
+ Console . WriteLine ( "Current Account:" ) ;
293
+ var full = await client . Users . GetCurrentAccountAsync ( ) ;
294
+
295
+ Console . WriteLine ( "Account id : {0}" , full . AccountId ) ;
296
+ Console . WriteLine ( "Country : {0}" , full . Country ) ;
297
+ Console . WriteLine ( "Email : {0}" , full . Email ) ;
298
+ Console . WriteLine ( "Is paired : {0}" , full . IsPaired ? "Yes" : "No" ) ;
299
+ Console . WriteLine ( "Locale : {0}" , full . Locale ) ;
300
+ Console . WriteLine ( "Name" ) ;
301
+ Console . WriteLine ( " Display : {0}" , full . Name . DisplayName ) ;
302
+ Console . WriteLine ( " Familiar : {0}" , full . Name . FamiliarName ) ;
303
+ Console . WriteLine ( " Given : {0}" , full . Name . GivenName ) ;
304
+ Console . WriteLine ( " Surname : {0}" , full . Name . Surname ) ;
305
+ Console . WriteLine ( "Referral link : {0}" , full . ReferralLink ) ;
306
+
307
+ if ( full . Team != null )
279
308
{
280
- Console . WriteLine ( "Current Account:" ) ;
281
- var full = await client . Users . GetCurrentAccountAsync ( ) ;
282
-
283
- Console . WriteLine ( "Account id : {0}" , full . AccountId ) ;
284
- Console . WriteLine ( "Country : {0}" , full . Country ) ;
285
- Console . WriteLine ( "Email : {0}" , full . Email ) ;
286
- Console . WriteLine ( "Is paired : {0}" , full . IsPaired ? "Yes" : "No" ) ;
287
- Console . WriteLine ( "Locale : {0}" , full . Locale ) ;
288
- Console . WriteLine ( "Name" ) ;
289
- Console . WriteLine ( " Display : {0}" , full . Name . DisplayName ) ;
290
- Console . WriteLine ( " Familiar : {0}" , full . Name . FamiliarName ) ;
291
- Console . WriteLine ( " Given : {0}" , full . Name . GivenName ) ;
292
- Console . WriteLine ( " Surname : {0}" , full . Name . Surname ) ;
293
- Console . WriteLine ( "Referral link : {0}" , full . ReferralLink ) ;
294
-
295
- if ( full . Team != null )
296
- {
297
- Console . WriteLine ( "Team" ) ;
298
- Console . WriteLine ( " Id : {0}" , full . Team . Id ) ;
299
- Console . WriteLine ( " Name : {0}" , full . Team . Name ) ;
300
- }
301
- else
302
- {
303
- Console . WriteLine ( "Team - None" ) ;
304
- }
309
+ Console . WriteLine ( "Team" ) ;
310
+ Console . WriteLine ( " Id : {0}" , full . Team . Id ) ;
311
+ Console . WriteLine ( " Name : {0}" , full . Team . Name ) ;
305
312
}
306
- catch ( Exception e )
313
+ else
307
314
{
308
- throw e ;
315
+ Console . WriteLine ( "Team - None" ) ;
309
316
}
310
-
311
317
}
312
318
}
313
319
}
0 commit comments