Skip to content

TMR Accesses

The concept of TmrAccesses is a feature of the Seald SDK that enables retrieving an EncryptionSession through a 2-man rule mechanism, similar to the storage of identities.

The idea is to protect an EncryptionSession for a certain Auth Factor (an email address, or a phone number), with a specific overEncryptionKey that will be stored by your servers (like the twoManRuleKey for identity storage).

Then, when the recipient wants to retrieve it, they receive a challenge by email or SMS, valid for 6 hours, and must repeat it in the application to prove they control the Auth Factor, and can access the EncryptionSession.

This feature is particularly useful for adding as recipient to an EncryptionSession a user who is not yet registered on your application, for example when sending them an invitation.

Creating a TmrAccess

The user who created an EncryptionSession, or any user who has access and the forward right, can create a TmrAccess for this EncryptionSession.

To create a TmrAccess, retrieve the EncryptionSession object, and use the session.addTmrAccess() method. This method takes as argument tmrRecipients, which is an Array of TmrRecipientWithRights. Each element of this Array contains three fields:

  • authFactor, which itself contains the type of the Auth Factor used ('EM' for an email address, 'SMS' for a phone number), and the value of this Auth Factor;
  • rawOverEncryptionKey, which is the key to protect this TmrAccess, must absolutely be the base64 encoding of a cryptographically random 64-byte buffer;
  • optionally, rights that define the rights for this TmrAccess.

TIP

To ensure end-to-end confidentiality, the rawOverEncryptionKey should only be accessible to the person creating the TmrAccess and the session recipient.

It is your server's responsibility to store these rawOverEncryptionKey and limit access to the user who creates the TmrAccess and the invited user.

If multiple users need to add TmrAccesses for the same Auth Factor, you should create different rawOverEncryptionKeys for each one. You can also create a different rawOverEncryptionKey for each addition of TmrAccess, even if it is the same user adding it for the same Auth Factor, if this is simpler for you.

You can also specify rights for the recipient of this TmrAccess with the rights argument. When the user converts a TmrAccess into normal access, these rights will be converted with it.

TIP

The default rights for a TmrAccess are { read: true, forward: true, revoke: false }.

The session.addTmrAccess() method returns the ID of the created TmrAccess. This ID is not secret, and can be sent to your server. It can be used to manage the TmrAccess, or to choose which TmrAccess to use to retrieve a session.

Example usage:

javascript
// Each `rawOverEncryptionKey` must necessarily be a 64-byte cryptographically secure random buffer, encoded in base64.
const rawOverEncryptionKey = await sealdSdk.utils.generateB64EncodedSymKey()

// Creating `TmrAccess`
const tmrAccessId = await session.addTmrAccess({
  authFactor: { type: 'EM', value: 'test@example.com' },
  rawOverEncryptionKey,
  rights: { read: true, forward: false, revoke: false }
})

// Now, you can send the tmrAccessId with the rawOverEncryptionKey to your servers to save them.
// Each `rawOverEncryptionKey` must necessarily be a 64-byte cryptographically secure random buffer, encoded in base64.
const rawOverEncryptionKey = await sealdSdk.utils.generateB64EncodedSymKey()

// Creating `TmrAccess`
const tmrAccessId = await session.addTmrAccess({
  authFactor: { type: 'EM', value: 'test@example.com' },
  rawOverEncryptionKey,
  rights: { read: true, forward: false, revoke: false }
})

// Now, you can send the tmrAccessId with the rawOverEncryptionKey to your servers to save them.

TIP

If you need to add multiple TmrAccesses to the same EncryptionSession, for multiple recipient Auth Factors for example, you can also use the session.addMultipleTmrAccesses() method. the session.addMultipleTmrAccesses() method. This has a more complex return type, detailed in its reference. This has a more complex return type, detailed in its reference.

Retrieving a session with a TmrAccess

To retrieve an EncryptionSession via a TmrAccess, one must first prove that they control the recipient's Auth Factor. To do this, obtain a JWT from the SSKS server with the ssks2MR.getFactorToken() method. This method, of course, requires being authenticated with SSKS, that is either having an authenticatedSessionId, or using the POST /tmr/back/challenge_send/ API endpoint (with force_auth to true) to obtain a challenge, and passing this challenge during the call to ssks2MR.getFactorToken().

