Skip to content

Commit

Permalink
feat(webchat): local storage encryption (#377)
Browse files Browse the repository at this point in the history
* feat(webchat): local storage encryption

* fix

* inject encryption key

* fix

* bump
  • Loading branch information
samuelmasse committed Feb 18, 2022
1 parent 4ff43f0 commit be16c91
Show file tree
Hide file tree
Showing 6 changed files with 72 additions and 9 deletions.
4 changes: 2 additions & 2 deletions packages/inject/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@botpress/webchat-inject",
"version": "0.2.2",
"version": "0.2.3",
"license": "AGPL-3.0",
"scripts": {
"build": "yarn && yarn run -T parcel build src/index.html src/inject.js --public-url ./",
Expand All @@ -20,7 +20,7 @@
"@types/react-dom": "^17.0.11"
},
"dependencies": {
"@botpress/webchat": "0.2.1",
"@botpress/webchat": "0.2.3",
"react": "^17.0.2",
"react-dom": "^17.0.2"
}
Expand Down
21 changes: 20 additions & 1 deletion packages/inject/src/inject.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,15 @@ function _injectDOMElement(tagName, selector, options) {
}

function _generateIFrameHTML(host, config) {
const options = encodeURIComponent(JSON.stringify({ config: config }))
const keyStorage = `bp-chat-key-${config.clientId}`
let encryptionKey = localStorage.getItem(keyStorage)

if (!encryptionKey) {
encryptionKey = _generateRandomString(32)
localStorage.setItem(keyStorage, encryptionKey)
}

const options = encodeURIComponent(JSON.stringify({ config: { ...config, encryptionKey } }))
const title = config.botConvoDescription || config.botName || config.botId
const iframeSrc = host + '/index.html?options=' + options
const iframeId = _getIframeId(config.chatId)
Expand All @@ -42,6 +50,17 @@ function _generateIFrameHTML(host, config) {
)
}

function _generateRandomString(length) {
let chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'

let str = ''
for (let i = 0; i < length; i++) {
str += chars.charAt(Math.floor(Math.random() * chars.length))
}

return str
}

const chatRefs = {}

// provides proper chat reference
Expand Down
4 changes: 3 additions & 1 deletion packages/webchat/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@botpress/webchat",
"version": "0.2.1",
"version": "0.2.3",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"source": "src/index.tsx",
Expand All @@ -14,6 +14,7 @@
"dist"
],
"devDependencies": {
"@types/crypto-js": "^4.1.1",
"@types/lodash": "^4.14.178",
"@types/mime": "^2.0.3",
"@types/node": "^16.11.13",
Expand All @@ -31,6 +32,7 @@
"@juggle/resize-observer": "^3.0.2",
"axios": "^0.25.0",
"classnames": "^2.3.1",
"crypto-js": "^4.1.1",
"date-fns": "^1.30.1",
"lodash": "^4.17.21",
"mime": "^3.0.0",
Expand Down
30 changes: 27 additions & 3 deletions packages/webchat/src/core/socket.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
import { Message, MessagingSocket, UserCredentials } from '@botpress/messaging-socket'
import AES from 'crypto-js/aes'
import utf8 from 'crypto-js/enc-utf8'
import SHA256 from 'crypto-js/sha256'
import { Config } from '../typings'

export default class BpSocket {
public socket: MessagingSocket
private chatId: string | undefined
private encryptionKey: string | undefined
private clientId: string | undefined
private waitingForUser?: Promise<void>

public onClear!: (event: any) => void
Expand All @@ -14,6 +19,8 @@ export default class BpSocket {

constructor(config: Config) {
this.chatId = config.chatId
this.encryptionKey = config.encryptionKey
this.clientId = config.clientId
this.socket = new MessagingSocket({ url: config.messagingUrl, clientId: config.clientId })
}

Expand Down Expand Up @@ -71,11 +78,16 @@ export default class BpSocket {
}

public getStorage<T>(key: string): T | undefined {
const stored = localStorage.getItem(this.getStorageKey(key))
let stored = localStorage.getItem(this.getStorageKey(key))

if (!stored) {
return undefined
}

if (this.encryptionKey?.length) {
stored = AES.decrypt(stored, this.encryptionKey).toString(utf8)
}

try {
const val = JSON.parse(stored)
return val
Expand All @@ -85,10 +97,22 @@ export default class BpSocket {
}

public setStorage<T>(key: string, object: T) {
localStorage.setItem(this.getStorageKey(key), JSON.stringify(object))
let string = JSON.stringify(object)

if (this.encryptionKey?.length) {
string = AES.encrypt(string, this.encryptionKey).toString()
}

localStorage.setItem(this.getStorageKey(key), string)
}

private getStorageKey(key: string) {
return `bp-chat-${key}`
const rawKey = `bp-chat-${key}`

if (this.encryptionKey?.length) {
return `${rawKey}-${SHA256(`${this.clientId}-${this.encryptionKey}`).toString()}`
} else {
return `${rawKey}-${this.clientId}`
}
}
}
2 changes: 2 additions & 0 deletions packages/webchat/src/typings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,8 @@ export interface Config {
chatId?: string
/** CSS class to be applied to iframe */
className?: string
/** Key used to encrypt data in the localStorage */
encryptionKey?: string
}

export type OverridableComponents = 'below_conversation' | 'before_container' | 'composer'
Expand Down
20 changes: 18 additions & 2 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2393,7 +2393,7 @@ __metadata:
version: 0.0.0-use.local
resolution: "@botpress/webchat-inject@workspace:packages/inject"
dependencies:
"@botpress/webchat": 0.2.1
"@botpress/webchat": 0.2.3
"@parcel/config-default": ^2.2.1
"@parcel/reporter-bundle-analyzer": 2.2.1
"@parcel/transformer-typescript-tsc": ^2.2.1
Expand All @@ -2404,7 +2404,7 @@ __metadata:
languageName: unknown
linkType: soft

"@botpress/webchat@*, @botpress/webchat@0.2.1, @botpress/webchat@workspace:packages/webchat":
"@botpress/webchat@*, @botpress/webchat@0.2.3, @botpress/webchat@workspace:packages/webchat":
version: 0.0.0-use.local
resolution: "@botpress/webchat@workspace:packages/webchat"
dependencies:
Expand All @@ -2414,6 +2414,7 @@ __metadata:
"@formatjs/intl-pluralrules": ^4.1.6
"@formatjs/intl-utils": ^3.8.4
"@juggle/resize-observer": ^3.0.2
"@types/crypto-js": ^4.1.1
"@types/lodash": ^4.14.178
"@types/mime": ^2.0.3
"@types/node": ^16.11.13
Expand All @@ -2422,6 +2423,7 @@ __metadata:
"@types/uuid": ^8.3.4
axios: ^0.25.0
classnames: ^2.3.1
crypto-js: ^4.1.1
date-fns: ^1.30.1
lodash: ^4.17.21
mime: ^3.0.0
Expand Down Expand Up @@ -5912,6 +5914,13 @@ __metadata:
languageName: node
linkType: hard

"@types/crypto-js@npm:^4.1.1":
version: 4.1.1
resolution: "@types/crypto-js@npm:4.1.1"
checksum: ea3d6a67b69f88baeb6af96004395903d2367a41bd5cd86306da23a44dd96589749495da50974a9b01bb5163c500764c8a33706831eade036bddae016417e3ea
languageName: node
linkType: hard

"@types/debug@npm:^4.1.4":
version: 4.1.7
resolution: "@types/debug@npm:4.1.7"
Expand Down Expand Up @@ -9855,6 +9864,13 @@ __metadata:
languageName: node
linkType: hard

"crypto-js@npm:^4.1.1":
version: 4.1.1
resolution: "crypto-js@npm:4.1.1"
checksum: b3747c12ee3a7632fab3b3e171ea50f78b182545f0714f6d3e7e2858385f0f4101a15f2517e033802ce9d12ba50a391575ff4638c9de3dd9b2c4bc47768d5425
languageName: node
linkType: hard

"css-declaration-sorter@npm:^6.0.3":
version: 6.1.3
resolution: "css-declaration-sorter@npm:6.1.3"
Expand Down

0 comments on commit be16c91

Please sign in to comment.