In the guide, we covered identity verification with a verification code sent by email. There are alternative ways to verify a user's identity.

Each alternative method can be used on its own, or in conjunction with other verification methods.

Verifying the identity with a passphrase

It is possible to verify a user's identity by setting up and providing a passphrase.

This can be a user-chosen password, a randomly-generated passphrase or even the answer to a security question. Just be sure not to store the passphrase anywhere as it would compromise the user's account security.

Setting up a verification passphrase

Setting up a passphrase is done by using the registerIdentity() method with the passphrase argument.

if (status === Tanker.statuses.IDENTITY_REGISTRATION_NEEDED) {
  // Get the user to input their security passphrase
  const passphrase = await app.promptUser('Enter your passphrase:');

  // Register the identity with the passphrase as a verification method
  await tanker.registerIdentity({ passphrase });
}
if (status == TKRStatusIdentityRegistrationNeeded) {

  // Get the user to input his security passphrase
  NSString* passphrase = [self.app promptUser:@"Enter your passphrase:"];

  // Convert the passphrase into a TKRVerification.
  TKRVerification* verification = [TKRVerification verificationFromPassphrase:passphrase];
  // Register the passphrase as the current identity verification method.
  [self.tanker registerIdentityWithVerification:verification
                              completionHandler:^(NSError* err) {
                              if (err == nil) {
                                // do something
                              }
                          }];
}
if (status == Status.IDENTITY_REGISTRATION_NEEDED) {
  // Get the user to input their security passphrase
  String passphrase = passphraseInput();

  // Register the identity with the passphrase as a verification method
  tanker.registerIdentity(new PassphraseVerification(passphrase)).get();
}

Verifying the identity with the passphrase

To later verify the user's identity using their passphrase, ask the user to input it and pass it to verifyIdentity() after starting the session:

if (status === Tanker.statuses.IDENTITY_VERIFICATION_NEEDED) {
  // Wait for the user to input their passphrase
  const passphrase = await app.promptUser('Enter your passphrase:');

  // Verify the identity
  await tanker.verifyIdentity({ passphrase });
}
if (status == TKRStatusIdentityVerificationNeeded) {
  // Wait for the user to input their passphrase
  NSString* passphrase = [self.app promptUser:@"Enter your passphrase:"];

  // Convert the passphrase into a TKRVerification.
  TKRVerification* verification = [TKRVerification verificationFromPassphrase:passphrase];
  // Verify the identity
  [self.tanker verifyIdentityWithVerification:verification
                            completionHandler:^(NSError* err) {
                            if (err == nil) {
                              // do something
                            }
                        }];
}
if (status == Status.IDENTITY_VERIFICATION_NEEDED) {
  // Wait for the user to input their passphrase
  String passphrase = passphraseInput();

  // Verify the identity
  tanker.verifyIdentity(new PassphraseVerification(passphrase)).get();
}

To change a user's passphrase, just call setVerificationMethod() with the new passphrase.

Note

The Tanker SDK takes care of hashing the passphrase client-side before sending it to the Tanker servers. Server-side, it is then hashed again and salted.

Recovering a lost passphrase

It is not possible to recover a lost passphrase.

However, in case a user forgot their passphrase, you can use any other verification method to verify their identity and then have them update their passphrase.

Using the application password as a passphrase

To avoid a situation in which users have to remember an additional password, you might want to use their application password as the security passphrase.

Warning

Please note that this is not the recommended way of handling identity verification, as it has security implications.

If you still want to do this, you must make sure that you hash the user's application password client-side before sending it to your application servers. This is very important because anyone (including you) having access to this password could decrypt any resources shared with the corresponding user.

Verifying the identity with OpenID Connect (OIDC)

Warning

The OIDC verification method is still under development and is provided as a preview version only.

Prerequisite: configuring your Tanker App

Warning

For now, the only supported OpenID Provider is Google.

If needed, follow the instructions on the Google OpenID Connect documentation to get a Client ID.

When ready, go to the dashboard and register your Google OpenID Connect Client ID on the desired Tanker App.

Setting up a verification with OpenID Connect

Setting up a user with OpenID Connect is done by using the registerIdentity() method with the oidcIdToken argument.

if (status === Tanker.statuses.IDENTITY_REGISTRATION_NEEDED) {
  // Get an OIDC ID Token from Google for the current user
  const idToken = await app.getGoogleIdToken();

  // Register the identity with OIDC
  await tanker.registerIdentity({ oidcIdToken: idToken });
}
if (status == Status.IDENTITY_REGISTRATION_NEEDED) {
  // Get an OIDC ID Token from Google for the current user
  String googleIdToken = app.getGoogleIdToken();
  Verification verification = new OIDCIDTokenVerification(googleIdToken);
  tanker.registerIdentity(verification).get();
}
if (status == TKRStatusIdentityRegistrationNeeded) {
  // Get an OIDC ID Token from Google for the current user
  NSString* googleIdToken = [self.app getGoogleIdToken];

  TKRVerification* verification = [TKRVerification verificationFromOIDCIDToken:googleIdToken];
  [self.tanker registerIdentityWithVerification:verification
               completionHandler:^(NSError* err) {
                 if (err == nil) {
                   // do something
                 }
               }];
}

