SSKS API
The default URL of SSKS is ssks.seald.io
.
Headers of the requests
Authentication is done through HTTP headers:
Variable | Type | Description |
---|---|---|
X-SEALD-APPID (required) | String | Application ID, given on the Seald SDK administration dashboard |
X-SEALD-APIKEY (required) | String | API key, given on the Seald SDK administration dashboard |
Content-type | String | application/json |
To learn where you can get these, you can check the paragraph about it in the 2-man-rule integration guide.
Requests for 2-man rule protection
Sending a challenge
This API endpoint is called by the application server using the SDK, and allows to:
- Create a "user" in the sense of SSKS (if
create_user
is set totrue
); - Create a "Session";
- If an identity has previously been stored onto SSKS with this
auth_factor
(email address or phone number, see the Checkingmust_authenticate
paragraph for details on the comparaison), or ifforce_auth
is set totrue
, thenmust_authenticate
is set totrue
; otherwise, tofalse
; - Return a
session_id
andmust_authenticate
; - If
must_authenticate
istrue
, send achallenge
to the user, by email or SMS, the backend must not have access to thischallenge
. Thechallenge
is valid for 6h.
The backend must return the session_id
and must_authenticate
to the frontend, as explained in the 2-man-rule integration guide.
TIP
Sending challenges by SMS is limited to the SMS quota authorized for your team. If the quota is exceeded, your request will receive an error 406 Not Acceptable: {"detail": "SMSQuotaFailed"}
.
Do not hesitate to contact us to increase the SMS quota for your team.
URL
POST https://{SSKS_URL}/tmr/back/challenge_send/
Format of the request body
This is a POST
request in JSON
format:
Variable | Type | Description |
---|---|---|
create_user (default: false) | Boolean | If true , creates a user if it does not exist. Otherwise rejects with a 404 error. If unused, the application must manually create a user |
user_id (required) | String | Unique identifier of the user. Can be an ID, a UUID, an email address or any string identifier unique to this user in the context of your application. The data is stored in clear text on SSKS. |
auth_factor (required) | Object | Authentication method of this user. Used to send the challenge . Can be the same for multiple users as long as the user_id is unique. |
auth_factor.type (required) | String | Type of authentication factor. "EM" for email address, "SMS" for phone number. |
auth_factor.value (required) | String | User's email address or phone number. Used to send the challenge . Must be normalized. It is not stored in clear text on SSKS, check the Storage of authFactors paragraph for details. |
email (deprecated) | String | Deprecated, use auth_factor . User's email address. |
force_auth (default: false) | Boolean | This forces the sending of an authentication challenge, even if this auth_factor has never been used before. |
template (required) | String | Template in HTML format of the email sent to the user containing the authentication challenge. Must contain the String $$CHALLENGE$$ which will be replaced by the challenge by SSKS. |
subject | String | Subject line of the email containing the challenge. Defaults to "End-to-end encryption challenge" . Ignored in case of SMS. |
fake_otp (default: false) | Boolean | Only in test / staging environment. If true , email or SMS won't be sent. The challenge will always be aaaaaaaa . If true in production environment, a 406 error will be thrown. |
TIP
For sending SMS to United-States phone numbers, a template pre-checking may be required.
Please contact us in order to set it up.
Format of the response
In JSON
format:
Variable | Type | Description |
---|---|---|
session_id | String | ID of the newly created session. Must be transmitted to the frontend. |
must_authenticate | Boolean | Boolean indicating if the newly created session needs a challenge . If true , the server sends an email to the user, containing this challenge , which they will need to repeat in the frontend in the next 6h. If false , the session can directly save the identity on SSKS without a challenge . |
task_id | String | If the challenge takes less than 3s to be sent to the user, this field will be None . Otherwise, task_id will be a String, to be used with /tmr/back/check_task/ to follow-up with sending status. |
Checking task_id
status
This API endpoint can be used by the server when sending a challenge takes more than 3 seconds.
In that case, /tmr/back/challenge_send/
should have returned a task_id
that can be used in this API endpoint.
TIP
Tasks are deleted 24h after success. In that case, checking an old, succeeded task might not return a SUCCESS status.
URL
POST https://{SSKS_URL}/tmr/back/check_task/
Format of the request body
This is a POST
request in JSON
format:
Variable | Type | Description |
---|---|---|
task_id | String | Value of the task_id given in /tmr/back/challenge_send/ . |
Format of the response
In JSON
format:
Variable | Type | Description |
---|---|---|
status | String | SUCCESS when the challenge is sent to the user. FAILURE or REVOKED when sending the challenge has failed. Other when the task is being processed. |
Checking must_authenticate
This API endpoint can be used by the server of the app that uses the SDK before sending a challenge to check what value must_authenticate
would take if the send challenge endpoint were called.
It allows checking if an identity has already been stored for a given auth_factor
.
TIP
To decide the value of must_authenticate
, the SSKS server does not simply use the raw authentication factor given by the request to check whether an identity has previously been stored with it or not. It also tries to remove any aliases potentially present.
For example, for Gmail email addresses, anything after the +
is ignored: email@gmail.com
and email+alias@gmail.com
are considered equivalent.
The exact procedure used to do this is subject to be changed and improved.
URL
POST https://{SSKS_URL}/tmr/back/must_authenticate/
Format of the request body
This is a POST
request in JSON
format:
Variable | Type | Description |
---|---|---|
type (required) | String | Type of authentication factor. "EM" for email address, "SMS" for phone number. |
value (required) | String | User's email address or phone number. Must be normalized. |
Format of the response
In JSON
format:
Variable | Type | Description |
---|---|---|
must_authenticate | Boolean | Boolean indicating if the newly created session needs a challenge . If true , the server would send an email or SMS to the user if the send_challenge endpoint were to be used. |
Manual creation of a user
This API point can be used if create_user
is set to False
on the previous API point. This will just create a blank "user" in the sense of SSKS.
URL
POST https://{SSKS_URL}/tmr/back/create_user/
Format of the request body
Variable | Type | Description |
---|---|---|
user_id (required) | String | Unique identifier of the user. Can be an ID, a UUID, an email address or any string identifier unique to this user in the context of your application. The data is stored in clear text on SSKS. |
auth_factor (required) | Object | Authentication method of this user. Used to send the challenge . |
auth_factor.type (required) | String | Type of authentication factor. 'EM' for email address, 'SMS' for phone number. |
auth_factor.value (required) | String | User's email address or phone number. Must be normalized. It is not stored in clear text on SSKS, check the Storage of authFactors paragraph for details. |
email (deprecated) | String | Deprecated, use auth_factor . User's email address. |
Response
This API point does not have any information to return to the server. It simply responds with {"status": "ok"}
.
Deletion of a user
This API point can be used to delete all data stored on a user on SSKS.
URL
POST https://{SSKS_URL}/tmr/back/delete_user/
Format of the request body
Variable | Type | Description |
---|---|---|
user_id (required) | String | Unique identifier that was used to create the user to delete. |
auth_factor (optional) | Object | Object containing information regarding user authentication. If used, only the user with corresponding auth_factor will be deleted. (It can be useful is there are multiple users with the same user_id ) |
auth_factor.type (required if auth_factor is used) | String | Type of authentication factor. 'EM' for email address, 'SMS' for phone number. |
auth_factor.value (required if auth_factor is used) | String | User's email address or phone number. Must be normalized. |
full_forget (optional) | Boolean | Only in test / staging environment. If true , completely delete the user, to avoid must_authenticate being true when storing another identity with the same factor. If true in production environment, a 406 error will be thrown. |
Response
Variable | Type | Description |
---|---|---|
status | String | ok if the request performed correctly |
deleted | Number | Number of identities deleted |
Checking an identity exists
This API point can be used to check if a given user_id
(possibly with a specific auth_factor
) has a 2-man-rule protected identity.
It responds with the number of identities corresponding to that user, either 0
when there is none or the user does not exists, or a stricly positive integer if there is one or more.
It is also possible to check if there is an identity stored for a user_id
without checking the auth_factor
it is associated with.
URL
POST https://{SSKS_URL}/tmr/back/identity_check/
Format of the request body
Variable | Type | Description |
---|---|---|
user_id (required) | String | Unique identifier that was used to create the user to check. |
auth_factor (optional) | Object | Authentication method of this user. Used to send the challenge . |
auth_factor.type (required if auth_factor.value is given) | String | Type of authentication factor. 'EM' for email address, 'SMS' for phone number. |
auth_factor.value (required if auth_factor.type is given) | String | User's email address or phone number. Must be normalized. |
Response
Variable | Type | Description |
---|---|---|
identities_count | Number | Number of identities stored for this user_id , maybe filtered with the auth_factor . |
user | Object | Data about the requested user |
user.user_id | Object | user_id given in the request |
user.app_id | Object | app_id of the application for Seald |
Retrieve identities
This API point can be used to retrieve information regarding stored identities.
URL
GET https://{SSKS_URL}/tmr/back/identities/?user_id={user_id}&id={id}&cursor={cursor}
Format of request parameters
Variable | Type | Description |
---|---|---|
cursor | String | To be used in order to retrieve previous or next results in case of many results. |
user_id | String | Unique identifier used to create the user. |
id | String | Unique identifier returned when creating the user. |
Response
This API point returns the following information in a paginated way.
Variable | Type | Description |
---|---|---|
next_cursor | String | Value to use in cursor in order to retrieve next results. |
previous_cursor | String | Value to use in cursor in order to retrieve previous results. |
results | String | Results (see below). |
result
follows this schema :
Variable | Type | Description |
---|---|---|
id | String | Unique identifier returned when creating the user. |
app_id | String | app_id of the application for Seald. |
created | String | Identity date of creation |
user_id | String | Unique identifier used to create the user. |
auth_factor_type | String | Type of authentication factor. 'EM' for email address, 'SMS' for phone number. |
hash_converted | Boolean | True if the authentication factor value has been converted once. |
hash_v2_converted | Boolean | True if the authentication factor value has been converted twice. |
Deleting an identity (2)
This API endpoint can be used to remove the identity corresponding to a given id
, or all identities stored for a given user_id
.
URL
DELETE https://{SSKS_URL}/tmr/back/identities/?user_id={user_id}&id={id}
Format of request parameters
Variable | Type | Description |
---|---|---|
user_id (required*) | String | Unique identifier used to create the user. |
id (required*) | String | Unique identifier returned when creating the user. |
* One of user_id
or id
is required, but not both. Using only one of those parameters is enough.
Response
This API point does not have any information to return to the server. It simply responds with {"status": "ok"}
.
Normalization of authFactors
When using SSKS for 2-man-rule protection, all authFactors
sent to SSKS must be normalized. If an authFactor
is sent in a non-normalized manner the SSKS server may return an error.
WARNING
For the time being, non-normalized authFactor
s are still accepted, and are normalized internally by the server.
However, they are deprecated, and will be rejected in a few months.
Email normalization
Email addresses are normalized by:
- normalizing the string encoding to NFKC
- removing spaces
- making the string lowercase
To do this normalization, you can use the following snippets:
- in Python:
import unicodedata
normalized_email = unicodedata.normalize("NFKC", email).replace(" ", "").lower()
- in JavaScript :
const normalizedEmail = email.normalize('NFKC').replace(/ /g, '').toLowerCase()
Normalizing phone numbers
For phone numbers, they must be sent in international standard format E.164.
This is simply the phone number, without any spaces, preceded by the country code noted with a +
.
For example, the following notations are NOT valid:
01 23 45 67 89
0123456789
+33 1 23 45 67 89
+33-123456789
0033123456789
Use the following notation:
+33123456789
To convert any phone number to E.164. format, libraries exist in different languages. For example, to convert a number to E.164 considering that we are in France:
- in Python, you can use
phonenumbers
:
import phonenumbers
phonenumbers.format_number(phonenumbers.parse("01 23 45 67 89", "FR"), phonenumbers.PhoneNumberFormat.E164)
# '+33123456789'
- in JavaScript, you can use
google-libphonenumber
:
import libPhoneNumber from 'google-libphonenumber'
const instance = libPhoneNumber.PhoneNumberUtil.getInstance()
instance.format(instance.parse('01 23 45 67 89', 'FR'), libPhoneNumber.PhoneNumberFormat.E164)
// '+33123456789'
Storage of authFactors
The value of authentication factors is not stored in clear text on SSKS. It is first normalized, then hashed. The hash is stored so that it can be verified that the value given is the same when used later.
Also, a de-aliased version is computed, then hashed and stored, so that the SSKS can decide the value of must_authenticate
for later requests.
Finally, the auth_factor
is also encrypted for a key that the server does not possess, and this encrypted version is stored, so that Seald is able to, if necessary in the future, migrate the hashing procedures.
Migration
As of 01/31/2022, the hash function has been updated from v1 to v2 to improve performance by reducing the number of hash iterations.
As of 01/01/2023, the hash function has been updated from v2 to v3 by introducing prior normalization, as well as a de-aliasing mechanism to ensure that a change in case or encoding of the authFactor
is not treated as a different authFactor
, and that the use of common email aliases does not circumvent authentication.
Since the value of authFactor
for which an encrypted identity has been stored in v1 or v2 is not accessible by our teams, we cannot compute the v3 hash for them. Therefore, a migration mechanism has been implemented:
When a request containing an authFactor
(POST /tmr/back/challenge_send/
, POST /tmr/back/must_authenticate/
, POST /tmr/back/delete_user/
, POST /tmr/back/identity_check/
), SSKS computes the v3 hash, the v2 hash, and the v1 hash, then:
- if the
authFactor
has no match on the v3 hash, but has one on the v1 or v2 hash, theauthFactor
is migrated to the v3 hash and the old hash is deleted. - if the
authFactor
has a match on the v3 hash and has a match on the v1 or v2 hash, a normalization-related collision has occurred which notifies the Seald teams and responds to the client with a 500 error. In this case, the collision must be handled manually, please contact us at support@seald.io. - Otherwise, the
authFactor
has a match on the v3 hash, and not on the v1 and v2 hash, which is the normal operation.
DANGER
It is crucial to perform the migration with the raw, unnormalized value of the authFactor
, as given when storing the user's identity. Otherwise, the server would not be able to find the match with the old v1 hash or v2 hash.
Once the migration of your app is completed, it is recommended to send only the normalized version of the authFactor
.
TIP
Once all users of an application have been migrated to a hash version (v2 or v3), the hashes of previous versions are no longer calculated. In particular, the performance improvement linked to the introduction of the v2 hash is only effective once all users have been migrated to the v2 hash at least.
Perform migration
To perform the migration on all users, you must:
- For each user in your application:
- For each
authFactor
of that user:- Make an authenticated call to
POST /tmr/back/must_authenticate/
with parameters{ "type": <"EM"|"SMS">, "value": <value> }
. Please note: you must send the raw, unnormalized value of theauthFactor
as given when storing the user's identity for the migration to work.
- Make an authenticated call to
- For each
Once done, all known users in your application have been migrated.
You may have other unused identities in your application that should be deleted. To confirm that your migration is complete, please contact us at support@seald.io.
Once you have confirmed with Seald that all users have been migrated, you can normalize the authFactor
registered in your application.
Requests for password protection
Checking an identity exists
This API endpoint can be used to check if a given user_id
has one or more identities stored protected with a passsword.
It responds with identities_count
which represents how many identities there are for this user. It can either be 0
if there are none, or a strictly positive integer.
URL
POST https://{SSKS_URL}/strict/back/identity_check/
Format of the request body
Variable | Type | Description |
---|---|---|
user_id (required) | String | Unique identifier that was used to create the user to check. |
Response
Variable | Type | Description |
---|---|---|
identities_count | Number | Number of identities stored for this user_id . |
user | Object | Data about the requested user |
user.user_id | Object | user_id given in the request |
user.app_id | Object | app_id of the application for Seald |
Deleting an identity
This API endpoint can be used to remove all identities stored for a given user_id
.
It is not possible to distinguish different identities of a user. Therefore, it is only possible to remove them jointly.
URL
POST https://{SSKS_URL}/strict/back/identity_delete/
Format of the request body
Variable | Type | Description |
---|---|---|
user_id (required) | String | Unique identifier that was used to create the user to delete. |
Response
This API point does not have any information to return to the server. It simply responds with {"status": "ok"}
.
Retrieve identities
This API point can be used to retrieve information regarding stored identities.
URL
GET https://{SSKS_URL}/strict/back/identities/?user_id={user_id}&id={id}&cursor={cursor}
Format of request parameters
Variable | Type | Description |
---|---|---|
cursor | String | To be used in order to retrieve previous or next results in cas of many results. |
user_id | String | Unique identifier used to create the user. |
id | String | Unique identifier returned when creating the user. |
Response
This API point returns the following information in a paginated way.
Variable | Type | Description |
---|---|---|
next_cursor | String | Value to use in cursor in order to retrieve next results. |
previous_cursor | String | Value to use in cursor in order to retrieve previous results. |
results | String | Results (see below). |
result
follows this schema :
Variable | Type | Description |
---|---|---|
id | String | Unique identifier returned when creating the user. |
app_id | String | app_id of the application for Seald. |
created | String | Identity date of creation. |
user_id | String | Unique identifier used to create the user. |
Deleting an identity (2)
This API endpoint can be used to remove the identity corresponding to a given id
, or all identities stored for a given user_id
.
URL
DELETE https://{SSKS_URL}/strict/back/identities/?user_id={user_id}&id={id}
Format of request parameters
Variable | Type | Description |
---|---|---|
user_id (required*) | String | Unique identifier used to create the user. |
id (required*) | String | Unique identifier returned when creating the user. |
* One of user_id
or id
is required, but not both. Using only one of those parameters is enough.
Response
This API point does not have any information to return to the server. It simply responds with {"status": "ok"}
.