diff --git a/examples/kotlin/shared/src/commonTest/kotlin/com/mongodb/realm/realmkmmapp/AppClientTest.kt b/examples/kotlin/shared/src/commonTest/kotlin/com/mongodb/realm/realmkmmapp/AppClientTest.kt index 229e2b1c25e..88c5246110c 100644 --- a/examples/kotlin/shared/src/commonTest/kotlin/com/mongodb/realm/realmkmmapp/AppClientTest.kt +++ b/examples/kotlin/shared/src/commonTest/kotlin/com/mongodb/realm/realmkmmapp/AppClientTest.kt @@ -30,23 +30,47 @@ class AppClientTest: RealmTest() { } @Test - fun setCustomHttpHeadersTest() { - val myEncryptionKey = getEncryptionKey() - val config1 = - // :snippet-start: configure-app-client + fun configureAppClient() { + val config = + // :snippet-start: configure-app-client // Creates an App with custom configuration values AppConfiguration.Builder(YOUR_APP_ID) // Replace with your App ID + // Specify your custom configuration values .appName("my-app-name") + .appVersion("1.0.0") + .baseUrl("http://localhost:9090") + .build() + // :snippet-end: + assertEquals(config.appName, "my-app-name") + assertEquals(config.baseUrl, "http://localhost:9090") + assertEquals(config.appVersion, "1.0.0") + } + + @Test + fun encryptAppMetadata() { + val myEncryptionKey = getEncryptionKey() + val config = + // :snippet-start: encrypted-app-client + AppConfiguration.Builder(YOUR_APP_ID) // Replace with your App ID + // Specify the encryption key .encryptionKey(myEncryptionKey) .build() // :snippet-end: + assertEquals(config.encryptionKey, myEncryptionKey) + } + + @Test + fun setCustomHttpHeadersTest() { + val myEncryptionKey = getEncryptionKey() + val config1 = AppConfiguration.Builder(YOUR_APP_ID) + .appName("my-app-name") + .encryptionKey(myEncryptionKey) + .build() val config2 = // :snippet-start: set-custom-http-headers AppConfiguration.Builder(YOUR_APP_ID) .authorizationHeaderName("MyApp-Authorization") - .customRequestHeaders { - put("X-MyApp-Version", "1.0.0") - } + .customRequestHeaders { put("X-MyApp-Version", "1.0.0") } .build() // :snippet-end: assertEquals(config1.authorizationHeaderName, "Authorization") diff --git a/examples/kotlin/shared/src/commonTest/kotlin/com/mongodb/realm/realmkmmapp/EncryptARealmTest.kt b/examples/kotlin/shared/src/commonTest/kotlin/com/mongodb/realm/realmkmmapp/EncryptARealmTest.kt index cc38e3fdfb1..0b258f2924a 100644 --- a/examples/kotlin/shared/src/commonTest/kotlin/com/mongodb/realm/realmkmmapp/EncryptARealmTest.kt +++ b/examples/kotlin/shared/src/commonTest/kotlin/com/mongodb/realm/realmkmmapp/EncryptARealmTest.kt @@ -2,9 +2,15 @@ package com.mongodb.realm.realmkmmapp import io.realm.kotlin.Realm import io.realm.kotlin.RealmConfiguration +import io.realm.kotlin.ext.query import io.realm.kotlin.internal.platform.runBlocking +import io.realm.kotlin.mongodb.App +import io.realm.kotlin.mongodb.Credentials +import io.realm.kotlin.mongodb.sync.SyncConfiguration import kotlin.random.Random import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertTrue class EncryptARealmTest : RealmTest() { @Test @@ -15,7 +21,8 @@ class EncryptARealmTest : RealmTest() { // generate a new 64-byte encryption key val key = ByteArray(64) if (seed != null) { - // If there is a seed provided, create a random number with that seed and fill the byte array with random bytes + // If there is a seed provided, create a random number with that seed + // and fill the byte array with random bytes Random(seed).nextBytes(key) } else { // fill the byte array with random bytes @@ -23,31 +30,53 @@ class EncryptARealmTest : RealmTest() { } return key } + // :remove-start: + assertTrue(getRandomKey().size == 64) + val generatedKey = getRandomKey() + // :remove-end: runBlocking { // Create the configuration - val config = RealmConfiguration.Builder( - setOf(Frog::class)) + val config = RealmConfiguration.Builder(setOf(Frog::class)) // :remove-start: .deleteRealmIfMigrationNeeded() .directory("tmp/encrypted") // :remove-end: - // specify the encryptionKey - .encryptionKey(getRandomKey()) + // Specify the encryption key + .encryptionKey(generatedKey) .build() - // :remove-start: - Realm.deleteRealm(config) - // :remove-end: - // Open a realm with the encryption key. + // Open the realm with the configuration val realm = Realm.open(config) - Log.v("Successfully opened realm:" + - realm.configuration.name - ) - + Log.v("Successfully opened encrypted realm:" + realm.configuration.name) // :remove-start: + assertEquals(generatedKey, realm.configuration.encryptionKey) realm.close() + Realm.deleteRealm(config) // :remove-end: } // :snippet-end: } + + @Test + fun encryptSyncedRealm() { + val generatedKey = getEncryptionKey() + val app = App.create(yourFlexAppId) + runBlocking { + val user = app.login(Credentials.anonymous()) + // :snippet-start: encrypt-synced-realm + val config = SyncConfiguration.Builder(user, setOf(Frog::class)) + .initialSubscriptions { realm -> + add(realm.query()) + } + // Specify the encryption key + .encryptionKey(generatedKey) + .build() + val realm = Realm.open(config) + Log.v("Successfully opened encrypted realm: ${realm.configuration}") + // :snippet-end: + assertEquals(generatedKey, realm.configuration.encryptionKey) + realm.close() + user.delete() + } + } } \ No newline at end of file diff --git a/source/examples/generated/kotlin/AppClientTest.snippet.configure-app-client.kt b/source/examples/generated/kotlin/AppClientTest.snippet.configure-app-client.kt index 7afd3ed07de..cba5512f895 100644 --- a/source/examples/generated/kotlin/AppClientTest.snippet.configure-app-client.kt +++ b/source/examples/generated/kotlin/AppClientTest.snippet.configure-app-client.kt @@ -1,5 +1,7 @@ // Creates an App with custom configuration values AppConfiguration.Builder(YOUR_APP_ID) // Replace with your App ID + // Specify your custom configuration values .appName("my-app-name") - .encryptionKey(myEncryptionKey) + .appVersion("1.0.0") + .baseUrl("http://localhost:9090") .build() diff --git a/source/examples/generated/kotlin/AppClientTest.snippet.encrypted-app-client.kt b/source/examples/generated/kotlin/AppClientTest.snippet.encrypted-app-client.kt new file mode 100644 index 00000000000..083c3cb0434 --- /dev/null +++ b/source/examples/generated/kotlin/AppClientTest.snippet.encrypted-app-client.kt @@ -0,0 +1,4 @@ +AppConfiguration.Builder(YOUR_APP_ID) // Replace with your App ID + // Specify the encryption key + .encryptionKey(myEncryptionKey) + .build() diff --git a/source/examples/generated/kotlin/AppClientTest.snippet.set-custom-http-headers.kt b/source/examples/generated/kotlin/AppClientTest.snippet.set-custom-http-headers.kt index 70dd11e5e2a..f5977f47cef 100644 --- a/source/examples/generated/kotlin/AppClientTest.snippet.set-custom-http-headers.kt +++ b/source/examples/generated/kotlin/AppClientTest.snippet.set-custom-http-headers.kt @@ -1,6 +1,4 @@ AppConfiguration.Builder(YOUR_APP_ID) .authorizationHeaderName("MyApp-Authorization") - .customRequestHeaders { - put("X-MyApp-Version", "1.0.0") - } + .customRequestHeaders { put("X-MyApp-Version", "1.0.0") } .build() diff --git a/source/examples/generated/kotlin/EncryptARealmTest.snippet.encrypt-a-realm.kt b/source/examples/generated/kotlin/EncryptARealmTest.snippet.encrypt-a-realm.kt index 7c77265ed6b..ac991e1b62c 100644 --- a/source/examples/generated/kotlin/EncryptARealmTest.snippet.encrypt-a-realm.kt +++ b/source/examples/generated/kotlin/EncryptARealmTest.snippet.encrypt-a-realm.kt @@ -3,7 +3,8 @@ fun getRandomKey(seed: Long? = null): ByteArray { // generate a new 64-byte encryption key val key = ByteArray(64) if (seed != null) { - // If there is a seed provided, create a random number with that seed and fill the byte array with random bytes + // If there is a seed provided, create a random number with that seed + // and fill the byte array with random bytes Random(seed).nextBytes(key) } else { // fill the byte array with random bytes @@ -14,15 +15,11 @@ fun getRandomKey(seed: Long? = null): ByteArray { runBlocking { // Create the configuration - val config = RealmConfiguration.Builder( - setOf(Frog::class)) - // specify the encryptionKey - .encryptionKey(getRandomKey()) + val config = RealmConfiguration.Builder(setOf(Frog::class)) + // Specify the encryption key + .encryptionKey(generatedKey) .build() - // Open a realm with the encryption key. + // Open the realm with the configuration val realm = Realm.open(config) - Log.v("Successfully opened realm:" + - realm.configuration.name - ) - + Log.v("Successfully opened encrypted realm:" + realm.configuration.name) } diff --git a/source/examples/generated/kotlin/EncryptARealmTest.snippet.encrypt-synced-realm.kt b/source/examples/generated/kotlin/EncryptARealmTest.snippet.encrypt-synced-realm.kt new file mode 100644 index 00000000000..1b50df6172b --- /dev/null +++ b/source/examples/generated/kotlin/EncryptARealmTest.snippet.encrypt-synced-realm.kt @@ -0,0 +1,9 @@ +val config = SyncConfiguration.Builder(user, setOf(Frog::class)) + .initialSubscriptions { realm -> + add(realm.query()) + } + // Specify the encryption key + .encryptionKey(generatedKey) + .build() +val realm = Realm.open(config) +Log.v("Successfully opened encrypted realm: ${realm.configuration}") diff --git a/source/sdk/kotlin/app-services/connect-to-app-services-backend.txt b/source/sdk/kotlin/app-services/connect-to-app-services-backend.txt index 915d1ab56fb..6b4ac435276 100644 --- a/source/sdk/kotlin/app-services/connect-to-app-services-backend.txt +++ b/source/sdk/kotlin/app-services/connect-to-app-services-backend.txt @@ -60,20 +60,44 @@ Configure the App Client You can add optional arguments to the ``AppConfiguration`` for more granular control of your app connection details, such as custom -timeouts for connections and keys for local encryption. +request headers and keys for local encryption. To control the additional configuration options, use the `AppConfiguration.Builder -<{+kotlin-sync-prefix+}io.realm.kotlin.mongodb/-app-configuration/-builder/index.html>`__ and call the ``.build()`` method to pass a configuration -object: +<{+kotlin-sync-prefix+}io.realm.kotlin.mongodb/-app-configuration/-builder/index.html>`__ +and call the ``.build()`` method to pass a configuration object: .. literalinclude:: /examples/generated/kotlin/AppClientTest.snippet.configure-app-client.kt :language: kotlin .. include:: /includes/multiple-app-client-details-and-app-config-cache.rst +.. _kotlin-encrypt-app-metadata: + +Encrypt App Metadata +~~~~~~~~~~~~~~~~~~~~ + +When you connect to App Services, Realm creates additional metadata files on +a device. For more information about these metadata files, refer to +:ref:``. + +You can encrypt the metadata that App Services stores on client devices, +similar to how you :ref:``. + +To encrypt App metadata, pass your encryption key to the +`encryptionKey <{+kotlin-sync-prefix+}io.realm.kotlin.mongodb/-app-configuration/encryption-key.html>`__ +property when you initialize the App: + +.. literalinclude:: /examples/generated/kotlin/AppClientTest.snippet.encrypted-app-client.kt + :language: kotlin + +.. note:: Metadata Encrypted By Default on Apple Devices + + On Apple devices, metadata is encrypted by default. To disable this, + add ``REALM_DISABLE_METADATA_ENCRYPTION`` to your environment variables. + Set Custom HTTP Headers ------------------------ +~~~~~~~~~~~~~~~~~~~~~~~ .. versionadded:: 1.11.0 diff --git a/source/sdk/kotlin/realm-database/realm-files/encrypt-a-realm.txt b/source/sdk/kotlin/realm-database/realm-files/encrypt-a-realm.txt index 414cf9b0a6c..42a2f9f6544 100644 --- a/source/sdk/kotlin/realm-database/realm-files/encrypt-a-realm.txt +++ b/source/sdk/kotlin/realm-database/realm-files/encrypt-a-realm.txt @@ -10,9 +10,6 @@ Encrypt a Realm - Kotlin SDK :depth: 2 :class: singlecol -Overview --------- - You can encrypt your realms to ensure that the data stored to disk can't be read outside of your application. You encrypt the realm file on disk with AES-256 + SHA-2 by supplying a 64-byte encryption key when @@ -27,13 +24,49 @@ integrity using a :wikipedia:`hash-based message authentication code .. include:: /includes/encrypt-use-strong-cryptographic-hash.rst +Encrypt a Local Realm +--------------------- + +To encrypt a local realm, pass your encryption key to the +`encryptionKey <{+kotlin-local-prefix+}io.realm.kotlin/-configuration/encryption-key.html>`__ +property in your +`RealmConfiguration.Builder() <{+kotlin-local-prefix+}io.realm.kotlin/-realm-configuration/index.html>`__. + +The following code demonstrates how to generate an encryption key and +open an encrypted local realm: + +.. literalinclude:: /examples/generated/kotlin/EncryptARealmTest.snippet.encrypt-a-realm.kt + :language: kotlin + :emphasize-lines: 19-20 + +.. _kotlin-encrypt-a-synced-realm: + +Encrypt a Synced Realm +---------------------- + +You can also encrypt a :ref:`synced realm `, similar +to encrypting a local realm. Refer to the :ref:`` +section on this page for more information. + +To encrypt a synced realm, pass your encryption key to the +`encryptionKey <{+kotlin-sync-prefix+}io.realm.kotlin.mongodb/-app-configuration/encryption-key.html>`__ +property in your +`SyncConfiguration.Builder() +<{+kotlin-sync-prefix+}io.realm.kotlin.mongodb.sync/-sync-configuration/-builder/-builder.html>`__. + +The following code demonstrates how to open an encrypted synced realm: + +.. literalinclude:: /examples/generated/kotlin/EncryptARealmTest.snippet.encrypt-synced-realm.kt + :language: kotlin + :emphasize-lines: 5-6 + Considerations -------------- The following are key impacts to consider when encrypting a realm. -Storing & Reusing Keys -~~~~~~~~~~~~~~~~~~~~~~ +Store & Reuse Keys +~~~~~~~~~~~~~~~~~~ You **must** pass the same encryption key every time you open the encrypted realm. If you don't provide a key or specify the wrong key for an encrypted @@ -53,20 +86,8 @@ Performance Impact Reads and writes on encrypted realms can be up to 10% slower than unencrypted realms. -Encryption and Atlas Device Sync -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -You can encrypt a :ref:`synced realm `. - -.. include:: /includes/encrypt-atlas-device-sync.rst - -If you need unique keys for each user of your application, you can use an OAuth provider or -use one of the :ref:`Realm authentication providers ` -and an :ref:`authentication trigger` -to create a 64-bit key and store that key in a :ref:`user object `. - -Multiple Processes -~~~~~~~~~~~~~~~~~~ +Access an Encrypted Realm from Multiple Processes +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. versionchanged:: 10.8.0 @@ -77,13 +98,21 @@ If your app uses Realm Kotlin SDK version 10.7.1 or earlier, attempting to open an encrypted realm from multiple processes throws this error: ``Encrypted interprocess sharing is currently unsupported.`` -Example -------- +.. _kotlin-encryption-and-device-sync: -.. include:: /includes/encrypt-use-strong-cryptographic-hash.rst +Encryption and Atlas Device Sync +-------------------------------- -The following code demonstrates how to generate an encryption key and -open an encrypted realm: +.. include:: /includes/encrypt-atlas-device-sync.rst -.. literalinclude:: /examples/generated/kotlin/EncryptARealmTest.snippet.encrypt-a-realm.kt - :language: kotlin \ No newline at end of file +If you need unique keys for each user of your application, you can use an OAuth provider or +use one of the :ref:`Realm authentication providers ` +and an :ref:`authentication trigger` +to create a 64-bit key and store that key in a :ref:`user object `. + +Encrypt App Metadata +~~~~~~~~~~~~~~~~~~~~ + +You can encrypt the App Services App metadata that Realm stores on the device. + +To learn more, refer to :ref:``.