Migrating user access tokens from the Sync SDK to the Core SDK

The Sync API is being deprecated, and we recommend switching to the Core API. To avoid users having to reauthorize your app when you make the switch, you’ll want to extract the access tokens stored by the Sync API and reuse them.

This post shows you how to get the OAuth access tokens stored by the Sync SDK and use them with the Core SDK. For more on how OAuth works with Dropbox, check out this handy guide.

Android

Replacing Sync with Core

  1. Remove the Sync SDK JAR files from your project, as well as the native component files in the jniLibs folder (If you built your app using Eclipse, you might have only put files in your project’s libs directory).
  2. Delete anything in the AndroidManifest.xml that is specific to Dropbox or the Sync SDK.
  3. Follow the steps on our site to install the Core API SDK for Android.
  4. Add the code snippets below to get the tokens saved by the Sync SDK, and then use them with the Core API.

Remember, unlike the Sync SDK, the Android Core SDKs don’t save the access tokens for you. You’ll need the tokens again after your app closes, so it’s important to save them for future access (though it’s not shown here). If you don’t, the user will have to re-authorize every time they use your app. A common way to implement storing keys is through Android’s SharedPreferences API, which is what the Sync SDK did.

Getting stored tokens

For any user who connected your app using the Sync API, you can get the token stored in SharedPreferences. The stored token might be an OAuth 1 or an OAuth 2 token; this example handles both.

List getTokensFromSyncAPI() {
    List tokens = new ArrayList<>();

    JSONArray jsonAccounts = new JSONArray(getApplicationContext().getSharedPreferences("dropbox-credentials", Context.MODE_PRIVATE).getString("accounts", null));

    for (int i = 0; i < jsonAccounts.length(); i++) {
        String token = jsonAccounts.getJSONObject(i).getString("userToken");
        if (token.startsWith("|oa2|")) {
            tokens.add(token.substring(5));
        } else {
            String[] split = token.split("\\|");
            String key = split[0];
            String secret = split[1];
            tokens.add(new AccessTokenPair(key,secret));
        }
    }

    return tokens;
}

Using the tokens with the Core API
Learn how to use the Core API with Android in this tutorial.

List tokens = getTokensFromSyncAPI();

// If there is no access token stored for this user, start the authentication flow
if (tokens.length() == 0) {
    // MyActivity below should be your activity class name
    mDBApi.getSession().startOAuth2Authentication(MyActivity.this);
    // See the Core API tutorial for instructions on how to finish the authentication flow
}

// Otherwise, initialize your session with the stored token (this example just uses the first one)
else {
    Object token = getTokensFromSyncAPI().get(0);
    if (token instanceof AccessTokenPair) {
        session.setAccessTokenPair((AccessTokenPair) token);
    } else {
        session.setOAuth2AccessToken((String) token);
    }
}

iOS

Replacing Sync with Core

  1. Remove Dropbox/Dropbox.h imports from your code.
  2. Remove Dropbox.framework from “Frameworks”.
  3. Follow the installation instructions for installing Core API on iOS.
  4. Add the code snippets below to get the tokens from the Sync SDK for use with the Core API.

Getting stored tokens

The Dropbox Sync SDK stored OAuth 1 tokens on the iOS keychain. The below snippet will get you the OAuth 1 token and secret.

- (NSArray *)getTokensFromSyncAPI
{
    NSString *keychainPrefix = [[NSBundle mainBundle] bundleIdentifier];
    NSString *keychainId = [NSString stringWithFormat:@"%@.%@", keychainPrefix, @"dropbox-sync.auth"];
    NSDictionary *keychainDict = @{(__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
                                   (__bridge id)kSecAttrService: keychainId,
                                   (__bridge id)kSecMatchLimit: (__bridge id)kSecMatchLimitOne,
                                   (__bridge id)kSecReturnAttributes: (__bridge id)kCFBooleanTrue,
                                   (__bridge id)kSecReturnData: (__bridge id)kCFBooleanTrue};

    CFDictionaryRef result = NULL;
    OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)keychainDict,
                                          (CFTypeRef *)&result);
    NSDictionary *attrDict = (__bridge_transfer NSDictionary *)result;
    NSData *foundValue = [attrDict objectForKey:(__bridge id)kSecValueData];
    NSMutableArray *credentials = [[NSMutableArray alloc] init];

    if (status == noErr && foundValue) {
        NSDictionary *savedCreds = [NSKeyedUnarchiver unarchiveObjectWithData:foundValue];
        NSArray *credsForApp = [[savedCreds objectForKey:@"accounts"] objectForKey:APP_KEY];
        for (NSDictionary *credsForUser in credsForApp) {
            NSDictionary *token = @{
                                    @"userId": [credsForUser objectForKey:@"userId"],
                                    @"token": [credsForUser objectForKey:@"token"],
                                    @"tokenSecret": [credsForUser objectForKey:@"tokenSecret"]
                                    };
            [credentials addObject:token];
        }
    }
    return credentials;
}