Then, once you have this token, use the sdk.retrieveEncryptionSessionByTmr() method. This method takes as arguments:

  • the ID of the EncryptionSession;
  • the token provided by ssks2MR.getFactorToken();
  • the rawOverEncryptionKey used;
  • optionally, options (including filtering) detailed below.

The sdk.retrieveEncryptionSessionByTmr() method returns an EncryptionSession, which then allows for encrypting/decrypting messages and files.

TIP

If you access an EncryptionSession via a TmrAccess, you cannot use the access management methods of that session (session.addRecipients(), session.addSymEncKey(), session.revokeRecipients(), session.listRecipients(), ...) without having converted it.

Example of simple usage:

javascript
// Retrieving a tmrJWT
const getFactorTokenResponse = await sdk.ssks2MR.getFactorToken({ sessionId: ssksSessionId, authFactor, challenge })

// Retrieving an EncryptionSession with a `TmrAccess`
const encryptionSession = await sdk.retrieveEncryptionSessionByTmr(
  encryptionSessionId, // `encryptionSessionId` is not secret, and can be transmitted via your server
  getFactorTokenResponse.token, // The token obtained by calling `ssks2MR.getFactorToken()`
  rawOverEncryptionKey // `rawOverEncryptionKey` transmitted by your server
)
// Retrieving a tmrJWT
const getFactorTokenResponse = await sdk.ssks2MR.getFactorToken({ sessionId: ssksSessionId, authFactor, challenge })

// Retrieving an EncryptionSession with a `TmrAccess`
const encryptionSession = await sdk.retrieveEncryptionSessionByTmr(
  encryptionSessionId, // `encryptionSessionId` is not secret, and can be transmitted via your server
  getFactorTokenResponse.token, // The token obtained by calling `ssks2MR.getFactorToken()`
  rawOverEncryptionKey // `rawOverEncryptionKey` transmitted by your server
)

Warning, if there are multiple TmrAccesses for the same Auth Factor and the same EncryptionSession, by default retrieveEncryptionSessionByTmr() will throw an error. Indeed, this function must know which TmrAccess to use, that is, which one corresponds to the passed rawOverEncryptionKey.

For this, retrieveEncryptionSessionByTmr() accepts an options object, which contains the following optional arguments:

  • useCache: a boolean, which defaults to true. Indicates to try retrieving the session from the cache;
  • tmrAccessId: the ID of the TmrAccess to use. If you have it, it is recommended to always pass it, to avoid any problems;
  • createdById: the ID of the user who added the TmrAccess to use;
  • tryIfMultiple: a boolean, which defaults to false. If set to true, and if the other optionally passed filters match multiple TmrAccesses, indicates to iterate over all retrieved TmrAccesses to find on which the passed rawOverEncryptionKey works, instead of returning an error.

Example usage with options:

javascript
// Retrieving a tmrJWT
const getFactorTokenResponse = await sdk.ssks2MR.getFactorToken({ sessionId: ssksSessionId, authFactor, challenge })

// Retrieving an EncryptionSession with a `TmrAccess`
const encryptionSession = await sdk.retrieveEncryptionSessionByTmr(
  encryptionSessionId, // `encryptionSessionId` is not secret, and can be transmitted via your server
  getFactorTokenResponse.token, // The token obtained by calling `ssks2MR.getFactorToken()`
  rawOverEncryptionKey, // `rawOverEncryptionKey` given by your server
  {
    tmrAccessId // retrieves the session via the TmrAccess that corresponds to this ID
  }
)
// Retrieving a tmrJWT
const getFactorTokenResponse = await sdk.ssks2MR.getFactorToken({ sessionId: ssksSessionId, authFactor, challenge })