Verifying the identity with OpenID Connect

To later verify the user's identity with OpenID Connect, pass the oidcIdToken argument to verifyIdentity() after starting the session:

if (status === Tanker.statuses.IDENTITY_VERIFICATION_NEEDED) {
  // Get an OIDC ID Token from Google for the current user
  const idToken = await app.getGoogleIdToken();

  // Verify the identity with OIDC
  await tanker.verifyIdentity({ oidcIdToken: idToken });
}
if (status == Status.IDENTITY_VERIFICATION_NEEDED) {
  // Get an OIDC ID Token from Google for the current user
  String googleIdToken = app.getGoogleIdToken();
  Verification verification = new OIDCIDTokenVerification(googleIdToken);
  tanker.verifyIdentity(verification).get();
}
if (status == TKRStatusIdentityVerificationNeeded) {
  // Get an OIDC ID Token from Google for the current user
  NSString* googleIdToken = [self.app getGoogleIdToken];

  TKRVerification* verification = [TKRVerification verificationFromOIDCIDToken:googleIdToken];
  [self.tanker verifyIdentityWithVerification:verification
               completionHandler:^(NSError* err) {
                 if (err == nil) {
                   // do something
                 }
               }];
}

To update a user's OIDC verification method, just call setVerificationMethod() with a new oidcIdToken.

Security considerations

The terminology used in this section comes from the OAuth 2.0 RFC and the OpenID Connect Spec.

Here are a few requirements for the OIDC verification method to be used safely:

  1. The Client must never use the ID Token to authenticate users or give them access to any resource (including Tanker identities), otherwise Tanker could impersonate users on the Client side.
  2. The Client must never request a Refresh Token with the openid scope. Otherwise, the Client would be able to request a new ID Token and proceed to Tanker identity verifications, even when the user is offline.
  3. The ID Token must never be persisted by the Client, even in volatile memory. Before its expiration, an unduly persisted ID Token would remain available for use, even after the user has disconnected from the Client. The Client servers having access to the Tanker identities, the Client would be able to proceed to Tanker identity verifications without user consent.

Manually handling the verification key

This is a low level API that can be used instead of other verification methods. It allows application developers to implement their own workflow for verifying a user's identity.

A verification key can be generated and later used for verifying a user's identity. Please note that losing this key may render your data inaccessible.

Getting the verification key

To get the user's verification key, you need to call generateVerificationKey() on a started Tanker session.

The simplest way to use this verification key is to display it to the user and make sure they store it in a safe place.

if (status === Tanker.statuses.IDENTITY_REGISTRATION_NEEDED) {
  const verificationKey = await tanker.generateVerificationKey();
  console.log('Please save this key in a safe place: ' + verificationKey);

  // Verify the identity
  await tanker.registerIdentity({ verificationKey });
}
if (status == TKRStatusIdentityRegistrationNeeded) {
  [self.tanker generateVerificationKeyWithCompletionHandler:verificationKeyHandler];
}

TKRVerificationKeyHandler verificationKeyHandler = ^(TKRVerificationKey* key, NSError* err) {
  if (err == nil) {
    // Ask user to store the verification key in a safe place, for instance using iCloud Keychain
    [self.app showSavingKeyViewWithVerificationKey:verificationKey];
    // Convert the verificationKey into a TKRVerification.
    TKRVerification* verification = [TKRVerification verificationFromVerificationKey:key];
    // Verify the identity
    [self.tanker registerIdentityWithVerification:verification
                                completionHandler:^(NSError* err) {
                                  if (err == nil) {
                                    // do something
                                  }
                                }];
  }
}
if (status == Status.IDENTITY_REGISTRATION_NEEDED) {
  String verificationKey = tanker.generateVerificationKey().get();
  Log.i(TAG, "Please save this key in a safe place: " + verificationKey);

  // Verify the identity
  tanker.registerIdentity(new VerificationKeyVerification(verificationKey)).get();
}

Verifying the identity with a verification key

To verify the user's identity using their verification key, ask the user to input it and pass it to verifyIdentity() after starting the session:

if (status === Tanker.statuses.IDENTITY_VERIFICATION_NEEDED) {
  // Wait for the user to input their verification key
  const verificationKey = await app.promptUser('Paste your verification key:');

  // Verify the identity
  await tanker.verifyIdentity({ verificationKey });
}
if (status == TKRStatusIdentityVerificationNeeded) {
  // Retrieve verification key
  TKRVerificationKey* verificationKey = [TKRVerificationKey verificationKeyFromValue:[self.app promptUser:@"Paste your verification key:"]];

  // Convert the verification key into a TKRVerification.
  TKRVerification* verification = [TKRVerification verificationFromVerificationKey:verificationKey];
  // Verify the identity
  [self.tanker verifyIdentityWithVerification:verification
                            completionHandler:^(NSError* err) {
                              if (err == nil) {
                                // do something
                              }
                            }];
}
if (status == Status.IDENTITY_VERIFICATION_NEEDED) {
  // Wait for the user to input their verification key
  String verificationKey = app.promptUser("Paste your verification key:");

  // Verify the identity
  tanker.verifyIdentity(new VerificationKeyVerification(verificationKey)).get();
}