# Identity management

After importing and instantiating the SDK, it will need to have an "identity" to perform other operations.

A user is represented by several "identities", usually one per device. These identities are created through the SDK. They can either be kept only in volatile memory, or in a persistent local database. They can also be saved onto our servers in a secure way to be able to reuse them later.

To save an identity on our servers securely, there are two ways to protect it:

For a manual management of the protection of an identity that would not be covered by these plugins, do not hesitate to contact us or use the manual export.

uml diagram

# Creating identities

A Seald user is represented by several "identities". These are actually private keys generated locally on the user's device with the SDK.

They are all linked together by a signature chain. For more details about our technology, please visit the dedicated page (opens new window).

We distinguish two cases:

  • a first identity ;
  • the next identities that are attached to it, called "sub-identities".

# First identity

To create an identity for a new user, use the sdk.initiateIdentity function.

The two arguments needed for this function are:

  • userId: a unique identifier of this user for your application. This userId can be any string, as long as no other user in your application has the same, and that it does not contain any special characters;
  • userLicenseToken: this is a license token, authorizing the creation of this account in your team. A dedicated guide will explain how to generate these license tokens.

WARNING

If you call initiateIdentity several times for the same userId, it will create several independent identities, and only the last one will be active, the others will be disabled.

# Sub-identities

From an existing identity of a user you can create a "sub-identity" using the function sdk.createSubIdentity on an SDK instance which has already been initialized.

This sub-identity will then be able to decrypt everything that the initial identity can decrypt: when another user encrypts something for that user, both the identity and the subidentity will be able to decrypt it.

This is typically used to have a separate sub-identity per device. For example, when the user logs in to a new device, we can retrieve a "main" identity protected by password or with 2-man rule, then create a new sub-identity linked to it for this device. It is then possible to store this sub-identity in localStorage (opens new window), and to retrieve it when opening a new page, in order to have persistent sessions accross tabs.

This function returns a Buffer (opens new window) similar to the one to the one created by sdk.exportIdentity and described in the manual export paragraph.

You can then import this Buffer (opens new window) into another instance of the SDK with the function sdk.importIdentity, or save it directly on SSKS with the functions sdk.ssks2MR.saveIdentity or sdk.ssksPassword.saveIdentity.

# Persistent local database

# How it works

By default, the Seald SDK only saves its database in volatile memory: if you create an identity, then close and re-open your browser tab, the created identity will not be accessible anymore and will be lost, except if you have protected it onto SSKS with a password or with two-man-rule. It this case, even if the user has an authenticated session, they will have to re-type their password, or re-authenticate with their email, in order to retrieve their Seald identity.

In order to have a better user experience, we can use a persistent local database, automatically stored securely in the browser by the Seald SDK, encrypted with a key stored on your backend called databaseKey.

TIP

The Seald database uses nedb (opens new window), which in the browser does not necessarily use the localStorage, but choses (through localForage (opens new window)) the best storage method between IndexedDB, WebSQL or localStorage depending on your browser.

uml diagram

When the user re-opens the application, the databaseKey is retrieved from the backend through an authenticated session, and this key will be used to decrypt the encryptedDB.

uml diagram

# Using a persistent local database

To use a persistent local database, you just need to add a databasePath argument to save the database, and a databaseKey argument to encrypt it, when initializing the SDK.

Here, databaseKey and sessionID must be generated and returned by the server.

We add sessionID in the databasePath in order for it to be unique for each session.

Indeed, if the same user logs-out, then logs back in, the server will have generated a new databaseKey. We therefore cannot use the same database: the database must be unique for each session, so we make databasePath change depending on the sessionID.

TIP

Unlike the protection of a user's identity on SSKS, here the usage is the same whether you want to create a new identity and store it in a persistent database, or retrieve an existing identity from it.

You will find more details, especially on what you will have to implement on the backend side, on the example project's page about the persistent database.

# Password protection

Password protection of an identity is based on a derivation of a password known by the user into a symmetric key that is used to encrypt and decrypt the identity.

WARNING

A "forgotten password" functionality cannot be implemented with this method without loss of data. If it is needed in your application, use the protection with 2-man-rule instead.

# How it works

This encrypted identity is then stored on a dedicated service named Seald Secure Key Storage or SSKS.

The password protection of an identity works as follows:

uml diagram

The retrieval of a password protected identity works as follows:

uml diagram

The @seald-io/sdk-plugin-ssks-password plugin allows to do this automatically.

TIP

More precisely, the user's password is derived twice :

  • once by combining it with the userId and the appId to give a "storage key" which is not secret ;
  • a second time by combining it with a random "salt", the userId and the appId to give an "encryption key" which is secret.

The identity is encrypted with the "encryption key". Then, are sent to SSKS:

  • the "storage key" ;
  • the "salt" concatenated with the encrypted identity.

The identity is retrieved by sending to SSKS the storage key which returns the salt concatenated with the encrypted identity. The salt is then used to obtain the encryption key which is then used to decrypt the identity.

# Save the identity

To use this storage method, you need to add the @seald-io/sdk-plugin-ssks-password plugin when instantiating the SDK, then after creating the identity, store it on SSKS using the sdk.ssksPassword.saveIdentity function.

