← Back to blog
Introducing Store

Introducing Store

authors photo
Written by Dante
Tuesday, August 6th 2024

Our customers at Onboardbase have been demanding a secure and efficient way to store and retrieve secrets for quite a while, so we finally did it: here comes Store―a straightforward API providing an encrypted key-value store for your customers’ secrets.

Imagine you’re building a payment system for a web application: each customer might, for example, need a license key to activate their product. With Store, you can securely store these secrets and retrieve them when needed without complexifying your database design or writing custom encryption logic. It just works in a few lines of code by importing an npm package.

But that’s not all, Store has many useful use cases. First, let’s see how Store fits into your development workflow.

Why Store

Store safeguards sensitive information by centralizing secrets in a secure key-value store, diminishing the risks of exposing credentials within your regular database―or worse, in your codebase.

The store’s main strength is its simplicity. Integrating it into any codebase with a straightforward key-value interface is a breeze: no complex setup or steep learning curve is involved. Set up a store from your Onboardbase dashboard and use the Store REST API to start.

Built on the Onboardbase platform, the Store inherits robust features: advanced authentication and authorization, granular team management, and security logs. The stored secrets are encrypted, but Onboardbase also ensures your development team enforces strict security policies: each access is monitored so you know precisely who or which program queries the API, and you can update your Onboardbase access control list accordingly.

But talk is cheap; let’s examine the code and how it integrates into your app.

A Simple Key/Value Store

1. Creating a store

  • Log in to your Onboardbase dashboard or create a new account if you don’t have one.
  • In your project page, create a new store
  • Pick a name and click on the copy icon to get your STORE KEY.

Your store key is a unique identifier for your store―keep it safe! Your store key should never be hard-coded use environment variables or Onboardbase instead.

2. Install the npm package

npm install @onboardbase/store

3. Create a Store object

Import the Store class and create a new Store object instance:

import Store from '@onboardbase/store';
import dotenv from 'dotenv';
dotenv.config();

const store = new Store(process.env.STORE_KEY);

4. Set a secret

To store a secret, use the set method:

await store.set({ 
    key: 'hey', 
    value: 'hello' 
});

If you don’t use Javascript, you can use the REST API directly:

API

5. Retrieve a secret

You can just as easily retrieve a secret with the get method:

await store.get({ 
    key: 'hey' 
})

Again, there is a REST API method for that:

API

6. Use a locker

Sometimes, you should group secrets together in a namespace. To store all the secrets related to a specific customer, for example. You can use a locker for that:

await store.set({ 
    key: 'hey2', 
    value: 'hello-some-locker', 
    lockerKey: 'some-locker' 
})

await store.get({ 
    key: 'hey2', 
    lockerKey: 'some-locker' 
})

Depending on your use case, you might want to use a locker for each customer, secret type, environment, or application. Lockers are a way to organize your secrets and keep them separate without complex key naming conventions.

Use Cases

Store is a versatile tool for many scenarios:

  • API keys - Secure your customer API keys.
  • JWT tokens - Store your JSON Web Tokens and refresh tokens securely.
  • License keys - Protect your customer license keys for product activation.
  • Domain names - Need to build a custom domain feature? Store them securely.
  • Passwords - Store your salted passwords securely.

Frontend applications, mobile apps, and serverless functions can all benefit from Store. In a time where customer data privacy is crucial, the user-friendly Store API can be a quick and effective tool to build secure features or reduce attack surfaces.

Encryption Out-Of-The-Box

Onboardbase Store uses a combination of AES encryption at rest and perfect forward secrecy in transit to keep your secrets safe.

We use AES-256-GCM for encryption at rest. It’s virtually unbreakable since it would take years to brute-force a key, while being among the fastest symmetric encryption algorithms for more practical usages at scale in real-life applications.

During transit, Perfect Forward Secrecy (PFS) consists in regularly changing the keys used to encrypt and decrypt information in an automated way using software. For example, using an asymmetric encryption method like Elliptic Curve Cryptography (ECC), we will make sure the private keys used by the receivers will change on each transaction without manual intervention.

Encryption algorithms rely on private keys (passphrases, passwords) to secure data and transactions. Just like passwords, these keys must be stored securely, too: if the keys are leaked, the whole encryption system can be breached. With perfect forward secrecy, a security breach will only result in minimal compromised information since the keys change regularly. PFS is suitable for insecure networks, so you can work from your public library without worrying about attackers spying on you.

Read more about how we use AES and ECC on our blog.

Works With Files Too!

Store can also handle files. You can store any file up to 5MB in size:

import { promises as fs } from 'node:fs'
import { Blob } from 'node:buffer'

(async () => {
    let buffer = await fs.readFile('./.gitignore');
    let blob = new Blob([buffer]);
    
    await store.setFile({ 
        key: 'hey-file', 
        fileName: 'gitignore', 
        lockerKey: 'some-locker', 
        file: blob 
    });
})()

To retrieve a file, use the getFile method:

await store.getFile({ key: 'hey-file', lockerKey: 'some-locker' })

You can use the API to store SSH keys, certificates, or any other sensitive document you need to keep encrypted.

The Store API might look too simple to be useful, so we found it interesting to include three concrete feature examples many apps need in order to make it more tangible for you.

Example 1: Client API key management

Chances are your software might offer a public API to users. A fundamental requirement is to securely manage API keys used to authenticate API requests and control access to different API endpoints.

