Migrating from version 1.10 to version 2.0

Important caveat

When upgrading to the 2.0 SDK, make sure to upgrade both web and mobile applications at the same time.

Otherwise, you may have blocks pushed to the Tanker servers from the web application that mobile applications won't be able to process.

Server-side changes

The first step is to replace the usertoken SDK with the identity SDK.

Then, if you had existing users with user tokens stored in database, you should convert them to secret identities using the function named upgradeUserToken from the identity SDK.

For instance, in Go:


import (
    "github.com/TankerHQ/identity-go/identity"
)

// Note: error handling omitted for brievity
func GetTankerIdentity(userID string) {
    user := storage.GetUser(userID)

    config := identity.Config {
        AppID: ...,
        AppSecret: ...,
    }

    if user.tankerIdentity != "" {
        // Tanker identity already created, return it
        return user.tankerIdentity
    }

    if user.tankerUserToken != "" {
        // user had a user token, convert to an identity
        user.tankerIdentity = identity.UpradeUserToken(config, userID, user.tankerUserToken)
    }

    if user.tankerIdentity != "" {
        // user had neither user token nor identiny, create a new one, save it and
        // return it
        user.tankerIdentity = identity.Create(config, userID)
    }

    storage.SaveUser(user)
    return user.tankerIdentity
}

Once this is done, you may proceed to adapt server code to store and retrieve identities, and, in case you need it, implement pre-registration sharing

In particular, this means you should modify your authentication routes to send back tanker identities instead of user tokens.

Client-side changes

Overview of method changes

The old and new API methods do not map 1-to-1, since internal behaviors have been redispatched significantly.

Here is a quick recap table to have a grasp of the changes described in the sections hereafter:

SDK 2.0 methods Roughly equivalent to SDK 1.10 methods
start first half of open
registerIdentity second half of open + registerUnlock with a verified method
verifyIdentity second half of open + unlockCurrentDevice
setVerificationMethod additional registerUnlock call, with a verified method
stop close

Starting a session

// Before 2.0
await tanker.open(userId, userToken);

// After 2.0
await tanker.start(identity);

Note: tanker.open() used to block until the device was unlocked, using the tanker.on('unlockRequired') callback. Now tanker.start() can return even if the Tanker session is not ready. See the section starting a Tanker session for details.

Use new Tanker status values

// Before 2.0
const {
  CLOSED,
  OPEN,
  UNLOCK_REQUIRED,
} = tanker;

// After 2.0
const {
  STOPPED,
  READY,
  IDENTITY_REGISTRATION_NEEDED,
  IDENTITY_VERIFICATION_NEEDED
} = Tanker.statuses;

Note that you must register a verification method and verify it right away when a user first connects to Tanker. Previously it was possible to create a user without any unlock mechanism in place and thus risk data loss.

Replace registerUnlock with registerIdentity

With email

// Before 2.0
// After open()
await tanker.registerUnlock({ email });

// After 2.0
// After start(), in case status is IDENTITY_REGISTRATION_NEEDED
await tanker.registerIdentity({ email, verificationCode });

Note

You need to send an email containing the verification code before using other Tanker methods.

With password

Replace password with passphrase:

// Before 2.0
// After open()
await tanker.registerUnlock({ password });

// After 2.0
// After start(), in case status in IDENTITY_REGISTRATION_NEEDED
await tanker.registerIdentity({ passphrase: password });

Replace unlockCurrentDevice with verifyIdentity

With email

// Before 2.0
// After open(), in the unlockRequired callback:
await tanker.unlockCurrentDevice({ verificationCode });

// After 2.0
// After start(), in case status in IDENTITY_VERIFICATION_NEEDED
await tanker.verifyIdentity({ email, verificationCode });

With password

// Before 2.0
// After open(), in the unlockRequired callback:
await tanker.unlockCurrentDevice({ password });

// After 2.0
// After start(), in case status is IDENTITY_VERIFICATION_NEEDED
await tanker.verifyIdentity({ passphrase });

Replace generateAndRegisterUnlockKey with generateVerificationKey

// Before 2.0
// After open()
const unlockKey = await tanker.generateAndRegisterUnlockKey();
// In the unlockRequired callback
await tanker.unlockCurrentDevice({ unlockKey });

// After 2.0
// After start(), in case status is IDENTITY_REGISTRATION_NEEDED
const verificationKey = await tanker.generateVerificationKey();
await tanker.registerIdentity({ verificationKey });
// After start(), in case status is IDENTITY_VERIFICATION_NEEDED
await tanker.verifyIdentity({ verificationKey });

Replace close with stop

// Before 2.0
await tanker.close();

// After 2.0
await tanker.stop();

Replace registeredUnlockMethods et al with getVerificationMethods

// Before 2.0
const methods = tanker.registeredUnlockMethods();
const hasEmail = tanker.hasRegisteredUnlockMethod('email');
const hasAnyMethod = tanker.hasRegisteredUnlockMethods();

// After 2.0
const methods = await tanker.getVerificationMethods(); // Warning: this is now an asynchronous method
const hasEmail = methods.some(m => m.type === 'email');
const hasAnyMethod; // always true, by design

Replace second call to registerUnlock with setVerificationMethod

By passphrase

// Before 2.0
await tanker.registerUnlock({ password: newPassword });

// After 2.0
await tanker.setVerificationMethod({ passphrase: newPassphrase });

By email

// Before 2.0
await tanker.registerUnlock({ email: newEmail });

// After 2.0
await tanker.setVerificationMethod({ email: newEmail, verificationCode });

Replace user IDs with identities

// Before 2.0
// Using user ids
await tanker.encrypt(text, { shareWithUsers: [bobId, charlieId] });
await tanker.share([resourceId], { shareWithUsers: [bobId, charlieId] });
await tanker.createGroup([bobId, charlieId]);
await tanker.updateGroupMembers(groupId, { usersToAdd: [bobId, charlieId] });

// After 2.0
// Using public identities
await tanker.encrypt(text, { shareWithUsers: [bobPublicIdentity, charliePublicIdentity] });
await tanker.share([resourceId], { shareWithUsers: [bobPublicIdentity, charliePublicIdentity] });
await tanker.createGroup([bobPublicIdentity, charliePublicIdentity]);
await tanker.updateGroupMembers(groupId, { usersToAdd: [bobPublicIdentity, charliePublicIdentity] });