Initialize the SDK
Introduction​
The CardinalSdk
interface is the access point to all the features of Cardinal.
The initialization of the SDK requires that you provide some mandatory parameters such as the user authentication
details, and some optional additional configurations that you can provide through the SdkOptions
parameter.
- Kotlin
- Typescript
- Python
- Dart
import com.icure.cardinal.sdk.CardinalSdk
import com.icure.cardinal.sdk.auth.UsernamePassword
import com.icure.cardinal.sdk.options.AuthenticationMethod
import com.icure.cardinal.sdk.options.EncryptedFieldsConfiguration
import com.icure.cardinal.sdk.options.SdkOptions
import com.icure.cardinal.sdk.storage.impl.FileStorageFacade
suspend fun initializeMySdk(username: String, password: String) =
CardinalSdk.initialize(
"com.mycompany.mycardinalapp",
"https://api.icure.cloud",
AuthenticationMethod.UsingCredentials(UsernamePassword(username, password)),
FileStorageFacade("/path/to/storage/directory"),
SdkOptions(
encryptedFields = EncryptedFieldsConfiguration(
patient = setOf("notes", "addresses")
)
)
)
import {AuthenticationMethod, CardinalSdk, StorageFacade} from "@icure/cardinal-sdk";
function initializeMySdk(username: string, password: string): Promise<CardinalSdk> {
return CardinalSdk.initialize(
"com.mycompany.mycardinalapp",
"https://api.icure.cloud",
new AuthenticationMethod.UsingCredentials.UsernamePassword(username, password),
StorageFacade.usingFileSystem("/path/to/storage/directory"),
{
encryptedFields: {
patient: ["notes", "addresses"]
}
}
)
}
from cardinal_sdk import CardinalSdk
from cardinal_sdk.authentication import UsernamePassword
from cardinal_sdk.storage import FileSystemStorage
from cardinal_sdk.options import SdkOptions, EncryptedFieldsConfiguration
sdk = CardinalSdk(
"com.mycompany.mycardinalapp",
"https://api.icure.cloud",
UsernamePassword("username", "password"),
FileSystemStorage("/path/to/storage/directory"),
SdkOptions(
encrypted_fields=EncryptedFieldsConfiguration(
patient=["notes", "addresses"]
)
)
)
import 'package:cardinal_sdk/auth/authentication_method.dart';
import 'package:cardinal_sdk/auth/credentials.dart';
import 'package:cardinal_sdk/cardinal_sdk.dart';
import 'package:cardinal_sdk/options/sdk_options.dart';
import 'package:cardinal_sdk/options/storage_options.dart';
Future<CardinalSdk> initializeMySdk(String username, String password) {
return CardinalSdk.initialize(
"com.mycompany.mycardinalapp",
"https://api.icure.cloud",
AuthenticationMethod.UsingCredentials(Credentials.UsernamePassword(username, password)),
StorageOptions.PlatformDefault,
options: SdkOptions(
encryptedFields: EncryptedFieldsConfiguration(
patient: {"notes", "addresses"}
)
)
);
}
The SDK initialization process will automatically log in the user to the Cardinal backend and load or initialize his cryptographic keys.
Application id​
The application id is used to isolate different applications. If the authentication method of a user allows accessing different databases, only those matching the provided application id will be considered.
You can configure the application id for your solution through the cockpit, and different solutions can use the same application ids.
We recommend using reverse domain name notation for application ids (for example com.mycompany.myapp
).
You can configure the application id for your
Backend URL​
You can access the Cardinal backend through multiple urls, which follow different release schedules.
Authentication method​
There are three main authentication methods supported by the Cardinal SDK: using credentials provided to the SDK at initialization time, using an authentication process, or using a custom secret provider.
During initialization, the SDK will use the provided authentication method to login the user and request JSON web tokens which will be used to authenticate the user for the future requests.
You don't need to worry about managing the login and JWTs of the user, you only need to provide the authentication method for the api initialization. The SDK will automatically manage the JWT and refresh them as needed.
Authentication with initialization credentials​
You can provide different types of user credentials:
- The username/email/phone and user-chosen password
- The username/email/phone and a temporary login token generated by the Cardinal backend
- A supported third party authentication token
Username + password​
- Kotlin
- Typescript
- Python
- Dart
import com.icure.cardinal.sdk.auth.UsernamePassword
import com.icure.cardinal.sdk.options.AuthenticationMethod
val auth = AuthenticationMethod.UsingCredentials(UsernamePassword("username", "password"))
import {AuthenticationMethod} from "@icure/cardinal-sdk";
const auth = new AuthenticationMethod.UsingCredentials.UsernamePassword("username", "password")
from cardinal_sdk.authentication import UsernamePassword
auth = UsernamePassword("username", "password")
import 'package:cardinal_sdk/auth/authentication_method.dart';
import 'package:cardinal_sdk/auth/credentials.dart';
final auth = AuthenticationMethod.UsingCredentials(Credentials.UsernamePassword("username", "password"));
Username + login token​
You can generate login token through the cockpit or through the Cardinal SDK itself (🚧). The lifespan of a login token is configurable, and there may be multiple login tokens associated with a user. Login tokens can be revoked, but any JWTs generated through that login token won't be automatically revoked.
- Kotlin
- Typescript
- Python
- Dart
import com.icure.cardinal.sdk.auth.UsernameLongToken
import com.icure.cardinal.sdk.options.AuthenticationMethod
val auth = AuthenticationMethod.UsingCredentials(UsernameLongToken("username", "token"))
import {AuthenticationMethod} from "@icure/cardinal-sdk";
const auth = new AuthenticationMethod.UsingCredentials.UsernameLongToken("username", "token")
from cardinal_sdk.authentication import UsernameLongToken
auth = UsernameLongToken("username", "token")
import 'package:cardinal_sdk/auth/authentication_method.dart';
import 'package:cardinal_sdk/auth/credentials.dart';
final auth = AuthenticationMethod.UsingCredentials(Credentials.UsernameLongToken("username", "token"));
Third party authentication​
- Kotlin
- Typescript
- Python
- Dart
import com.icure.cardinal.sdk.auth.ThirdPartyAuthentication
import com.icure.cardinal.sdk.auth.ThirdPartyProvider
import com.icure.cardinal.sdk.options.AuthenticationMethod
val auth = AuthenticationMethod.UsingCredentials(ThirdPartyAuthentication("google-token", ThirdPartyProvider.GOOGLE))
import {AuthenticationMethod, ThirdPartyProvider} from "@icure/cardinal-sdk";
const auth = new AuthenticationMethod.UsingCredentials.ThirdPartyAuth("google-token", ThirdPartyProvider.GOOGLE)
from cardinal_sdk.authentication import ThirdPartyAuthentication, ThirdPartyProvider
auth = ThirdPartyAuthentication("google-token", ThirdPartyProvider.GOOGLE)
import 'package:cardinal_sdk/auth/authentication_method.dart';
import 'package:cardinal_sdk/auth/credentials.dart';
final auth = AuthenticationMethod.UsingCredentials(
Credentials.ThirdPartyAuthentication("google-token", ThirdPartyProvider.google)
);
Authentication with a process​
You can use authentication processes to authenticate users using one-time tokens sent via email or SMS.
In this case authentication is done in two steps: the first step will generate a one-time token and send it to the user via email or sms, then, once the user provides the received token, the second step will actually complete the authentication and initialize the SDK.
To initialize the SDK with an authentication process, you need to use a different initialization method which also requires some additional parameters.
- Kotlin
- Typescript
- Python
- Dart
import com.icure.cardinal.sdk.CardinalSdk
import com.icure.cardinal.sdk.auth.AuthenticationProcessTelecomType
import com.icure.cardinal.sdk.auth.AuthenticationProcessTemplateParameters
import com.icure.cardinal.sdk.auth.CaptchaOptions
import com.icure.cardinal.sdk.options.EncryptedFieldsConfiguration
import com.icure.cardinal.sdk.options.SdkOptions
import com.icure.cardinal.sdk.storage.impl.FileStorageFacade
suspend fun initializeMySdk(
userEmail: String
): CardinalSdk {
// The authentication with a process uses a different method, with additional parameters.
val authenticationStep = CardinalSdk.initializeWithProcess(
"com.mycompany.mycardinalapp",
"https://api.icure.cloud",
"https://msg-gw.icure.cloud/",
// Retrieve this from the cockpit, constant for your application.
specId,
// Retrieve this from the cockpit, constant for your application.
processId,
AuthenticationProcessTelecomType.Email,
userEmail,
CaptchaOptions.Kerberus.Delegated { progress -> println("Progress: ${progress * 100}%") },
FileStorageFacade("/path/to/storage/directory"),
// Process template parameters are additional parameters which
// may be used by the SMS/email template to the user.
// If you are not using them in your template you can omit them.
AuthenticationProcessTemplateParameters(),
SdkOptions(
encryptedFields = EncryptedFieldsConfiguration(
patient = setOf("notes", "addresses")
)
)
)
// The authentication is not yet complete.
// The user will receive a mail with the validation code required to proceed.
val validationCode = askValidationCode()
return authenticationStep.completeAuthentication(validationCode)
}
import {
AuthenticationProcessTelecomType,
CardinalSdk,
StorageFacade,
CaptchaOptions
} from "@icure/cardinal-sdk";
async function initializeMySdk(
userEmail: string
): Promise<CardinalSdk> {
// The authentication with a process uses a different method, with additional parameters.
const authenticationStep = await CardinalSdk.initializeWithProcess(
"com.mycompany.mycardinalapp",
"https://api.icure.cloud",
"https://msg-gw.icure.cloud/",
// Retrieve this from the cockpit, constant for your application.
specId,
// Retrieve this from the cockpit, constant for your application.
processId,
AuthenticationProcessTelecomType.Email,
userEmail,
new CaptchaOptions.Kerberus.Delegated({onProgress: (x) => console.log('Progress', x)}),
StorageFacade.usingFileSystem("/path/to/storage/directory"),
// Process template parameters are additional parameters which
// may be used by the SMS/email template to the user.
// If you are not using them in your template you can omit them.
{},
{
encryptedFields: {
patient: ["notes", "addresses"]
}
}
)
// The authentication is not yet complete.
// The user will receive a mail with the validation code required to proceed.
const validationCode = await askValidationCode()
return authenticationStep.completeAuthentication(validationCode)
}
This authentication method is unavailable on the Cardinal python SDK
import 'package:cardinal_sdk/auth/authentication_process_telecom_type.dart';
import 'package:cardinal_sdk/auth/authentication_process_template_parameters.dart';
import 'package:cardinal_sdk/auth/captcha_options.dart';
import 'package:cardinal_sdk/cardinal_sdk.dart';
import 'package:cardinal_sdk/options/sdk_options.dart';
import 'package:cardinal_sdk/options/storage_options.dart';
Future<CardinalSdk> initializeMySdk(
String userEmail,
String captchaToken
) async {
// The authentication with a process uses a different method, with additional parameters.
final authenticationStep = await CardinalSdk.initializeWithProcess(
"com.mycompany.mycardinalapp",
"https://api.icure.cloud",
"https://msg-gw.icure.cloud/",
// Retrieve this from the cockpit, constant for your application.
specId,
// Retrieve this from the cockpit, constant for your application.
processId,
AuthenticationProcessTelecomType.email,
userEmail,
CaptchaOptions.KerberusDelegated(),
StorageOptions.PlatformDefault,
// Process template parameters are additional parameters which
// may be used by the SMS/email template to the user.
// If you are not using them in your template you can omit them.
authenticationProcessTemplateParameters: const AuthenticationProcessTemplateParameters(),
options: SdkOptions(
encryptedFields: EncryptedFieldsConfiguration(
patient: { "notes", "addresses" }
)
)
);
// The authentication is not yet complete.
// The user will receive a mail with the validation code required to proceed.
final validationCode = await askValidationCode();
return await authenticationStep.completeAuthentication(validationCode);
}
Message gateway URL​
The message gateway is the component of Cardinal backend that generates one-time tokens and sends them to the users.
This is a separate component from the main backend, and therefore you also need to provide its URL.
Currently, there is only one deployed message gateway which is available at https://msg-gw.icure.cloud
specId
and processId
​
The specId
is unique to your organization and allows the message gateway to connect to the external services linked to
your organization, which are used for example to send SMS to the users.
The processId
instead links to the type of authentication process and respective configuration.
You may have different types of processes, for example, some processes may only be used for the login of existing users
via email or phone, but others may also allow to register patient and healthcare party users.
The configuration of the process always includes the email/SMS template, but may also include other parameters depending
on the type of process.
The type of process you're using also determines whether you have to pass the email or phone number of the user.
You can get the specId
and processId
from the cockpit.
For a quick start, we suggest that you use the demo setup during the cockpit onboarding,
but you can also configure your services and custom processes.
Captcha​
To prevent abuse of the messaging system when using the authentication with a process also requires that you provide a captcha.
Currently, the Cardinal SDK supports reCAPTCHA v3, FriendlyCaptcha and Kerberus. You can configure which service to use through the cockpit, but if you're using the demo setup you will need to use Kerberus.
Refer to the documentation here to learn how to integrate them in your application.
Authentication with secret provider​
The last authentication method supported by the SDK is based on a secret provider.
A secret provider is essentially a callback that the SDK can use when it needs to get an authentication secret of the user. Implementing a secret provider requires more effort than providing initialization credentials; however, the authentication with a secret provider is much more flexible when the user needs to perform sensitive operations.
In Cardinal SDK some operations such as the creation of a login token are considered sensitive and can only be performed if the user logged in using a certain category of secrets (configurable for each group). By default, if the user logged in using any secret other than long-lived authentication tokens the api will be allowed to perform sensitive operations. This is because long-lived authentication tokens are considered the least secure authentication secret because they're often cached (for example, saved in the browser local storage) to implement mechanisms such as "remember me".
This means that if you initialize the SDK with long-lived token credentials, the SDK instance can't perform any sensitive operation. Using a secret provider, instead, you can perform the initial login using a long-lived token then, if later you need to perform a sensitive operation, the SDK will automatically ask the secret provider for a better secret. Your secret provider implementation, in turn, may prompt the user for his password and then return it to the SDK.
The following code provides an example implementation of a authentication secret provider that uses a long-lived token when possible and as fallback asks the user for its password. If you want more information on authentication with a secret provider refer to the dedicated documentation page
- Kotlin
- Typescript
- Python
- Dart
import com.icure.cardinal.sdk.auth.AuthSecretDetails
import com.icure.cardinal.sdk.auth.AuthSecretProvider
import com.icure.cardinal.sdk.auth.AuthenticationProcessApi
import com.icure.cardinal.sdk.model.embed.AuthenticationClass
import com.icure.cardinal.sdk.options.AuthenticationMethod
val auth = AuthenticationMethod.UsingSecretProvider(
loginUsername = username,
secretProvider = object : AuthSecretProvider {
override suspend fun getSecret(
acceptedSecrets: Set<AuthenticationClass>,
previousAttempts: List<AuthSecretDetails>,
authProcessApi: AuthenticationProcessApi
): AuthSecretDetails {
// If we can use a secret that doesn't require user interaction we prioritize using that.
if (AuthenticationClass.LongLivedToken in acceptedSecrets) {
val cachedToken = getCachedToken()
if (cachedToken != null && previousAttempts.none { it.secret == cachedToken }) {
return AuthSecretDetails.LongLivedTokenDetails(cachedToken)
}
}
// If the SDK is performing a sensitive operation and the long lived token is not sufficient
// we will provide the password
if (AuthenticationClass.Password in acceptedSecrets) {
return AuthSecretDetails.PasswordDetails(askUserPassword())
}
// If our group uses the default configuration, the password will always be an accepted secret
// We can throw an exception if for some reason that is not the case.
throw UnsupportedOperationException(
"This secret provider only support password and long lived tokens."
)
}
}
)
import {
AuthenticationClass,
AuthenticationMethod,
AuthenticationProcessApi,
AuthSecretDetails
} from "@icure/cardinal-sdk";
const auth = new AuthenticationMethod.UsingSecretProvider(
{
async getSecret(
acceptedSecrets: Array<AuthenticationClass>,
previousAttempts: Array<AuthSecretDetails>,
authProcessApi: AuthenticationProcessApi
): Promise<AuthSecretDetails> {
// If we can use a secret that doesn't require user interaction we prioritize using that.
if (AuthenticationClass.LongLivedToken in acceptedSecrets) {
const cachedToken = getCachedToken()
if (cachedToken != null && previousAttempts.every((x) => x.secret != cachedToken) {
return new AuthSecretDetails.LongLivedTokenDetails(cachedToken)
}
}
// If the SDK is performing a sensitive operation and the long lived token is not sufficient
// we will provide the password
if (AuthenticationClass.Password in acceptedSecrets) {
return new AuthSecretDetails.PasswordDetails(await askUserPassword())
}
// If our group uses the default configuration, the password will always be an accepted secret
// We can throw an exception if for some reason that is not the case.
throw Error(
"This secret provider only support password and long lived tokens."
)
}
},
{ loginUsername: username }
)
This authentication method is unavailable on the Cardinal python SDK
This authentication method is currently unavailable on the Cardinal dart SDK
Storage facade​
The Cardinal SDK needs access to some persistent storage solution to save the cryptographic keys of the user and some
associated metadata. When initializing the SDK you have to provide a StorageFacade
which interfaces the SDK with
the storage solution you want to use.
Each version of the SDK comes with one or more storage facade implementations, depending on the platform. For example, on the Cardinal Typescript SDK an implementation that uses the local storage (for browser environments), and another that stores the data as files on the filesystem (for node or other desktop js runtimes).
If none of the provided implementations included in the SDK suits your needs, you can always provide a custom implementation.
- Kotlin
- Typescript
- Python
- Dart
On the kotlin multiplatform SDK the storage solutions available depend on the actual platform being used.
From the common code you can access a FileStorageFacade
which uses the platform file system (not available on js
outside of node).
On kotlin/js the SDK also provides a storage implementation which uses the browser local storage.
import com.icure.cardinal.sdk.storage.StorageFacade
import com.icure.cardinal.sdk.storage.impl.FileStorageFacade
import com.icure.cardinal.sdk.storage.impl.LocalStorageStorageFacade
// Note: the file storage initialization on kotlin is suspend
suspend fun createFileStorage(): StorageFacade =
FileStorageFacade("/path/to/storage/directory")
// Local storage facade is available only on kotlin/js
fun createLocalStorage(): StorageFacade =
LocalStorageStorageFacade()
// Custom storage implementation
class MyVolatileStorage : StorageFacade {
private val storedData = mutableMapOf<String, String>()
override suspend fun getItem(key: String): String? {
return storedData[key]
}
override suspend fun setItem(key: String, value: String) {
storedData[key] = value
}
override suspend fun removeItem(key: String) {
storedData.remove(key)
}
}
On typescript you can use file-system storage when running on node, or local storage when running on browser.
On other platforms you will need to provide your own storage implementation.
If you are on react-native we provide a supplementary package a storage facade and other required components. Refer to the expo boilerplate for more information (🚧).
import {StorageFacade, CardinalStorageOptions} from "@icure/cardinal-sdk";
// On the typescript SDK the pre-implemented storage solutions are not available for use directly.
// Instead you have `CardinalStorageOptions` which will be used by the SDK initializer to instantiate the
// appropriate `StorageFacade`
// In all Cardinal SDK methods `StorageFacade` and `CardinalStorageOptions` are interchangeable.
// The file storage works only on node
const fileStorage: CardinalStorageOptions =
StorageFacade.usingFileSystem("/path/to/storage/directory")
// Local storage facade works only on browser
const localStorage: CardinalStorageOptions =
StorageFacade.usingBrowserLocalStorage()
// Custom storage implementation
class MyVolatileStorage implements StorageFacade {
private readonly data: { [key: string]: string } = {}
async getItem(key: string): Promise<string | undefined> {
return this.data[key]
}
async setItem(key: string, value: string): Promise<void> {
this.data[key] = value
}
async removeItem(key: string): Promise<void> {
delete this.data[key]
}
}
On python you can use file-system storage or provide your own implementation.
from typing import Optional
from cardinal_sdk.storage import FileSystemStorage, CustomStorageFacade, StorageOptions
fileStorage: StorageOptions = FileSystemStorage("/path/to/storage/directory")
class MyVolatileStorage(CustomStorageFacade):
data: dict[str, str] = {}
def get_item(self, key: str) -> Optional[str]:
return self.data.get(key)
def set_item(self, key: str, value: str):
self.data[key] = value
def remove_item(self, key: str):
self.data.pop(key, None)
customStorage: StorageOptions = MyVolatileStorage()
On dart currently the only storage option is the "platform default". This option uses the DataStore on Android and the UserDefaults on iOs.
The platform default will use local storage on web.
import 'package:cardinal_sdk/options/storage_options.dart';
final storageOptions = StorageOptions.PlatformDefault;
Optional parameters​
When initializing the SDK you can provide some optional configurations through the SdkOptions
parameter.
Not all optional configuration parameters are available on all platforms.
The parameters marked as occasionally required in the following sections are actually required if certain conditions are met, and the SDK initialization will fail if they aren't provided.
Refer to the content of the section to learn what are these conditions.
Encrypted fields configuration​
Encrypting data is always a trade-off between usability and security.
The parts of the data that the Cardinal SDK encrypts by default should be a good compromise between usability and
security for most applications.
However, the default configuration may not be optimal for your application.
If you want to change which parts of your user's data is encrypted, you can provide an EncryptedFieldsConfiguration
object at the SDK initialization time.
To learn more about the syntax of the encrypted fields configuration and for more tips on how to configure it you can refer to the dedicated documentation page
- Kotlin
- Typescript
- Python
- Dart
import com.icure.cardinal.sdk.options.EncryptedFieldsConfiguration
import com.icure.cardinal.sdk.options.SdkOptions
val options = SdkOptions(
encryptedFields = EncryptedFieldsConfiguration(
patient = setOf("notes", "addresses")
)
)
import {SdkOptions} from "@icure/cardinal-sdk";
const options: SdkOptions = {
encryptedFields: {
patient: ["notes", "addresses"]
}
}
from cardinal_sdk.options import SdkOptions, EncryptedFieldsConfiguration
options = SdkOptions(
encrypted_fields=EncryptedFieldsConfiguration(
patient=["notes", "addresses"]
)
)
import 'package:cardinal_sdk/options/sdk_options.dart';
final options = SdkOptions(
encryptedFields: EncryptedFieldsConfiguration(
patient: { "notes", "addresses" }
)
);
Key storage​
By default, the cryptographic keys of the user are exported to pkcs8/spki, encoded, and then stored using the storage
facade provided through the mandatory parameters. However, you can also customize the key storage solution, by providing
an implementation of KeyStorageFacade
.
Like for the standard StorageFacade
the SDK comes with some implementations of KeyStorageFacade
but you can also
implement your own.
The implementations currently provided by the SDK are wrappers around a standard storage facade that use different
encoding solutions for the keys (for example, encoding the key as a JsonWebKey then storing it using a StorageFacade
).
Custom key storage facades aren't yet supported on the python SDK.
- Kotlin
- Typescript
- Dart
import com.icure.cardinal.sdk.storage.impl.FileStorageFacade
import com.icure.cardinal.sdk.storage.impl.JsonAndBase64KeyStorage
import com.icure.cardinal.sdk.storage.impl.JwkKeyStorage
// This is the same encoding method as the default, but you can use a different underlying storage facade,
// which for example, could store the key files at a different location from everything else
suspend fun base64EncodedKeyStorage() =
JsonAndBase64KeyStorage(FileStorageFacade("/path/to/key/storage"))
suspend fun jwkEncodedKeyStorage() =
JwkKeyStorage(FileStorageFacade("/path/to/key/storage"))
import {KeyStorageFacade, StorageFacade} from "@icure/cardinal-sdk";
// This is the same encoding method as the default, but you can use a different underlying storage facade,
// which for example, could store the key files at a different location from everything else
const base64EncodedKeyStorage =
KeyStorageFacade.encodingAsBase64(StorageFacade.usingFileSystem("/path/to/key/storage"))
const jwkEncodedKeyStorage =
KeyStorageFacade.encodingAsJwk(StorageFacade.usingFileSystem("/path/to/key/storage"))
Custom key storage facades aren't supported on the dart SDK.
Group selector (occasionally required)​
if you don't know what groups and applications are in the Cardinal ecosystem you should read the dedicated documentation page (🚧).
If your application uses a multi-group structure, you may have the same person or organization may have multiple users in different groups, potentially with different roles and different cryptographic keys. In some situations, the authentication method provided to the SDK may be valid for the login of multiple users in different groups. This can happen, for example, when the user uses a third party authentication provider to login.
An instance of the Cardinal SDK, however, is bound to a specific instance of user in a group.
In cases where the chosen authentication method allows the user to access multiple groups you need to provide a groupSelector
which tells the SDK in which group the user should login.
The implementation of this group selector depends on the needs and intended use of your application. For example, assume you're developing an application for GPs and you have a group for each practice. In some cases, the same GP may work in multiple practices, and therefore exist in multiple groups. If your application is going to be installed on laptops and the doctor can use it in both clinics you should have your group selector interact with the user and ask in which group he wants to login to. However, if your application is going to be installed only on desktop computers, you may have a configuration file that specifies the id of the group that the installation should use.
This group selector is used only during the initialization of the SDK.
You can also change the group of an initialized SDK at a later moment using the switchGroup
method on the SDK instance.
Client-side password hashing​
The Cardinal SDK hashes the password of the user before sending it to the backend (in addition to salting on the backend before storing the passwords). This ensures that the clear password of the user is never available to us.
The client-side hashing is done using the application id as a salt.
If you want to disable this behavior set the saltPasswordWithApplicationId
property of the SdkOptions
to false.
Cryptography configurations​
You can configure the way the Cardinal SDK does some of its cryptographic operations. For example, you can implement custom key recovery solutions. You can also reduce the trust you put on the Cardinal backend by providing a custom key verification method, to make sure that the users are sharing data with who they intend.
These topics are a bit advanced, but we recommend that you read about them after you've familiarized with the cardinal SDK. The parameters that allow you to configure the cryptographic operations of the Cardinal SDK are:
cryptoStrategies
: allows you to provide custom key generation, recovery and verification solutions (🚧).createTransferKeys
: allows you to disable the automatic creation of information that allows to recover newly created keys using previously existing lost but verified keys of the user (🚧).useHierarchicalDataOwners
: enables the hierarchical data owners key management when set to true. The SDK will expect to have keys for the data owners parents (if any).
Http client configuration (kotlin only)​
You can configure the ktor client used by the SDK perform requests to the backend through the httpClient
property.
If you configure the client you should also provide the configured Json serializer through the httpClientJson
property,
or there may be some inconsistencies in the behavior of the SDK.
If you don't provide a custom client the SDK uses a client shared across all instances of the SDK. If you need to
close this client you can use the CardinalSdk.closeSharedClient
method.
Cryptographic primitives service configuration (occasionally required)​
All the cryptographic operations performed by the Cardinal SDK use some cryptographic primitives such as RSA or AES. On each platform, the Cardinal SDK uses the native cryptographic primitives solutions (for example, subtle crypto on js, or SecKey on apple platforms).
However, there are some situations where the SDK can't figure out automatically which cryptographic primitives can be
used, and in those cases you need to provide a CryptoService
instance through the SdkOptions
.
Currently, this is the case only for the typescript SDK when it is used on react-native. Refer to the boilerplate documentation for more information on how you can provide the appropriate crypto service to the SDK (🚧).
Legacy support configurations​
The configuration options that were omitted from this page are used only to support the migration of legacy data.
You will not need to use these configurations unless you've been instructed to do so by our team.