The identity is now saved.

TIP

You can also store another sub-identity, instead of the identity of the current SDK instance:

# Retrieve the identity

To retrieve the identity at login, call the sdk.ssksPassword.retrieveIdentity function, with the same userId and password arguments as used in the save-identity.

The SDK instance is now ready to be used!

# Change the password

When the user changes the password, you can use the sdk.ssksPassword.changeIdentityPassword function to have the identity stored with the new password and delete the one stored with the old one.

The password is now changed and only the new one can be used.

TIP

If the old password is not known (typically a forgotten password), this plugin does not allow to recover the identity. If the "forgotten password" functionality is needed in your application, use the 2-man-rule protection instead.

WARNING

This operation does not change the keys of the identity itself, it only re-protects it with a new password. If the old password is considered breached, you must also create a new identity and revoke the old one.

# Password also used for authentication

In case you want to use the same password for identity protection as for authentication, it cannot be transmitted to the server as is, otherwise the server could then reconstruct the user's key, thus breaking the end-to-end encryption.

In this case, the login and account creation steps must be modified to introduce password pre-derivation using secure derivation functions so that your server never has access to the raw password, but only to a derived version.

You can find an example implementation here.

TIP

In case you can't change the authentication function (e.g. in case of federated authentication), then you should use a second password or another identity protection mode like 2-man rule.

# Protection with 2-man-rule

The 2-man-rule protection mode consists of "splitting" the identity to be protected in two and placing one half on the application's back-end subject to authentication, and the other half on Seald Secure Key Storage (SSKS) subject to independent authentication.

The goal is that a user has "the right" to lose all their passwords and all their devices without losing any data. The trade-off is that this mode is not strictly "end-to-end".

It is necessary to implement on the server side the storage of a secret stored and delivered against authentication, as well as an interaction with SSKS.

# How it works

More specifically, to protect an identity, the protocol is as follows (in the normal case):

uml diagram

TIP

In the case where an identity has previously been stored onto SSKS with the same email address, the SSKS server will then demand an authentication with an email challenge, similarly to what is described in the following section about retrieving an identity.

For more details, you may refer to the guide about Integrating 2-man-rule on your backend.

To retrieve an identity, the protocol is as follows:

uml diagram

Thus if the backend is breached, the encryptedIdentity stored on SSKS will not be breached, and the backend cannot simulate an authentication to SSKS.

Conversely, if SSKS is breached, the twoManRuleKey will not be breached and SSKS cannot simulate authentication to the backend.

WARNING

This mode is not strictly end-to-end in the sense that the key to decrypt a user's data can be retrieved without the user's consent by combining twoManRuleKey on SSKS and encryptedIdentity on the backend.

TIP

Seald takes the following precautions to ensure the privacy of data stored on SSKS :

  • use of servers not subject to the Cloud Act (opens new window) (using OVH (opens new window) in France) so that the 2-man rule protection mode is robust when using host providers subject to the extra-territoriality of US law for the backend;
  • over-encryption at rest of sensitive fields (encryptedIdentity) ;
  • hashing and salting of the users' e-mail addresses to which the encryptedIdentity are associated, so that they are only known at runtime. Thus, a breach at rest of SSKS won't reveal to which e-mail address an encryptedIdentity is associated.

# Protection of an identity

To use this method of protection, you must install, import & add when instantiating the SDK the @seald-io/sdk-plugin-ssks-2mr plugin.

Your application server must then :

  • create a new user on SSKS by giving its email address ;
  • generate an SSKS session for this user;
  • generate a secret twoManRuleKey for this user (a sufficiently robust random string of characters, for example an UUID would be a good choice)
  • transmit the sessionId and the secret twoManRuleKey to the front-end which we will symbolize by API.getSSKSSession() in the example.

The user can then store their identity using the sdk.ssks2MR.saveIdentity function.

You can find the SSKS API documentation for your application server here.

The identity is now saved.

TIP

You can also store another sub-identity, instead of the identity of the current SDK instance:

TIP

The SSKS server cannot access the user's identity, because it does not have access to the secret key twoManRuleKey kept by your server. Conversely, your application server cannot access it either, because to retrieve an identity it would need a challenge sent by email, which it nerver receives and cannot forge.

# Retrieving an identity

Then, upon a new instantiation of the SDK, your backend must generate in the same way a session for the user, and pass the sessionId and the twoManRuleKey to the front-end. The SSKS server then sends an email containing a challenge (valid for 6h) to the user's email address.

Then you need to call the function sdk.ssks2MR.retrieveIdentity with the arguments userId, sessionId, email, challenge and twoManRuleKey.

Your SDK instance is now ready to be used!

# Manual export

It is also possible to manage the storage of an identity manually.

For this, you can use the sdk.exportIdentity and sdk.importIdentity functions.

sdk.exportIdentity allows you to export a Buffer (opens new window) containing the current identity of the SDK instance.

Conversely, sdk.importIdentity imports an identity Buffer (opens new window) in the SDK instance in order to make it functional.

WARNING

It is then up to you to ensure the security of these identity exports. This security is of course paramount to ensure the confidentiality of the encrypted data for this user.