diff --git a/examples/react-native/v12/TestApp/App.tsx b/examples/react-native/v12/TestApp/App.tsx index 92571046ac..c25016a1c9 100644 --- a/examples/react-native/v12/TestApp/App.tsx +++ b/examples/react-native/v12/TestApp/App.tsx @@ -12,6 +12,7 @@ import {Logger} from './src/components/logger/Logger'; import {ObjectModels} from './src/components/object-models/ObjectModels'; import {RelationshipExamples} from './src/components/relationships/RealmWrapper'; import {CompensatingWriteErrorHandling} from './src/components/errors/CompensatingWriteWrapper'; +import {EncryptMetadata} from './src/components/encryption/EncryptMetadata'; // Screens import {SubscriptionScreen} from './src/screens/SubscriptionScreen'; @@ -22,6 +23,9 @@ import {RootStackParamList} from './src/navigation/types'; const Drawer = createDrawerNavigator(); +// Create encryption key for encryption examples. +const encryptionKey = new ArrayBuffer(64); + /* // Each screen has its own RealmProvider and realm. However, they all point to // the default path. This means you'll get an error when you try to navigate @@ -38,18 +42,46 @@ function App(): JSX.Element { return ( - - - - - - - + + + + + + + - + + } + /> ); diff --git a/examples/react-native/v12/TestApp/babel.config.cjs b/examples/react-native/v12/TestApp/babel.config.cjs index d7f011f121..1e1584f26e 100644 --- a/examples/react-native/v12/TestApp/babel.config.cjs +++ b/examples/react-native/v12/TestApp/babel.config.cjs @@ -1,7 +1,7 @@ module.exports = { presets: [ ['@babel/preset-env', {targets: {node: 'current'}}], - // 'module:metro-react-native-babel-preset', + 'module:metro-react-native-babel-preset', '@babel/preset-typescript', ], plugins: ['react-native-reanimated/plugin'], diff --git a/examples/react-native/v12/TestApp/package-lock.json b/examples/react-native/v12/TestApp/package-lock.json index ab16fe073c..f341a609ba 100644 --- a/examples/react-native/v12/TestApp/package-lock.json +++ b/examples/react-native/v12/TestApp/package-lock.json @@ -20,7 +20,7 @@ "react-native-reanimated": "^3.4.2", "react-native-safe-area-context": "^4.7.1", "react-native-screens": "^3.24.0", - "realm": "^12.2.1" + "realm": "^12.2.3" }, "devDependencies": { "@babel/core": "^7.20.0", @@ -13433,9 +13433,9 @@ "integrity": "sha512-k2d6ACCkiNYz222Fs/iNze30rRJ1iIicW7JuX/7/cozvih6YCkFZH+J6mAFDVgv0dRBaAyr4jDqC95R2y4IADg==" }, "node_modules/realm": { - "version": "12.2.1", - "resolved": "https://registry.npmjs.org/realm/-/realm-12.2.1.tgz", - "integrity": "sha512-r9lB5S3FiqS2QZmxRvwt8cRLAx/g8KGotz5neV/ky1AhG9b7FtqTwLyJwhVLjkstnV8j40GaY0xMjuAbURINDg==", + "version": "12.3.0", + "resolved": "https://registry.npmjs.org/realm/-/realm-12.3.0.tgz", + "integrity": "sha512-qlWu8RpgGQhCllwutGZUJ+B37AF+7RNNXlyo5SftZoQFiTKVlmhN5w6B2fBmTd7R9J7Tw+lJcYUbvCZuZ4es0w==", "hasInstallScript": true, "dependencies": { "bson": "^4.7.2", diff --git a/examples/react-native/v12/TestApp/package.json b/examples/react-native/v12/TestApp/package.json index 5548c6bf14..a46682fff2 100644 --- a/examples/react-native/v12/TestApp/package.json +++ b/examples/react-native/v12/TestApp/package.json @@ -25,7 +25,7 @@ "react-native-reanimated": "^3.4.2", "react-native-safe-area-context": "^4.7.1", "react-native-screens": "^3.24.0", - "realm": "^12.2.1" + "realm": "^12.2.3" }, "devDependencies": { "@babel/core": "^7.20.0", diff --git a/examples/react-native/v12/TestApp/src/components/encryption/EncryptMetadata.test.tsx b/examples/react-native/v12/TestApp/src/components/encryption/EncryptMetadata.test.tsx new file mode 100644 index 0000000000..171f5ef253 --- /dev/null +++ b/examples/react-native/v12/TestApp/src/components/encryption/EncryptMetadata.test.tsx @@ -0,0 +1,16 @@ +import 'react-native'; +import React from 'react'; +import {render, screen} from '@testing-library/react-native'; + +import {EncryptMetadata} from './EncryptMetadata'; + +// Create encryption key for encryption examples. +const encryptionKey = new ArrayBuffer(64); + +test('linking an anonymous user with an email/password account', async () => { + render(); + + const encryptionResultTextNode = await screen.findByTestId('is-realm-app'); + expect(encryptionResultTextNode).toBeInTheDocument; + expect(encryptionResultTextNode.children[1]).toBe('true'); +}); diff --git a/examples/react-native/v12/TestApp/src/components/encryption/EncryptMetadata.tsx b/examples/react-native/v12/TestApp/src/components/encryption/EncryptMetadata.tsx new file mode 100644 index 0000000000..b9a2bc8732 --- /dev/null +++ b/examples/react-native/v12/TestApp/src/components/encryption/EncryptMetadata.tsx @@ -0,0 +1,59 @@ +// :snippet-start: imports +import React from 'react'; +import {Text, View} from 'react-native'; +import {MetadataMode} from 'realm'; +import {AppProvider} from '@realm/react'; +// :snippet-end: +import {StyleSheet} from 'react-native'; +import {useApp} from '@realm/react'; +import {APP_ID} from '../../../appServicesConfig'; + +// :snippet-start: encrypt-metadata +// :replace-start: { +// "terms": { +// "export ": "" +// } +// } +export const EncryptMetadata = ({ + encryptionKey, +}: { + encryptionKey: ArrayBuffer; +}) => { + // :emphasize-start: + const metadataConfig = { + mode: MetadataMode.Encryption, + encryptionKey: encryptionKey, + }; + // :emphasize-end: + + return ( + + + + ); +}; +// :replace-end: +// :snippet-end: + +const RestOfApp = () => { + const app = useApp(); + + return ( + + + Is an instance of `Realm.App`?: {app ? 'true' : 'false'} + + + ); +}; + +const styles = StyleSheet.create({ + section: { + flex: 1, + marginTop: 8, + paddingVertical: 12, + alignItems: 'center', + }, +}); diff --git a/examples/react-native/v12/TestApp/src/navigation/types.tsx b/examples/react-native/v12/TestApp/src/navigation/types.tsx index 5365e0e306..6103c0bdf5 100644 --- a/examples/react-native/v12/TestApp/src/navigation/types.tsx +++ b/examples/react-native/v12/TestApp/src/navigation/types.tsx @@ -12,6 +12,9 @@ export type RootStackParamList = { Relationships: undefined; Errors: undefined; Authentication: undefined; + Encryption: { + encryptionKey: ArrayBuffer; + }; }; export type SubscriptionStackParamList = { diff --git a/source/examples/generated/react-native/v12/EncryptMetadata.snippet.encrypt-metadata.tsx.rst b/source/examples/generated/react-native/v12/EncryptMetadata.snippet.encrypt-metadata.tsx.rst new file mode 100644 index 0000000000..cc2d8e9fbc --- /dev/null +++ b/source/examples/generated/react-native/v12/EncryptMetadata.snippet.encrypt-metadata.tsx.rst @@ -0,0 +1,21 @@ +.. code-block:: typescript + :emphasize-lines: 6-9 + + const EncryptMetadata = ({ + encryptionKey, + }: { + encryptionKey: ArrayBuffer; + }) => { + const metadataConfig = { + mode: MetadataMode.Encryption, + encryptionKey: encryptionKey, + }; + + return ( + + + + ); + }; diff --git a/source/examples/generated/react-native/v12/EncryptMetadata.snippet.imports.tsx.rst b/source/examples/generated/react-native/v12/EncryptMetadata.snippet.imports.tsx.rst new file mode 100644 index 0000000000..c9d441f604 --- /dev/null +++ b/source/examples/generated/react-native/v12/EncryptMetadata.snippet.imports.tsx.rst @@ -0,0 +1,6 @@ +.. code-block:: typescript + + import React from 'react'; + import {Text, View} from 'react-native'; + import {MetadataMode} from 'realm'; + import {AppProvider} from '@realm/react'; diff --git a/source/sdk/react-native/app-services/connect-to-app-services-app.txt b/source/sdk/react-native/app-services/connect-to-app-services-app.txt index 6fb08f0368..c0148cbad4 100644 --- a/source/sdk/react-native/app-services/connect-to-app-services-app.txt +++ b/source/sdk/react-native/app-services/connect-to-app-services-app.txt @@ -62,3 +62,24 @@ from the ``realm`` package, then pass in your ``App ID``. import Realm from 'realm'; const app = Realm.App.getApp(""); + +.. _react-native-encrypt-app-metadata: + +Encrypt App Metadata +-------------------- + +You can encrypt the metadata App Services stores on client devices. Use the +values of the :js-sdk:`MetadataMode enum ` +to determine encryption behavior. + +To encrypt App metadata: + +#. Import ``MetadataMode`` from ``Realm``. +#. Create an App configuration object that contains the ``metadata`` property. +#. Set ``metadata.mode`` to a value from the ``MetadataMode`` enum. +#. Set ``metadata.encryptionKey`` to the key you want to use for encryption. +#. Pass the App configuration object to ``new Realm.App()``. + +.. include:: /examples/generated/react-native/v12/EncryptMetadata.snippet.imports.tsx.rst + +.. include:: /examples/generated/react-native/v12/EncryptMetadata.snippet.encrypt-metadata.tsx.rst diff --git a/source/sdk/react-native/realm-files/encrypt.txt b/source/sdk/react-native/realm-files/encrypt.txt index c7b7b02db4..912eb3d2b8 100644 --- a/source/sdk/react-native/realm-files/encrypt.txt +++ b/source/sdk/react-native/realm-files/encrypt.txt @@ -22,13 +22,16 @@ integrity using a :wikipedia:`hash-based message authentication code .. include:: /includes/encrypt-use-strong-cryptographic-hash.rst -Considerations --------------- +The following code demonstrates how to generate an encryption key and +open an encrypted realm: + +.. literalinclude:: /examples/generated/react-native/ts/encrypted-realm.test.snippet.encrypted-realm.tsx + :language: typescript The following are key impacts to consider when encrypting a realm. Storing & Reusing 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 @@ -38,12 +41,12 @@ Apps should store the encryption key securely, typically in the target platform's secure key/value storage, so that other apps cannot read the key. 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 `. @@ -54,24 +57,25 @@ 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 `. -Accessing an Encrypted Realm from Multiple Processes -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Access an Encrypted Realm from Multiple Processes +------------------------------------------------- .. versionchanged:: ``realm@11.8.0`` -Starting with Realm React Native SDK version 11.8.0, Realm supports opening +Starting with Realm React Native SDK version v11.8.0, Realm supports opening the same encrypted realm in multiple processes. -If your app uses Realm React Native SDK version 11.7.0 or earlier, attempting to +If your app uses Realm React Native SDK version v11.7.0 or earlier, attempting to open an encrypted realm from multiple processes throws this error: ``Encrypted interprocess sharing is currently unsupported.`` -Example -------- +Encrypt App Services App Metadata +--------------------------------- -The following code demonstrates how to generate an encryption key and -open an encrypted realm: +If you use Atlas Device Sync with your realm, your App Services App uses an +on-device metadata file to determine changes that should sync. -.. literalinclude:: /examples/generated/react-native/ts/encrypted-realm.test.snippet.encrypted-realm.tsx - :language: typescript +You can encrypt this metadata file in a similar manner as encrypting your +realm. +To learn more, refer to :ref:`Ecrypt App Metadata `.