Using the tokens with the Core API

Once you have the tokens, you can easily load them into the Core SDK, which also uses Apple’s Keychain for storing tokens.

#import <DropboxSDK/DropboxSDK.h>

NSArray *credentials = [self getTokensFromSyncAPI];
if ([credentials count] == 0) {
  // See the Core API tutorial for instructions on how to create a URL scheme
  // and finish the normal Core authentication flow
  [[DBSession sharedSession] linkFromController:self];
} else {
  // This example also only uses the one of potentially several stored tokens
  NSDictionary *credential = [credentials objectAtIndex:0];
  [[DBSession sharedSession] updateAccessToken:credential[@"token"] accessTokenSecret:credential[@"tokenSecret"] forUserId:credential[@"userId"]];
}

OSX

Replacing Sync with Core

  1. Remove Dropbox/Dropbox.h imports from your code.
  2. Remove the Dropbox.framework from “Frameworks”.
  3. Download the Core API from the developer site.
  4. Drag and drop the DropboxOSX.framework directory to your “Frameworks” directory, make sure Copy items… is selected, and click Finish.
  5. Ensure that you have Security.framework added to your project. To do this, select your project file in the file explorer, select your target, and select the Build Phases sub-tab. Under Link Binary with Libraries, press the + button, select Security.framework, and press Add.
  6. Add the code snippets below to get the tokens from the Sync SDK for use with the Core API.

Getting stored tokens

The Dropbox Sync SDK stored OAuth 1 tokens on Apple’s keychain service.

- (NSArray *)getTokensFromSyncAPI
{
    NSString *keychainId = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleName"];
    keychainId = [keychainId stringByAppendingFormat:@".%@", @"dropbox-sync.auth"];
    NSDictionary *ret = nil;
    UInt32 dataLen = 0;
    void *pData = NULL;
    SecKeychainItemRef _itemRef = NULL;
    OSStatus status = SecKeychainFindGenericPassword(NULL,
                                                     (int32_t)[keychainId length], [keychainId UTF8String],
                                                     (int32_t)strlen("Dropbox"), "Dropbox",
                                                     &dataLen, &pData, &_itemRef);

    NSMutableArray *credentials = [[NSMutableArray alloc] init];
    if (status == noErr) {
        NSData *data = [NSData dataWithBytes:pData length:dataLen];
        ret = [NSKeyedUnarchiver unarchiveObjectWithData:data];
        NSArray *credsForApp = [[ret objectForKey:@"accounts"] objectForKey:APP_KEY];
        for (NSDictionary *credsForUser in credsForApp) {
            NSDictionary *token = @{
                                    @"userId": [credsForUser objectForKey:@"userId"],
                                    @"token": [credsForUser objectForKey:@"token"],
                                    @"tokenSecret": [credsForUser objectForKey:@"tokenSecret"]
                                    };
            [credentials addObject:token];
        }
    }

    if (pData) {
        SecKeychainItemFreeContent(NULL, pData);
    }
    return credentials;
}

Using the tokens with the Core API

Like iOS, once you have the tokens, you can easily load them into the Core SDK, which also uses Apple’s Keychain for storing tokens.

#import <DropboxOSX/DropboxOSX.h>;

NSArray *credentials = [self getTokensFromSyncAPI];
if ([credentials count] == 0) {
  // See the Core API tutorial for instructions on how to create a URL scheme
  // and finish the normal Core authentication flow
  [[DBSession sharedSession] linkFromController:self];
} else {
  // This example also only uses the one of potentially several stored tokens
  NSDictionary *credential = [credentials objectAtIndex:0];
  [[DBSession sharedSession] updateAccessToken:credential[@"token"] accessTokenSecret:credential[@"tokenSecret"] forUserId:credential[@"userId"]];
}