You might see that the Dropbox Community team have been busy working on some major updates to the Community itself! So, here is some info on what’s changed, what’s staying the same and what you can expect from the Dropbox Community overall.

Forum Discussion

Robert S.138's avatar
Robert S.138
Helpful | Level 7
9 years ago

iOS Exception: "Dropbox client already authorized"

While developing my code for using the new Objective-C SDK, I am running on the Xcode emulator.  The first time I tried OAuth, it worked and brought up the Dropbox login web page.  NSLog verified that the OAuth result was success.  The second time I ran it there was an exception thrown saying "A Dropbox client is already authorized".  How can I properly ensure there is no authorized client before I start to create a new one, or otherwise avoid this exception?  The call stack is:

2016-09-12 14:39:54.755 TuneLab[1303:57987] *** Assertion failure in +[DropboxClientsManager authorizeFromController:controller:openURL:browserAuth:], /Users/robertscott/Documents/iOS Proj/TuneLab/Pods/ObjectiveDropboxOfficial/Source/ObjectiveDropboxOfficial/PlatformDependent/iOS/DropboxClientsManager+MobileAuth.m:32

2016-09-12 14:39:54.760 TuneLab[1303:57987] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'A Dropbox client is already authorized'

*** First throw call stack:

(

0   CoreFoundation                      0x04017494 __exceptionPreprocess + 180

1   libobjc.A.dylib                     0x03ad1e02 objc_exception_throw + 50

2   CoreFoundation                      0x0401732a +[NSException raise:format:arguments:] + 138

3   Foundation                          0x00599322 -[NSAssertionHandler handleFailureInMethod:object:file:lineNumber:description:] + 118

4   TuneLab                             0x001bb1d1 +[DropboxClientsManager(MobileAuth) authorizeFromController:controller:openURL:browserAuth:] + 881

5   TuneLab                             0x0003ef99 -[DBxfer buttonPressed:] + 290

6   libobjc.A.dylib                     0x03ae60b5 -[NSObject performSelector:withObject:withObject:] + 84

7   UIKit                               0x00944e38 -[UIApplication sendAction:to:from:forEvent:] + 118

8   UIKit                               0x00944db7 -[UIApplication sendAction:toTarget:fromSender:forEvent:] + 64

9   UIKit                               0x00ae8f3b -[UIControl sendAction:to:forEvent:] + 79

10  UIKit                               0x00ae92d4 -[UIControl _sendActionsForEvents:withEvent:] + 433

11  UIKit                               0x00ae82c1 -[UIControl touchesEnded:withEvent:] + 714

12  UIKit                               0x009c552e -[UIWindow _sendTouchesForEvent:] + 1095

13  UIKit                               0x009c65cc -[UIWindow sendEvent:] + 1159

14  UIKit                               0x00967be8 -[UIApplication sendEvent:] + 266

15  UIKit                               0x0093c769 _UIApplicationHandleEventQueue + 7795

16  CoreFoundation                      0x03f29e5f __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 15

17  CoreFoundation                      0x03f1faeb __CFRunLoopDoSources0 + 523

18  CoreFoundation                      0x03f1ef08 __CFRunLoopRun + 1032

19  CoreFoundation                      0x03f1e846 CFRunLoopRunSpecific + 470

20  CoreFoundation                      0x03f1e65b CFRunLoopRunInMode + 123

21  GraphicsServices                    0x05c15664 GSEventRunModal + 192

22  GraphicsServices                    0x05c154a1 GSEventRun + 104

23  UIKit                               0x00942eb9 UIApplicationMain + 160

24  TuneLab                             0x000085c6 main + 230

25  libdyld.dylib                       0x04bb2a25 start + 1

26  ???                                 0x00000001 0x0 + 1

)

libc++abi.dylib: terminating with uncaught exception of type NSException

(lldb)

  • Robert S.138's avatar
    Robert S.138
    Helpful | Level 7

    In the emulator I selected a different device, which causes the iOS to reboot.  Then the error went away.  So perhaps the exception was a side-effect of stoping the app with Xcode and starting it up again.  But that leads me to wonder if there aren't some scenarios in real devices where something like this happens and an inconsistency is generated.  In that case I would like to know what addition steps I can take when starting an OAuth to ensure this exception will not occur.  I cannot use the presence or absence of a stored Access Token, but my data state might be inconsistent with the Dropbox system and I might not have a stored Access Token even though the SDK thinks I should have one.

    **** Edit: I tried rebooting the emulator and the exception in OAuth remains.  There is something persistent in the iOS the remembers there was a successful authorization, but it has nothing to do with my app.  I just saw [DropboxClientsManager unlinkClients]; in the example app.  Will this do it, and is it safe and reasonable to call this every time, just before authorizeFromController, even if there is no existing authorized client?

     

  • Greg-DB's avatar
    Greg-DB
    Icon for Dropbox Staff rankDropbox Staff

    Hi Robert, that assertion is actually easy enough to hit if you call authorizeFromController more than once (if you do link an account the first time). The DropboxClientsManager class is meant as a convenience for the standard single account case, so you can use the following to check if you're already linked:

    if ([DropboxClientsManager authorizedClient] != nil) {

    // already linked, use authorizedClient to make API calls

    That's the same thing the SDK checks for that assertion, and it's set by checking the SDK's own access token storage, so you shouldn't have to worry about inconsistency there. (If you're doing your own access token storage for some reason though, you should just construct DropboxClient objects directly.)

  • Greg-DB's avatar
    Greg-DB
    Icon for Dropbox Staff rankDropbox Staff

    Also, in response to your edit, you don't need to call unlinkClients each time, though that will clear out authorizedClient if that's what you want to do.

    You can re-use the tokens the SDK stores for you without having the user go through the app authorization flow each time (authorizeFromController).

  • Robert S.138's avatar
    Robert S.138
    Helpful | Level 7

    I think the confusion comes from my trying to persist the Access Token myself instead of letting DBKeychain do it.  In the Android version, I don't think there was any choice.  If we wanted the Access Token to persist between sessions, we had to store it ourselves in the app Preferences.  But for the iOS version, it appears the only reason to deal with an Access Token in my code is if the Access Token were manually generated externally.  If I understand correctly, the SDK for iOS will automatically persist the Access Token upon a successful OAuth2.  (I was actually getting the Access Token from the DBAuthResult.accessToken.accessToken.  But apparently that is unnecessary?)  Let me know if I should eliminate all explicit references to an Access Token in my application code, as long as I only intend to use Access Tokens that have been generated through the OAuth flow in that app itself.

     

  • Greg-DB's avatar
    Greg-DB
    Icon for Dropbox Staff rankDropbox Staff

    That's correct. The Android SDK doesn't do it for you, but the iOS SDK does automatically store access tokens in the keychain for you, so you don't have to. 

    • trinigato's avatar
      trinigato
      New member | Level 2

      How to decline automatic store in keychain? I don't want to be authorized after app's reinstallation

      • Greg-DB's avatar
        Greg-DB
        Icon for Dropbox Staff rankDropbox Staff

        trinigato The Dropbox Objective-C SDK doesn't have an option to disable the automatic token storage, but I'll pass this along as a feature request. (You can explicitly call unlinkAndResetClients when desired though.)