A simple key-value store can efficiently handle API key management:

  • Encrypted storage - The generated API key is encrypted by default when you use the key-value store to enhance security.
  • Search by hash - To efficiently validate an API key, a hash of the API key value can be used as the key in the key-value store.
  • Multiple keys per customer, or search per customer using lockers - For scenarios where multiple API keys are required per customer, a locker can be used to speed up search.
import crypto from 'node:crypto'
import { Buffer } from "node:buffer";
import Store from '@onboardbase/store';
import dotenv from 'dotenv';
dotenv.config();

let store = false

export async function getStore(){
  if(!store) {
    store = new Store(process.env.STORE_KEY);
  }

  return store
}

export async function hash(key) {
	const encoder = new TextEncoder("utf-8");

	const stringBuf = encoder.encode(key);

	const hashBuffer = await crypto.subtle.digest("SHA-256", stringBuf);

	return Buffer.from(hashBuffer).toString("hex");
}

async function storeApiKey(customerId, apiKey) {
  return store.set({ 
      key: await hash(apiKey), 
      value: apiKey, 
      lockerKey: customerId 
  })
}

async function getApiKey(customerId, hash) {
  return store.get({ 
      key: hash, 
      lockerKey: customerId 
  })
}

Of course, make sure to generate cryptographically strong random keys for each user, encourage rotation, and use Onboardbase to secure your Store’s primary API key.

Example 2: Encrypted file storage

A simple upload file feature can become a headache when you add encryption. Not with Onboardbase Store, though.

Users should be able to upload files as usual, which are then stored encrypted. This could be used for profile pictures, sensitive documents, or other private, user-generated content.

  1. Upload file - The user uploads a file through the web interface. This file is sent to the backend as form data.
  2. Backend function to store file - The backend receives the file and uses Store to keep it encrypted.
  3. Retrieve file - When a user requests a file, the backend retrieves the encrypted data from the key-value store, which is automatically decrypted before sending it back to the client.
import express from 'express'
import multer from 'multer'
import crypto from 'node:crypto'

import Store from '@onboardbase/store';
import dotenv from 'dotenv';
dotenv.config();

const app = express();
const upload = multer();

app.post('/upload', upload.single('file'), async (req, res) => {
  const file = req.file;
  const key = crypto.randomBytes(32).toString('hex'); // Generate a random key
  const blob = new Blob([file.buffer]);

  await store.setFile({ 
        key, 
        fileName: file.originalname, 
        lockerKey: req.data.userId, 
        file: blob 
  });

  res.json({ fileId: key });
});

app.get('/file/:fileId', async (req, res) => {
  const fileId = req.params.fileId;

  const file = await store.getFile({ key: fileId, lockerKey: req.data.userId });

  res.download(file);
});

app.listen(3000, () => {
  console.log('Server listening on port 3000');
});

Notice how you barely need to do anything to encrypt and decrypt the file? That’s the power of Store.

When working on collaborative UX, you often need to share documents securely with external users. Instead of exposing the full document URL, a short, unique invite link can be generated, shared, and removed, giving you complete control over who has access to the document.

Again, we can use a simple key-value store to manage these invite links:

  • Link generation - A random string is generated to serve as the link. We can use the parent document’s name as a key name or locker.
  • Link storage - The generated link is stored as the value. Additional metadata like creation date, expiration date, and access permissions can be stored as part of the value.
  • URL shortening - If the invite link is long and complicated with GET parameters, you might want to hash it to make it shorter. You can then use this hash as the key name for fast database lookups.
  • Access Control - Multiple links can be generated for the same document with different permissions (e.g., read-only, edit). Just add a suffix to the key name for fast retrieval.
import crypto from 'node:crypto'
import { Buffer } from "node:buffer";
import Store from '@onboardbase/store';
import dotenv from 'dotenv';
dotenv.config();

export async function hash(key) {
	const encoder = new TextEncoder("utf-8");

	const stringBuf = encoder.encode(key);

	const hashBuffer = await crypto.subtle.digest("SHA-256", stringBuf);

	return Buffer.from(hashBuffer).toString("hex");
}

async function generateInviteLink(projectId, documentId, permissions, expiration) {
  const link = crypto.randomBytes(32).toString('hex');

  const value = {
    documentId,
    permissions,
    expires: expiration,
    // Other metadata if needed
  };

  const key = `link:${link}`;
  const hash = await hash(key)
  
  await store.set({ 
      key: hash, 
      value, 
      lockerKey: projectId 
  })

  return hash;
}

async function getDocumentByHash(customerId, hash) {
  const link = store.get({ 
      key: hash, 
      lockerKey: customerId 
  })

  if (!link) {
    return null;
  }

  if (link.expires < Date.now()) {
    return null; 
  }

  const document = await getDocument(link.documentId);

  return document
}

Conclusion

This article introduced Store, a new API from Onboardbase that provides a secure and easy-to-use encrypted key-value store for your customers’ secrets.

As shown in the examples, the Store integrates seamlessly with your development workflow while eliminating the need for complex database design or custom encryption logic. Store is a versatile tool with a wide range of use cases, from securing API keys and JWT tokens to protecting license keys and user passwords. It can be used by frontend applications, mobile apps, and serverless functions regardless of your tech stack. Additionally, the Onboardbase platform takes care of access control and monitoring, ensuring your secrets are safe and only authorized users can access them.

Sign up for a free Onboardbase account today and start using Store to keep your customers’ secrets safe!

While Store offers a powerful solution for secret management, future improvements could include batch operations for efficiency when dealing with a large number of secrets, or advanced search features. Make sure to let us know how we can help, your feedback is appreciated.

Subscribe to our newsletter

The latest news, articles, features and resources of Onboardbase, sent to your inbox weekly