Skip to content

Commit

Permalink
feat(cookie): implements cookie encriptation (#720)
Browse files Browse the repository at this point in the history
  • Loading branch information
michelonsouza authored Dec 24, 2024
1 parent 49e66a6 commit b383b0d
Show file tree
Hide file tree
Showing 7 changed files with 539 additions and 48 deletions.
95 changes: 94 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ Using the [`crypto-js`](https://github.com/brix/crypto-js) library as an encrypt
- [NextJS](#nextjs)
- [AsyncEncryptStorage](#asyncencryptstorage)
- [AWS Amplify](#aws-amplify)
- [Cookie](#cookie)
- [_set_](#set)
- [_get_](#get)
- [_remove_](#remove)
- [State Management Persisters](#state-management-persisters)
- [_vuex-persist_](#vuex-persist)
- [_redux-persist_](#redux-persist)
Expand All @@ -60,7 +64,7 @@ Using the [`crypto-js`](https://github.com/brix/crypto-js) library as an encrypt

## Features

- Save encrypted data in `localStorage` and `sessionStorage`
- Save encrypted data in `localStorage`, `sessionStorage` and `cookies`
- Recover encrypted data with `get` functions
- Use in the same way as native `Web Storage` (localStorage and sessionStorage)
- If you use the `stateManagementUse` option, the data acquired in `get` functions will `not` have their return transformed into `Javascript objects`.
Expand Down Expand Up @@ -705,6 +709,95 @@ async function getDecryptedValue('key'): Promise<any | undefined> {
}
```

### Cookie

Encryptstorage can also be used to encrypt data in cookies. See below for ways to use it.

#### _set_

Set a `encrypted` cookie value passed by parameter.

```typescript
import { EncryptStorage } from 'encrypt-storage';
const encryptStorage = new EncryptStorage('secret-key-value', {
prefix: '@encrypt-storage',
});

encryptStorage.cookie.set('any-key', { value: 'any-value' });

// document.cookie
// any-key=U2FsdGVkX1/2KEwOH+w4QaIcyq5521ZXB5pqw...
```

You can pass parameters to the set method, which are normally used in cookies. View params in [CookieOptions](./src/types.ts#L62).

```typescript
import { EncryptStorage } from 'encrypt-storage';
const encryptStorage = new EncryptStorage('secret-key-value', {
prefix: '@encrypt-storage',
});

encryptStorage.cookie.set(
'any-key',
{ value: 'any-value' },
{
path: '/',
domain: 'example.com',
expires: new Date(Date.now() + 86400000),
secure: true,
sameSite: 'strict',
},
);

// document.cookie
// any-key=U2FsdGVkX1/2KEwOH+w4QaIcyq5521ZXB5pqw; path=/; domain=example.com; expires=Tue, 24 Dec 2024 18:51:07 GMT; secure
```

#### _get_

Set a `encrypted` cookie value passed by parameter.

```typescript
import { EncryptStorage } from 'encrypt-storage';
const encryptStorage = new EncryptStorage('secret-key-value', {
prefix: '@encrypt-storage',
});

const value = { value: 'any-value' };

encryptStorage.cookie.set('any-key', value);

// document.cookie
// any-key=U2FsdGVkX1/2KEwOH+w4QaIcyq5521ZXB5pqw...

const decryptedValue = encryptStorage.cookie.get('any-key');

// { value: 'any-value' }
```

#### _remove_

Set a `encrypted` cookie value passed by parameter.

```typescript
import { EncryptStorage } from 'encrypt-storage';
const encryptStorage = new EncryptStorage('secret-key-value', {
prefix: '@encrypt-storage',
});

const value = { value: 'any-value' };

encryptStorage.cookie.set('any-key', value);

// document.cookie
// any-key=U2FsdGVkX1/2KEwOH+w4QaIcyq5521ZXB5pqw...

encryptStorage.cookie.remove('any-key');

// document.cookie
// ''
```

### AWS Amplify

In the case of `aws-amplify`, if you want to use the facility of not needing to use `JSON.parse` in the rest of the application, prefer to create an instance within the `amplify` configuration file, as follows:
Expand Down
2 changes: 2 additions & 0 deletions __tests__/index.spec.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { test1 } from '../src/experiments/test-1';
import { test2 } from '../src/experiments/test-2';
import { test3 } from '../src/experiments/test-3';
import { test4 } from '../src/experiments/test-4';

describe('TestSuite', () => {
test4();
test1();
test2();
test3();
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "encrypt-storage",
"version": "2.13.04",
"version": "2.14.00",
"description": "Wrapper for encrypted localStorage and sessionStorage in browser",
"main": "dist/index.js",
"types": "dist/index.d.ts",
Expand Down
124 changes: 117 additions & 7 deletions src/encrypt-storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ import {
EncryptStorageOptions,
EncryptStorageInterface,
RemoveFromPatternOptions,
CookieInterface,
CookieOptions,
RemoveCookieOptions,
} from './types';
import { getEncryptation, hashSHA256, hashMD5 } from './utils';

Expand Down Expand Up @@ -125,7 +128,10 @@ export class EncryptStorage implements EncryptStorageInterface {
}
}

public getItem<T = any>(key: string, doNotDecrypt = false): T | undefined {
public getItem<DataType = any>(
key: string,
doNotDecrypt = false,
): DataType | undefined {
const decryptValues = this.#doNotEncryptValues || doNotDecrypt;
const storageKey = this.#getKey(key);
const item = this.storage?.getItem(storageKey);
Expand All @@ -143,7 +149,7 @@ export class EncryptStorage implements EncryptStorageInterface {
value: decryptedValue,
});
}
return decryptedValue as unknown as T;
return decryptedValue as unknown as DataType;
}

try {
Expand All @@ -159,16 +165,16 @@ export class EncryptStorage implements EncryptStorageInterface {
});
}

return value as T;
} catch (error) {
return value as DataType;
} catch {
if (this.#notifyHandler && !this.#multiple) {
this.#notifyHandler({
type: 'get',
key,
value: decryptedValue,
});
}
return decryptedValue as unknown as T;
return decryptedValue as unknown as DataType;
}
}

Expand Down Expand Up @@ -378,12 +384,12 @@ export class EncryptStorage implements EncryptStorageInterface {
return encryptedValue;
}

public decryptValue<T = any>(value: string): T {
public decryptValue<DataType = any>(value: string): DataType {
const decryptedValue = this.#encryptation.decrypt(value);

return (
this.#doNotParseValues ? decryptedValue : JSON.parse(decryptedValue)
) as T;
) as DataType;
}

public hash(value: string): string {
Expand All @@ -393,6 +399,110 @@ export class EncryptStorage implements EncryptStorageInterface {
public md5Hash(value: string): string {
return hashMD5(value, secret.get(this));
}

public cookie: CookieInterface = {
set: (key: string, value: any, options?: CookieOptions): void => {
if (
typeof document === 'undefined' ||
typeof document.cookie === 'undefined' ||
typeof window === 'undefined'
) {
return;
}
let interntValue = this.#doNotParseValues ? value : JSON.stringify(value);

if (!this.#doNotEncryptValues) {
interntValue = this.encryptValue(interntValue);
}

let cookieString = `${encodeURIComponent(this.#getKey(key))}=${encodeURIComponent(interntValue)}`;

if (options?.expires) {
const expires =
options.expires instanceof Date
? options.expires.toUTCString()
: new Date(Date.now() + options.expires * 1000).toUTCString();
cookieString += `; expires=${expires}`;
}

if (options?.path) {
cookieString += `; path=${options.path}`;
}

if (options?.domain) {
cookieString += `; domain=${options.domain}`;
}

if (options?.secure) {
cookieString += `; secure`;
}
if (options?.sameSite) {
cookieString += `; samesite=${options.sameSite}`;
}

document.cookie = cookieString;

if (this.#notifyHandler) {
this.#notifyHandler({
type: 'set:cookie',
key,
value: undefined,
});
}
},
get: <DataType = any>(key: string): DataType | null => {
if (
typeof document === 'undefined' ||
typeof document.cookie === 'undefined' ||
typeof window === 'undefined'
) {
return null;
}

const match = document.cookie.match(
new RegExp(`(?:^|; )${encodeURIComponent(this.#getKey(key))}=([^;]*)`),
);

let internValue = match ? match[1] : null;

if (!this.#doNotEncryptValues && internValue) {
internValue = this.decryptValue(decodeURIComponent(internValue));
}

if (this.#doNotParseValues) {
return internValue as unknown as DataType;
}

if (this.#notifyHandler) {
this.#notifyHandler({
type: 'get:cookie',
key,
value: undefined,
});
}

return internValue ? (JSON.parse(internValue) as DataType) : null;
},
remove: (key: string, options: RemoveCookieOptions = {}): void => {
if (
typeof document === 'undefined' ||
typeof document.cookie === 'undefined' ||
typeof window === 'undefined'
) {
return;
}

this.cookie.set(this.#getKey(key), '', { ...options, expires: -1 });

if (this.#notifyHandler) {
this.#notifyHandler({
type: 'remove:cookie',
key,
value: undefined,
});
}
},
};
}

/* istanbul ignore next */
Expand Down
Loading

0 comments on commit b383b0d

Please sign in to comment.