In some cases, you might want some of your users to share data with others who did not register their identity with Tanker yet.

We designed pre-registration sharing to allow users to share with anyone, with the same high security-level as the rest of the Tanker SDK.

When to use pre-registration sharing?

The basic use-cases of pre-registration sharing are:

  • users inviting other people to use your application by sharing data with them
  • migrating an existing user base to start using Tanker

How does it work?

This works by pre-registering a special identity type, we call provisional identity, when there is a need to share with someone who did not register their identity with Tanker yet. These provisional identities are always linked with an email address, which will be verified to access data shared with the provisional identity.

To distinguish them from provisional identities, the identities used until now are called permanent identities in the sections hereafter.

The application server creates and stores provisional identities and returns public provisional identities to the front-end application for use in sharing, among other public permanent identities of registered users.

Once a person registers their permanent identity with Tanker, they can attach the provisional identity to their Tanker account by providing a verification code for the corresponding email address. This will allow them to access data shared with them before their registration.

Generating provisional identities

A provisional identity should only be generated when sharing with a person who did not register their permanent identity with Tanker yet.

The generation and usage of provisional identities is very similar to permanent ones. They should be generated server-side, stored in your database (usually in a distinct column from permanent identities), and given only to the corresponding user once they have created their account.

Integrated in the code example used earlier, it could look like this:

package server

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

var tankerConfig = identity.Config{
    AppID:                <AppId>,
    AppSecret:            <AppSecret>,
}

func GetPublicIdentity(email string) (*string, error) {
    user, err := GetUser(email)
    if err != nil {
        return nil, err
    }

    // Try the user's permanent identity
    tankerIdentity := user.TankerIdentity

    if tankerIdentity == nil {
        if user.provisionalIdentity {
            // Use the user's provisional identity
            tankerIdentity = user.provisionalIdentity
        } else {
            // Generate a new provisional identity for this user
            provisionalIdentity, err := identity.CreateProvisional(tankerConfig, email)
            if err != nil {
                return nil, err
            }
            user.ProvisionalIdentity = provisionalIdentity

            // Store it in the database
            if err := UpdateUser(user); err != nil {
                return nil, err
            }
            // Use it
            tankerIdentity = provisionalIdentity
        }
    }

    return identity.GetPublicIdentity(tankerIdentity)
}

Warning

Tanker provisional identities are secret identities. Only an authenticated user must be able to access a secret provisional identity associated with their account. In a pre-registration sharing operation, use public provisional identities derived from the secret ones, as shown in the example above.

Sharing with provisional identities

Sharing and creating groups works exacly the same whether a public identity was obtained from a provisional or a permanent identity.

Attaching provisional identities to a user's account

After a user has registered their permanent identity with Tanker, their can attach provisional identities to their Tanker account. Once attached to an account, a provisional identity cannot be attached to any other one.

Doing so will automatically allow the user to decrypt any data shared with the provisional identity. They will also automatically become members of any group the provisional identity was added to and will be able to decrypt any data shared with these groups.

To attach a provisional identity to a started Tanker session, call the attachProvisionalIdentity() method. The method will return a result containing:

  • a status, which can be READY if everything worked, or IDENTITY_VERIFICATION_NEEDED if the user needs to provide a verification code to verify the email address.
  • the email address corresponding to the provisional identity

To verify the provisional identity, call verifyProvisionalIdentity().

import { Tanker } from '@tanker/client-browser';

const config = { appId: 'your-app-id' };
const tanker = new Tanker(config);

const id = 'user-id';
const password = 'Tanker';

const startTanker = async () => {
    // Authenticate the user with your server
    const user = await app.authenticateUser(id, password);

    // Start a Tanker session for the user
    const status = await tanker.start(user.tankerIdentity);
    // ...

    // Attach provisional identity
    if (user.provisionalIdentity) {
        const {
            status,
            verificationMethod
        } = await tanker.attachProvisionalIdentity(user.provisionalIdentity);

        if (status === tanker.statuses.IDENTITY_VERIFICATION_NEEDED) {
            const { email } = verificationMethod;

            // Ask the application server to send a verification code via Tanker
            await app.sendVerificationCode(email);

            // Wait for the user to input the verification code
            const verificationCode = await app.promptUser('Enter your verification code:');

            // Verify the provisional identity
            await tanker.verifyProvisionalIdentity({ email, verificationCode });
        }
    }
}

NSString* email = @"user@example.com";

// Authenticate the user with your server
NSUser* user = [self.app authenticateUser:userID password:password];

// Start a Tanker session for the user
[self.tanker startWithIdentity:user.identity
             completionHandler:startHandler];

TKRStartHandler startHandler = ^(TKRStatus status, NSError* err) {
  if (err == nil) {
    if (status == TKRStatusReady && user.provisionalIdentity != nil) {
      [self.tanker attachProvisionalIdentity:user.provisionalIdentity
                           completionHandler:attachResultHandler];
    }
  }
}

TKRAttachResultHandler attachResultHandler = ^(TKRAttachResult* result, NSError* err) {
  if (err == nil) {
    if (result.status == TKRStatusVerificationNeeded) {

      // Ask the application server to send a verification code via Tanker
      [self.app sendVerificationCode:result.method.email];
      // Wait for the user to input the verification code
      NSString* verificationCode = [self.app promptUser:@"Enter your verification code:"];
      // Convert the email and verificationCode into a TKRVerification.
      TKRVerification* verification = [TKRVerification verificationFromEmail:result.method.email
                                                                        code:verificationCode];

      [self.tanker verifyProvisionalIdentityWithVerification:verification
                                           completionHandler:^(NSError* err) {
                                             if (err == nil) {
                                               // do something
                                             }
                                           }];
    }
  }
}
public class LoginActivity extends AppCompatActivity {
    private static final String TAG = "LoginActivity";
    private Tanker tanker;
    private String userId = "user-id";
    private String password = "Tanker";

    protected void onCreate(Bundle savedInstanceState) {
        TankerOptions options = new TankerOptions();
        String writablePath = getApplicationContext().getFilesDir().getAbsolutePath();
        options.setWritablePath(writablePath);
        options.setAppId(appId);
        tanker = new Tanker(options);
    }

    public void startTanker() {
        // Authenticate the user with your server
        User user = authenticateUser(userId, password);

        // Start a Tanker session for the user
        Status status = tanker.start(user.tankerIdentity);
        // ...

        // Attach provisional identity
        if (user.provisionalIdentity != null) {
            AttachResult attachResult = tanker.attachProvisionalIdentity(user.provisionalIdentity).get();
            if (attachResult.getStatus() == Status.IDENTITY_VERIFICATION_NEEDED) {
                String emailAddress = ((EmailVerification)attachResult.getVerificationMethod()).getEmail();

                // Ask the application server to send a verification code via Tanker
                app.sendVerificationCode(emailAddress);

                // Wait for the user to input the verification code
                String verificationCode = app.promptUser("Enter your verification code:");

                // Verify the provisional identity
                tanker.verifyProvisionalIdentity(new EmailVerification(emailAddress, verificationCode)).get();
            }
        }
    }
}