diff --git a/.DS_Store b/.DS_Store index e7c8e66..df7a1e9 100644 Binary files a/.DS_Store and b/.DS_Store differ diff --git a/README.md b/README.md index 2afdc3a..69f5cc0 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ Using the [`crypto-js`](https://github.com/brix/crypto-js) library as an encrypt > **HELP THIS PROJECT**: Your Github `star` can help this project. Leave a `star`, it costs nothing. -> **⚠️ IMPORTANT**: Nothing on the front end is entirely secure. The library's proposal is to make it difficult for the user to see the data through the console, but as the secret key is on the front end, if the user searches hard enough, he will end up finding it. Just to make it clear that nothing is completely secure on the front end. Thank you for your attention. +> **⚠️ IMPORTANT**: Nothing on the front end is entirely secure. The library's proposal is to make it difficult for the user to see the data through the console, but as the secret key is on the front end, if the user searches hard enough, he will end up finding it. Just to make it clear that nothing is completely secure on the front end. Thank you for your attention. - [Encrypt Storage](#encrypt-storage) - [Features](#features) @@ -46,6 +46,7 @@ Using the [`crypto-js`](https://github.com/brix/crypto-js) library as an encrypt - [_decryptValue_](#decryptvalue) - [_hash_](#hash) - [_md5Hash_](#md5hash) + - [NextJS](#nextjs) - [AsyncEncryptStorage](#asyncencryptstorage) - [AWS Amplify](#aws-amplify) - [State Management Persisters](#state-management-persisters) @@ -629,6 +630,41 @@ result of `hashed value`: const value = '284e512750fb7d41f1cc5284a2c56a13'; ``` +### NextJS + +When used in NextJS, validation must be done. + +example: + +```typescript +// utils/storage.(ts|js) +import { EncryptStorage } from 'encrypt-storage'; + +const encryptStorage = (): EncryptStorage | null => { + const isInClientSide = + typeof window !== 'undefined' && typeof window?.self !== 'undefined'; + + if (isInClientSide) { + return new EncryptStorage( + String(process.env.NEXT_PUBLIC_STORAGE_SECRET), + // options, + ); + } + + return null; +}; +``` + +usage: + +```typescript +'use client'; +import { encryptStorage } from '../utils/storage.ts'; + +// ...rest of code +encryptStorage()?.setItem('any-key', { name: 'John Doe', age: 40 }); +``` + ### AsyncEncryptStorage EncryptStorage can also be used asynchronously, simply using its corresponding version already exported by the library. diff --git a/package.json b/package.json index 6d0a2c0..eae8090 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "encrypt-storage", - "version": "2.12.23", + "version": "2.13.00", "description": "Wrapper for encrypted localStorage and sessionStorage in browser", "main": "dist/index.js", "types": "dist/index.d.ts", diff --git a/src/encrypt-storage.ts b/src/encrypt-storage.ts index c2048ac..be3b2d7 100644 --- a/src/encrypt-storage.ts +++ b/src/encrypt-storage.ts @@ -23,6 +23,8 @@ export class EncryptStorage implements EncryptStorageInterface { readonly #doNotEncryptValues: boolean; + readonly #doNotParseValues: boolean; + readonly #notifyHandler: NotifyHandler | undefined; /** @@ -43,6 +45,7 @@ export class EncryptStorage implements EncryptStorageInterface { encAlgorithm = 'AES', doNotEncryptValues = false, notifyHandler, + doNotParseValues = false, } = options || {}; secret.set(this, secretKey); @@ -51,6 +54,7 @@ export class EncryptStorage implements EncryptStorageInterface { this.#notifyHandler = notifyHandler; this.#stateManagementUse = stateManagementUse; this.#doNotEncryptValues = doNotEncryptValues; + this.#doNotParseValues = doNotParseValues; this.#encryptation = getEncryptation(encAlgorithm, secret.get(this)); this.storage = typeof window === 'object' ? window[storageType] : null; } @@ -75,8 +79,13 @@ export class EncryptStorage implements EncryptStorageInterface { public setItem(key: string, value: any, doNotEncrypt = false): void { const encryptValues = this.#doNotEncryptValues || doNotEncrypt; const storageKey = this.#getKey(key); - const valueToString = + let valueToString = typeof value === 'object' ? JSON.stringify(value) : String(value); + + if (this.#doNotParseValues) { + valueToString = value; + } + const encryptedValue = encryptValues ? valueToString : this.#encryptation.encrypt(valueToString); @@ -138,7 +147,9 @@ export class EncryptStorage implements EncryptStorageInterface { } try { - const value = JSON.parse(decryptedValue) as T; + const value = this.#doNotParseValues + ? decryptedValue + : JSON.parse(decryptedValue); if (this.#notifyHandler && !this.#multiple) { this.#notifyHandler({ @@ -148,7 +159,7 @@ export class EncryptStorage implements EncryptStorageInterface { }); } - return value; + return value as T; } catch (error) { if (this.#notifyHandler && !this.#multiple) { this.#notifyHandler({ @@ -361,7 +372,8 @@ export class EncryptStorage implements EncryptStorageInterface { } public encryptValue(value: any): string { - const encryptedValue = this.#encryptation.encrypt(JSON.stringify(value)); + const parsedValue = this.#doNotParseValues ? value : JSON.stringify(value); + const encryptedValue = this.#encryptation.encrypt(parsedValue); return encryptedValue; } @@ -369,7 +381,9 @@ export class EncryptStorage implements EncryptStorageInterface { public decryptValue(value: string): T { const decryptedValue = this.#encryptation.decrypt(value); - return JSON.parse(decryptedValue) as T; + return ( + this.#doNotParseValues ? decryptedValue : JSON.parse(decryptedValue) + ) as T; } public hash(value: string): string { diff --git a/src/experiments/test-1.ts b/src/experiments/test-1.ts index 69e5765..8d00b62 100644 --- a/src/experiments/test-1.ts +++ b/src/experiments/test-1.ts @@ -28,6 +28,7 @@ export const makeSut = ( stateManagementUse, noOptions, encAlgorithm, + doNotParseValues = false, notifyHandler = mockNotify.mockedFn, secretKey = faker.random.alphaNumeric(10), } = params; @@ -39,6 +40,7 @@ export const makeSut = ( stateManagementUse, encAlgorithm, notifyHandler, + doNotParseValues, }; return new EncryptStorage(secretKey, options); }; @@ -97,6 +99,20 @@ export const test1 = () => expect(storagedDecrypetdValue).toEqual(value); }); + it('should localStorage.getItem returns correct decrypted value with doNotParseValues option', () => { + const safeStorage = makeSut({ + doNotParseValues: true, + }); + const values = ['[]', '{}', 'null', 'undefined', 'true', 'false', '123']; + const key = faker.random.word(); + const value = faker.random.arrayElement(values); + + safeStorage.setItem(key, value); + const storagedDecrypetdValue = safeStorage.getItem(key); + + expect(storagedDecrypetdValue).toEqual(value); + }); + it('should localStorage.getItem returns correct value but not decrypt', () => { const safeStorage = makeSut(); const key = faker.random.word(); @@ -450,6 +466,18 @@ export const test1 = () => expect(result).not.toEqual(value); }); + it('should encrypt value and return encrypted value with doNotParseValues option', () => { + const safeStorage = makeSut({ doNotParseValues: true }); + const value = { + name: faker.random.word(), + id: faker.datatype.uuid(), + }; + const stringfyValue = JSON.stringify(value); + const result = safeStorage.encryptValue(stringfyValue); + + expect(result).not.toEqual(stringfyValue); + }); + it('should decrypt string and return decrypted value', () => { const safeStorage = makeSut(); const value = faker.random.word(); @@ -472,6 +500,20 @@ export const test1 = () => expect(decryptedValue).toEqual(value); }); + it('should decrypt value and return decrypted value with doNotParseValues option', () => { + const safeStorage = makeSut({ doNotParseValues: true }); + const value = { + name: faker.random.word(), + id: faker.datatype.uuid(), + }; + const stringfyValue = JSON.stringify(value); + const encryptedValue = safeStorage.encryptValue(stringfyValue); + const decryptedValue = + safeStorage.decryptValue(encryptedValue); + + expect(decryptedValue).toEqual(stringfyValue); + }); + it('should test encryptStorage without options', () => { const safeStorage = makeSut({ noOptions: true }); const key = faker.random.word(); diff --git a/src/types.ts b/src/types.ts index 24f5443..9b250d6 100644 --- a/src/types.ts +++ b/src/types.ts @@ -33,6 +33,7 @@ export interface EncryptStorageOptions { storageType?: StorageType; encAlgorithm?: EncAlgorithm; doNotEncryptValues?: boolean; + doNotParseValues?: boolean; notifyHandler?: NotifyHandler; }