From e3ed36b00942990c9ad1b992c306d9338964df92 Mon Sep 17 00:00:00 2001 From: Mark Michaelis Date: Mon, 21 Jun 2021 13:54:31 -0700 Subject: [PATCH] Refactored to SimpleTest and OAuthPKCE program file differences were minimal and germane to the different approaches. --- .../Examples/OAuthPKCE/Program.cs | 156 +++++++------- .../Examples/SimpleTest/Program.cs | 193 ++++++++++-------- 2 files changed, 187 insertions(+), 162 deletions(-) diff --git a/dropbox-sdk-dotnet/Examples/OAuthPKCE/Program.cs b/dropbox-sdk-dotnet/Examples/OAuthPKCE/Program.cs index 6805137b98..84ca08c78e 100644 --- a/dropbox-sdk-dotnet/Examples/OAuthPKCE/Program.cs +++ b/dropbox-sdk-dotnet/Examples/OAuthPKCE/Program.cs @@ -44,8 +44,8 @@ private async Task Run() Console.WriteLine("Example OAuth PKCE Application"); DropboxCertHelper.InitializeCertPinning(); - var uid = await AcquireOAuthTokens(null, IncludeGrantedScopes.None); - if (string.IsNullOrEmpty(uid)) + string accessToken = await GetOAuthTokens(null, IncludeGrantedScopes.None); + if (string.IsNullOrEmpty(accessToken)) { return 1; } @@ -68,11 +68,8 @@ private async Task Run() }; var client = new DropboxClient(Settings.Default.RefreshToken, Settings.Default.ApiKey, config); + await GetCurrentAccount(client); // This call should succeed since the correct scope has been acquired - // This call should succeed since the correct scope has been acquired - await GetCurrentAccount(client); - - Console.WriteLine("Oauth PKCE Test Complete!"); Console.WriteLine("Exit with any key"); Console.ReadKey(); } @@ -90,6 +87,7 @@ private async Task Run() return 0; } + /// /// Handles the redirect from Dropbox server. Because we are using token flow, the local /// http server cannot directly receive the URL fragment. We need to return a HTML page with @@ -135,9 +133,7 @@ private async Task HandleJSRedirect(HttpListener http) context = await http.GetContextAsync(); } - var redirectUri = new Uri(context.Request.QueryString["url_with_fragment"]); - - return redirectUri; + return new Uri(context.Request.QueryString["url_with_fragment"]); } /// @@ -148,8 +144,8 @@ private async Task HandleJSRedirect(HttpListener http) /// displayed to authorize the user. /// /// - /// A valid uid if a token was acquired or null. - private async Task AcquireOAuthTokens(string[] scopeList, IncludeGrantedScopes includeGrantedScopes) + /// A valid access token if successful otherwise null. + private async Task GetOAuthTokens(string[] scopeList, IncludeGrantedScopes includeGrantedScopes) { Settings.Default.Upgrade(); Console.Write("Reset settings (Y/N) "); @@ -159,32 +155,34 @@ private async Task AcquireOAuthTokens(string[] scopeList, IncludeGranted } Console.WriteLine(); - string accessToken = Settings.Default.AccessToken; - string uid = Settings.Default.Uid; - - if (string.IsNullOrEmpty(accessToken)) + if (string.IsNullOrEmpty(Settings.Default.AccessToken)) { string apiKey = GetApiKey(); using var http = new HttpListener(); try { - Console.WriteLine("Waiting for credentials."); string state = Guid.NewGuid().ToString("N"); var OAuthFlow = new PKCEOAuthFlow(); - var authorizeUri = OAuthFlow.GetAuthorizeUri(OAuthResponseType.Code, apiKey, RedirectUri.ToString(), state: state, tokenAccessType: TokenAccessType.Offline, scopeList: scopeList, includeGrantedScopes: includeGrantedScopes); + var authorizeUri = OAuthFlow.GetAuthorizeUri( + OAuthResponseType.Code, apiKey, RedirectUri.ToString(), + state: state, tokenAccessType: TokenAccessType.Offline, + scopeList: scopeList, includeGrantedScopes: includeGrantedScopes); + http.Prefixes.Add(LoopbackHost); http.Start(); + // Use StartInfo to ensure default browser launches. ProcessStartInfo startInfo = new ProcessStartInfo( - authorizeUri.ToString()){ UseShellExecute = true }; + authorizeUri.ToString()) + { UseShellExecute = true }; try { // open browser for authentication + Console.WriteLine("Waiting for credentials and authorization."); Process.Start(startInfo); - Console.WriteLine("Waiting for authentication..."); } catch (Exception) { @@ -199,33 +197,24 @@ private async Task AcquireOAuthTokens(string[] scopeList, IncludeGranted http.Stop(); - Console.WriteLine("Exchanging code for token"); - var tokenResult = await OAuthFlow.ProcessCodeFlowAsync(redirectUri, apiKey, RedirectUri.ToString(), state); - Console.WriteLine("Finished Exchanging Code for Token"); - // Bring console window to the front. - SetForegroundWindow(GetConsoleWindow()); - accessToken = tokenResult.AccessToken; - string refreshToken = tokenResult.RefreshToken; - uid = tokenResult.Uid; - Console.WriteLine("Uid: {0}", uid); - Console.WriteLine("AccessToken: {0}", accessToken); - if (tokenResult.RefreshToken != null) + // Exchanging code for token + var result = await OAuthFlow.ProcessCodeFlowAsync( + redirectUri, apiKey, RedirectUri.ToString(), state); + if (result.State != state) { - Console.WriteLine("RefreshToken: {0}", refreshToken); - Settings.Default.RefreshToken = refreshToken; + // NOTE: Rightly or wrongly?, state is not returned or else + // we would return null here. + // See issue https://github.com/dropbox/dropbox-sdk-dotnet/issues/248 + Console.WriteLine("The state in the response doesn't match the state in the request."); } - if (tokenResult.ExpiresAt != null) - { - Console.WriteLine("ExpiresAt: {0}", tokenResult.ExpiresAt); - } - if (tokenResult.ScopeList != null) - { - Console.WriteLine("Scopes: {0}", String.Join(" ", tokenResult.ScopeList)); - } - Settings.Default.AccessToken = accessToken; - Settings.Default.Uid = uid; - Settings.Default.Save(); - Settings.Default.Reload(); + Console.WriteLine("OAuth token aquire complete"); + + // Bring console window to the front. + SetForegroundWindow(GetConsoleWindow()); + + DisplayOAuthResult(result); + + UpdateSettings(result); } catch (Exception e) { @@ -234,7 +223,32 @@ private async Task AcquireOAuthTokens(string[] scopeList, IncludeGranted } } - return uid; + return Settings.Default.AccessToken; + } + + private static void UpdateSettings(OAuth2Response result) + { + // Foreach Settting, save off the value retrieved from the result. + foreach (System.Configuration.SettingsProperty item in Settings.Default.Properties) + { + if (typeof(OAuth2Response).GetProperty(item.Name) is System.Reflection.PropertyInfo property) + { + Settings.Default[item.Name] = property.GetValue(result); + } + } + + Settings.Default.Save(); + Settings.Default.Reload(); + } + + private static void DisplayOAuthResult(OAuth2Response result) + { + Console.WriteLine("OAuth Result:"); + Console.WriteLine("\tUid: {0}", result.Uid); + Console.WriteLine("\tAccessToken: {0}", result.AccessToken); + Console.WriteLine("\tRefreshToken: {0}", result.RefreshToken); + Console.WriteLine("\tExpiresAt: {0}", result.ExpiresAt); + Console.WriteLine("\tScopes: {0}", string.Join(" ", result.ScopeList?? new string[0])); } /// @@ -273,41 +287,33 @@ private static string GetApiKey() /// /// The Dropbox client. /// An asynchronous task. - private async Task GetCurrentAccount(DropboxClient client) + static private async Task GetCurrentAccount(DropboxClient client) { - try + Console.WriteLine("Current Account:"); + var full = await client.Users.GetCurrentAccountAsync(); + + Console.WriteLine("Account id : {0}", full.AccountId); + Console.WriteLine("Country : {0}", full.Country); + Console.WriteLine("Email : {0}", full.Email); + Console.WriteLine("Is paired : {0}", full.IsPaired ? "Yes" : "No"); + Console.WriteLine("Locale : {0}", full.Locale); + Console.WriteLine("Name"); + Console.WriteLine(" Display : {0}", full.Name.DisplayName); + Console.WriteLine(" Familiar : {0}", full.Name.FamiliarName); + Console.WriteLine(" Given : {0}", full.Name.GivenName); + Console.WriteLine(" Surname : {0}", full.Name.Surname); + Console.WriteLine("Referral link : {0}", full.ReferralLink); + + if (full.Team != null) { - Console.WriteLine("Current Account:"); - var full = await client.Users.GetCurrentAccountAsync(); - - Console.WriteLine("Account id : {0}", full.AccountId); - Console.WriteLine("Country : {0}", full.Country); - Console.WriteLine("Email : {0}", full.Email); - Console.WriteLine("Is paired : {0}", full.IsPaired ? "Yes" : "No"); - Console.WriteLine("Locale : {0}", full.Locale); - Console.WriteLine("Name"); - Console.WriteLine(" Display : {0}", full.Name.DisplayName); - Console.WriteLine(" Familiar : {0}", full.Name.FamiliarName); - Console.WriteLine(" Given : {0}", full.Name.GivenName); - Console.WriteLine(" Surname : {0}", full.Name.Surname); - Console.WriteLine("Referral link : {0}", full.ReferralLink); - - if (full.Team != null) - { - Console.WriteLine("Team"); - Console.WriteLine(" Id : {0}", full.Team.Id); - Console.WriteLine(" Name : {0}", full.Team.Name); - } - else - { - Console.WriteLine("Team - None"); - } + Console.WriteLine("Team"); + Console.WriteLine(" Id : {0}", full.Team.Id); + Console.WriteLine(" Name : {0}", full.Team.Name); } - catch (Exception e) + else { - throw e; + Console.WriteLine("Team - None"); } - } } } diff --git a/dropbox-sdk-dotnet/Examples/SimpleTest/Program.cs b/dropbox-sdk-dotnet/Examples/SimpleTest/Program.cs index c04e5c6158..ea72192400 100644 --- a/dropbox-sdk-dotnet/Examples/SimpleTest/Program.cs +++ b/dropbox-sdk-dotnet/Examples/SimpleTest/Program.cs @@ -6,6 +6,7 @@ namespace SimpleTest using System.Linq; using System.Net; using System.Net.Http; + using System.Reflection; using System.Runtime.InteropServices; using System.Threading.Tasks; @@ -46,7 +47,7 @@ private async Task Run() Console.WriteLine(nameof(SimpleTest)); DropboxCertHelper.InitializeCertPinning(); - var accessToken = await this.GetAccessToken(); + string accessToken = await GetOAuthTokens(); if (string.IsNullOrEmpty(accessToken)) { return 1; @@ -79,6 +80,9 @@ private async Task Run() var client = new DropboxTeamClient(accessToken, userAgent: "SimpleTeamTestApp", httpClient: httpClient); await RunTeamTests(client); */ + + Console.WriteLine("Exit with any key"); + Console.ReadKey(); } catch (HttpException e) { @@ -94,59 +98,6 @@ private async Task Run() return 0; } - /// - /// Run tests for user-level operations. - /// - /// The Dropbox client. - /// An asynchronous task. - private async Task RunUserTests(DropboxClient client) - { - - await GetCurrentAccount(client); - - var path = "/DotNetApi/Help"; - await DeleteFolder(client, path); // Removes items left older from the previous test run. - - var folder = await CreateFolder(client, path); - - var pathInTeamSpace = "/Test"; - await ListFolderInTeamSpace(client, pathInTeamSpace); - - await Upload(client, path, "Test.txt", "This is a text file"); - - await ChunkUpload(client, path, "Binary"); - - ListFolderResult list = await ListFolder(client, path); - - Metadata firstFile = list.Entries.FirstOrDefault(i => i.IsFile); - if (firstFile != null) - { - await Download(client, path, firstFile.AsFile); - } - - await DeleteFolder(client, path); - } - - /// - /// Run tests for team-level operations. - /// - /// The Dropbox client. - /// An asynchronous task. - private async Task RunTeamTests(DropboxTeamClient client) - { - var members = await client.Team.MembersListAsync(); - - var member = members.Members.FirstOrDefault(); - - if (member != null) - { - // A team client can perform action on a team member's behalf. To do this, - // just pass in team member id in to AsMember function which returns a user client. - // This client will operates on this team member's Dropbox. - var userClient = client.AsMember(member.Profile.TeamMemberId); - await RunUserTests(userClient); - } - } /// /// Handles the redirect from Dropbox server. Because we are using token flow, the local @@ -183,7 +134,7 @@ private async Task HandleOAuth2Redirect(HttpListener http) /// /// The http listener. /// The - private async Task HandleJSRedirect(HttpListener http) + private async Task HandleJSRedirect(HttpListener http) { var context = await http.GetContextAsync(); @@ -193,23 +144,19 @@ private async Task HandleJSRedirect(HttpListener http) context = await http.GetContextAsync(); } - var redirectUri = new Uri(context.Request.QueryString["url_with_fragment"]); - - var result = DropboxOAuth2Helper.ParseTokenFragment(redirectUri); - - return result; + return new Uri(context.Request.QueryString["url_with_fragment"]); } /// - /// Gets the dropbox access token. + /// Acquires a dropbox OAuth tokens and saves them to the default settings for the app. /// - /// This fetches the access token from the applications settings, if it is not found there + /// This fetches the OAuth tokens from the applications settings, if it is not found there /// (or if the user chooses to reset the settings) then the UI in is /// displayed to authorize the user. /// /// - /// A valid access token or null. - private async Task GetAccessToken() + /// A valid access token if successful otherwise null. + private async Task GetOAuthTokens() { Settings.Default.Upgrade(); Console.Write("Reset settings (Y/N) "); @@ -219,32 +166,29 @@ private async Task GetAccessToken() } Console.WriteLine(); - string accessToken = Settings.Default.AccessToken; - - if (string.IsNullOrEmpty(accessToken)) + if (string.IsNullOrEmpty(Settings.Default.AccessToken)) { string apiKey = GetApiKey(); using var http = new HttpListener(); try { - Console.WriteLine("Waiting for credentials."); - var state = Guid.NewGuid().ToString("N"); + string state = Guid.NewGuid().ToString("N"); var authorizeUri = DropboxOAuth2Helper.GetAuthorizeUri( OAuthResponseType.Token, apiKey, RedirectUri, state: state); - + http.Prefixes.Add(LoopbackHost); http.Start(); // Use StartInfo to ensure default browser launches. - ProcessStartInfo startInfo = - new ProcessStartInfo(authorizeUri.ToString()) { UseShellExecute = true }; + ProcessStartInfo startInfo = new ProcessStartInfo( + authorizeUri.ToString()) { UseShellExecute = true }; try { // open browser for authentication + Console.WriteLine("Waiting for credentials and authorization."); Process.Start(startInfo); - Console.WriteLine("Waiting for authentication..."); } catch (Exception) { @@ -255,29 +199,23 @@ private async Task GetAccessToken() await HandleOAuth2Redirect(http); // Handle redirect from JS and process OAuth response. - var result = await HandleJSRedirect(http); - + Uri redirectUri = await HandleJSRedirect(http); http.Stop(); + OAuth2Response result = DropboxOAuth2Helper.ParseTokenFragment(redirectUri); if (result.State != state) { // The state in the response doesn't match the state in the request. return null; } - - Console.WriteLine("and back..."); + Console.WriteLine("OAuth token aquire complete"); // Bring console window to the front. SetForegroundWindow(GetConsoleWindow()); - accessToken = result.AccessToken; - var uid = result.Uid; - Console.WriteLine("Uid: {0}", uid); + DisplayOAuthResult(result); - Settings.Default.AccessToken = accessToken; - Settings.Default.Uid = uid; - Settings.Default.Save(); - Settings.Default.Reload(); + UpdateSettings(result); } catch (Exception e) { @@ -286,7 +224,32 @@ private async Task GetAccessToken() } } - return accessToken; + return Settings.Default.AccessToken; + } + + private static void UpdateSettings(OAuth2Response result) + { + // Foreach Settting, save off the value retrieved from the result. + foreach (System.Configuration.SettingsProperty item in Settings.Default.Properties) + { + if (typeof(OAuth2Response).GetProperty(item.Name) is PropertyInfo property) + { + Settings.Default[item.Name] = property.GetValue(result); + } + } + + Settings.Default.Save(); + Settings.Default.Reload(); + } + + private static void DisplayOAuthResult(OAuth2Response result) + { + Console.WriteLine("OAuth Result:"); + Console.WriteLine("\tUid: {0}", result.Uid); + Console.WriteLine("\tAccessToken: {0}", result.AccessToken); + Console.WriteLine("\tRefreshToken: {0}", result.RefreshToken); + Console.WriteLine("\tExpiresAt: {0}", result.ExpiresAt); + Console.WriteLine("\tScopes: {0}", string.Join(" ", result.ScopeList?? new string[0])); } /// @@ -296,7 +259,7 @@ private async Task GetAccessToken() private static string GetApiKey() { string apiKey = Settings.Default.ApiKey; - + while (string.IsNullOrWhiteSpace(apiKey)) { Console.WriteLine("Create a Dropbox App at https://www.dropbox.com/developers/apps."); @@ -316,6 +279,7 @@ private static string GetApiKey() return string.IsNullOrWhiteSpace(apiKey) ? null : apiKey; } + /// /// Gets information about the currently authorized account. /// @@ -324,8 +288,9 @@ private static string GetApiKey() /// /// The Dropbox client. /// An asynchronous task. - private async Task GetCurrentAccount(DropboxClient client) + static private async Task GetCurrentAccount(DropboxClient client) { + Console.WriteLine("Current Account:"); var full = await client.Users.GetCurrentAccountAsync(); Console.WriteLine("Account id : {0}", full.AccountId); @@ -352,6 +317,60 @@ private async Task GetCurrentAccount(DropboxClient client) } } + /// + /// Run tests for user-level operations. + /// + /// The Dropbox client. + /// An asynchronous task. + private async Task RunUserTests(DropboxClient client) + { + + await GetCurrentAccount(client); + + var path = "/DotNetApi/Help"; + await DeleteFolder(client, path); // Removes items left older from the previous test run. + + var folder = await CreateFolder(client, path); + + var pathInTeamSpace = "/Test"; + await ListFolderInTeamSpace(client, pathInTeamSpace); + + await Upload(client, path, "Test.txt", "This is a text file"); + + await ChunkUpload(client, path, "Binary"); + + ListFolderResult list = await ListFolder(client, path); + + Metadata firstFile = list.Entries.FirstOrDefault(i => i.IsFile); + if (firstFile != null) + { + await Download(client, path, firstFile.AsFile); + } + + await DeleteFolder(client, path); + } + + /// + /// Run tests for team-level operations. + /// + /// The Dropbox client. + /// An asynchronous task. + private async Task RunTeamTests(DropboxTeamClient client) + { + var members = await client.Team.MembersListAsync(); + + var member = members.Members.FirstOrDefault(); + + if (member != null) + { + // A team client can perform action on a team member's behalf. To do this, + // just pass in team member id in to AsMember function which returns a user client. + // This client will operates on this team member's Dropbox. + var userClient = client.AsMember(member.Profile.TeamMemberId); + await RunUserTests(userClient); + } + } + /// /// Delete the specified folder including any files within the folder ///