When you're building a new real-time feature, the initial focus is on making it work. You spin up a prototype, get the data flowing, and celebrate that first magical moment when two browser tabs update simultaneously. But before you ship to production, a critical question arises: Is this secure?
Security in real-time applications is non-trivial. How do you protect your API credentials on the client-side? How do you ensure one user can't access another user's data? How do you handle sensitive information with maximum confidentiality?
At Vaultrice, we believe security shouldn't be an afterthought. It should be a series of layers you can apply as your application grows in complexity. In this guide, we'll walk through a practical, progressive journey of securing a real-time React application, moving from a basic prototype to a production-ready, end-to-end encrypted system using Vaultrice's security model.
The Scenario: A Collaborative "To-Do" List
Imagine we're building a simple collaborative to-do list app. Multiple users can join a shared list and add or remove items.
- Prototype Goal: Get a basic real-time list working.
- Production Goal: Ensure only authorized users can access their own lists, their identity is verified, and the to-do items (which might be sensitive) are kept private.
Stage 1: The Prototype (Security Level 0) 🐣
At the start, we just want to get things working. We'll use Direct Authentication with an API key and secret.
The Code:
import { NonLocalStorage } from '@vaultrice/sdk';
// For a quick prototype, we place credentials directly.
const credentials = {
projectId: 'YOUR_PROJECT_ID',
apiKey: 'YOUR_API_KEY',
apiSecret: 'YOUR_API_SECRET'
};
const sharedTodoList = new NonLocalStorage(credentials, 'our-first-todo-list');
// We can now read and write to the list.
await sharedTodoList.setItem('task-1', { text: 'Build prototype' });
Security Analysis (Level 0):
- What's working: All communication is automatically encrypted in transit with TLS (HTTPS), and all data is encrypted at rest by default on the backend. This is SF 0: Transport + Default At-Rest Encryption and it's always active on all plans.
-
The Risk: Our
apiSecret
is exposed in the client-side code. A malicious actor could steal these credentials and use them from anywhere to access our data. This is fine for a local demo, but unacceptable for production.
Stage 2: Production-Ready (Security Levels 1 & 2) 🚀
Now, let's take our app to production. We need to lock down our credentials, control data access, and verify user identities.
Upgrading Authentication: Secure Access Tokens
First and foremost, we must remove the long-lived apiSecret
from our client-side code. The gold standard for this is using Backend-Issued Access Tokens.
The Pattern: Your own backend has a secure endpoint that uses your
apiKey
andapiSecret
to generate a short-lived access token for the client. The client then initializes the SDK with this temporary token, never knowing the secret.-
Action: Create a serverless function or API endpoint on your trusted backend.
// On your trusted backend (e.g., /api/vaultrice-token) import { retrieveAccessToken } from '@vaultrice/sdk'; async function generateTokenForClient(origin) { const accessToken = await retrieveAccessToken( 'YOUR_PROJECT_ID', 'YOUR_API_KEY', // Kept securely on the server 'YOUR_API_SECRET', // Kept securely on the server { origin } // Pass the client's origin if using Origin Restrictions ); return { accessToken }; }
-
Client-Side Implementation: Your client code is now much more secure. You can use the
getAccessToken
provider for automatic, hands-free token management.
// In your React App import { NonLocalStorage } from '@vaultrice/sdk'; const secureTodoList = new NonLocalStorage({ projectId: 'YOUR_PROJECT_ID', // The SDK will call this function automatically to get and refresh tokens getAccessToken: async () => { const response = await fetch('/api/vaultrice-token'); if (!response.ok) throw new Error('Failed to fetch token'); const { accessToken } = await response.json(); return accessToken; } }, 'your-object-id');
Result: You've achieved maximum credential security. Your
apiSecret
is never exposed to the browser, and the client operates on temporary, revokable tokens.
Layer 1: Perimeter Defense with Origin Restrictions (SF 1)
Next, we prevent our API key from being used on any other website.
-
Action: In the Vaultrice dashboard, we edit our API key and add our app's domain (e.g.,
https://mytodoapp.com
) to the Origin Restrictions list. - Result: The Vaultrice backend will now reject any request made with this key that doesn't originate from our domain. This is an essential first step for all production applications.
Layer 2: Data Access Control with ID Signatures (SF 2)
We must ensure a user can only access their own to-do list, not someone else's. We can't trust the client to be honest about which list it wants to access.
-
Action: We enable Object ID Signature Verification in our project's Class settings. Then, we create an endpoint on our backend that signs the
objectId
for a logged-in user.
// On your trusted backend server function getSignedTodoListId(user) { const objectId = `todo-list-${user.id}`; // You sign the ID with your private key const signature = signWithYourPrivateKey(objectId); return { objectId, signature }; }
-
Client-Side Implementation: The client now fetches this signed ID from the backend before initializing the SDK.
// Fetch the authorized object ID and signature from our backend const { objectId, signature } = await fetch('/api/get-todo-list-id'); // Vaultrice will now verify this signature on every request const userTodoList = new NonLocalStorage(credentials, { id: objectId, idSignature: signature });
Result: A user can no longer guess another user's
objectId
and access their data. The Vaultrice API will reject any request where the signature doesn't match the ID.
Layer 3: Optional Server-Side Hardening (SF 3)
For an extra layer of protection on the server, you can enable Automatic At-Rest Encryption.
- What it is: This feature transparently encrypts data values on the Vaultrice server with a unique key before they are stored in the database. This protects against a direct breach of the underlying database infrastructure.
- Action: In your Class settings, simply enable the "Additional At-Rest Encryption" checkbox.
- Result: Enhanced server-side data protection with no code changes required in your SDK implementation.
Stage 3: Maximum Confidentiality (Security Level 3) 🛡️
Our app is now secure for general use. But what if our to-do items contain highly sensitive information? For this, we need Security Level 3 and SF 4: End-to-End Encryption (E2EE).
-
Action: We enable E2EE by providing a
passphrase
during SDK initialization. This passphrase is known only to the client and is never sent to the server.
import { NonLocalStorage } from '@vaultrice/sdk'; const sensitiveList = new NonLocalStorage(credentials, { id: signedObjectId, // from Stage 2 idSignature: signature, // from Stage 2 passphrase: 'user-secret-passphrase-never-sent-to-server' }); // We must fetch the salt from the server to derive the encryption key await sensitiveList.getEncryptionSettings(); // This data is encrypted ON THE DEVICE before being sent. // Not even Vaultrice can read its content. await sensitiveList.setItem('task-1', { text: 'Top secret plan' });
Result: The content of our to-do list is now fully confidential. Vaultrice only stores ciphertext, providing the highest level of privacy. It's important to note that server-side atomic operations are incompatible with E2EE, as the server cannot decrypt the data to operate on it. So those operations are not E2EE encrypted.
Conclusion
Security is a journey, not a destination. By starting with a simple prototype and progressively adding layers of protection as needed, you can move to production with confidence. Vaultrice's layered security model provides a clear path to secure your real-time application, from basic transport encryption all the way to zero-knowledge, end-to-end encrypted collaboration.
Choose the security level that matches your needs and start building with confidence on the Vaultrice free tier.
Top comments (0)