// Retrieving an EncryptionSession with a `TmrAccess`
const encryptionSession = await sdk.retrieveEncryptionSessionByTmr(
  encryptionSessionId, // `encryptionSessionId` is not secret, and can be transmitted via your server
  getFactorTokenResponse.token, // The token obtained by calling `ssks2MR.getFactorToken()`
  rawOverEncryptionKey, // `rawOverEncryptionKey` given by your server
  {
    tmrAccessId // retrieves the session via the TmrAccess that corresponds to this ID
  }
)

Converting TmrAccesses into a normal accesses

You can also convert a TmrAccess into a normal access to the corresponding EncryptionSession. This mechanism can be useful if the TmrAccess was created as an invitation for a user who does not yet have a Seald account to access an EncryptionSession. Once they have created their Seald account, the TmrAccesses intended for them can be converted into normal accesses, allowing their Seald account to access the corresponding EncryptionSessions in the future without undergoing a 2-Man-Rule authentication each time.

To convert a TmrAccess into normal access to the EncryptionSession, one must first prove that they control the recipient's Auth Factor. To do this, obtain a JWT from the SSKS server with the ssks2MR.getFactorToken() method. This method, of course, requires being authenticated with SSKS, that is either having an authenticatedSessionId, or using the POST /tmr/back/challenge_send/ API endpoint (with force_auth to true) to obtain a challenge, and passing this challenge during the call to ssks2MR.getFactorToken().

Then, once you have this token, you must use the sdk.convertTmrAccesses() method. This method takes as arguments:

  • the token issued by ssks2MR.getFactorToken();
  • the rawOverEncryptionKey used;
  • optionally, options (including filtering) detailed below.

This method retrieves all the TmrAccesses matching the given filters, and converts them into normal access to the corresponding EncryptionSession.

WARNING

It is required that all the TmrAccesses matching these filters be protected by the same rawOverEncryptionKey!

For this, convertTmrAccesses() accepts an options object, which contains the following optional arguments:

  • deleteOnConvert: a boolean, which defaults to true. Indicates to delete the TmrAccesses after their conversion into normal accesses;
  • sessionId: the ID of the EncryptionSession for which to convert a TmrAccess;
  • tmrAccessId: the ID of the TmrAccess to convert;
  • createdById: the ID of the user who added the TmrAccesses to convert.

TIP

Since it is recommended to have a different rawOverEncryptionKey for each sender, and since the call to convertTmrAccesses() must absolutely contain filters that limit it to TmrAccesses having the same rawOverEncryptionKey, it is recommended to always specify createdById.

Example of use:

javascript
// Retrieving a tmrJWT
const getFactorTokenResponse = await sdk.ssks2MR.getFactorToken({ sessionId: ssksSessionId, authFactor, challenge })

// Converting TmrAccesses
const result = await sdk.convertTmrAccesses(
  getFactorTokenResponse.token, // The token obtained by the call to `ssks2MR.getFactorToken()`
  rawOverEncryptionKey, // `rawOverEncryptionKey` transmitted by your server
  {
    createdById, // converts only the TmrAccesses created by the user corresponding to this SealdID
    sessionId: encryptionSessionId // converts only the TmrAccesses corresponding to this EncryptionSession
  }
)

// Here, one can check `result` to see if the conversion was successful

// Now, the user can retrieve the session directly
const encryptionSession = await sdk.retrieveEncryptionSession({ sessionId: encryptionSessionId })
// Retrieving a tmrJWT
const getFactorTokenResponse = await sdk.ssks2MR.getFactorToken({ sessionId: ssksSessionId, authFactor, challenge })

// Converting TmrAccesses
const result = await sdk.convertTmrAccesses(
  getFactorTokenResponse.token, // The token obtained by the call to `ssks2MR.getFactorToken()`
  rawOverEncryptionKey, // `rawOverEncryptionKey` transmitted by your server
  {
    createdById, // converts only the TmrAccesses created by the user corresponding to this SealdID
    sessionId: encryptionSessionId // converts only the TmrAccesses corresponding to this EncryptionSession
  }
)

// Here, one can check `result` to see if the conversion was successful

// Now, the user can retrieve the session directly
const encryptionSession = await sdk.retrieveEncryptionSession({ sessionId: encryptionSessionId })