2

I'm using zustand with persist plugin to store the state of my application. I want to use localstorage but the cache has to be encrypted.

For encryption, I'm using encrypt-storage. For encryption keys, I want to make an API call to the backend and initialise the encrypt storage.

The problem is while the API call is being made, the storage is still undefined. How to properly initialise zustand with encrypt-storage ?

Here is what I have tried :

import { EncryptStorage } from "encrypt-storage";
import { create } from "zustand";
import { devtools, persist, } from "zustand/middleware";
import { createJSONStorage } from "zustand/middleware"


const fake_api = (ms: number) => new Promise(resolve => {
  setTimeout(resolve, ms)
})

export function KeyResolver(callback: () => void) {
  const fn = async () => {
    //
    await fake_api(2000);
    console.log("encryption key retrieved")
    encryptStorage.EKEY = 'secret-key-value';
    encryptStorage.storage = new EncryptStorage(encryptStorage.EKEY, {
      stateManagementUse: true,
    });
    callback();
  };
  if (!encryptStorage.EKEY) {
    fn();
  }
}

interface IEncryptStorage {
  storage: undefined | EncryptStorage,
  EKEY: null | string,
}

export const encryptStorage: IEncryptStorage = {
  storage: undefined,
  EKEY: null,

}


const useOptimiserStore = create<IOptimiserStore>()(
  devtools(
    persist(
      (set) => ({
        ...initialOtimiserStoreState,
        _hasHydrated: false, 
        setHyderated: (val) => set({ _hasHydrated: val })
      }),
      {
        name: "optimiser-storage",

       
        // @ts-expect-error
        storage: createJSONStorage(() => encryptStorage.storage),

        onRehydrateStorage: () => {
          KeyResolver(() => {
            useOptimiserStore.getState().setHyderated(true)
          });

        }
      }
    ),
    {
      name: "optimiser-storage",
    }
  )
);

// And i'm using it inside my component like this: 
const Component = () => {

  const hasHyderated = useOptimiserStore(state => state._hasHydrated);

  if (!hasHyderated) {
    return <>retreiving encryption keys </>
  }

  return <div> ... </div>


}

But I get the following error:

Uncaught TypeError: can't access property "setItem", storage is undefined

Voneone
  • 21
  • 4
  • Why does it need to be encrypted? Any client-side encryption is trivially bypassed by a mildly technical user. – AKX Feb 07 '23 at 07:48
  • @AKX Basically its a webapp for designing factory layouts. I'm using canvas for this. Now I'm using locastorage to save what the user has already drawn. I don't want to hide the information from the user but store the cache in a somewhat secure way. – Voneone Feb 07 '23 at 07:56
  • But... again, why? What are you protecting against, what's the threat model? – AKX Feb 07 '23 at 10:10

1 Answers1

0

I managed to make it work by implementing a custom storage engine.

https://docs.pmnd.rs/zustand/integrations/persisting-store-data#how-can-i-use-a-custom-storage-engine

kaffarell
  • 639
  • 5
  • 16
Voneone
  • 21
  • 4
  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Feb 10 '23 at 13:40