refactor: remove widget token

This commit is contained in:
Mohamed Marrouchi 2024-11-10 12:11:42 +01:00
parent 81a507d091
commit 9df4fbbdad
19 changed files with 34 additions and 101 deletions

View File

@ -1,5 +1,4 @@
{
"verification_token": "Verification Token",
"allowed_domains": "Allowed Domains",
"start_button": "Enable `Get Started`",
"input_disabled": "Disable Input",

View File

@ -1,5 +1,4 @@
{
"verification_token": "Jeton de vérification",
"allowed_domains": "Domaines autorisés",
"start_button": "Activer `Démarrer`",
"input_disabled": "Désactiver la saisie",

View File

@ -17,12 +17,6 @@ export const CONSOLE_CHANNEL_NAME = 'console-channel';
export const CONSOLE_CHANNEL_NAMESPACE = 'console_channel';
export default [
{
group: CONSOLE_CHANNEL_NAMESPACE,
label: Web.SettingLabel.verification_token,
value: 'test',
type: SettingType.text,
},
{
group: CONSOLE_CHANNEL_NAMESPACE,
label: Web.SettingLabel.allowed_domains,

View File

@ -77,7 +77,7 @@ export default abstract class BaseWebChannelHandler<
protected readonly attachmentService: AttachmentService,
protected readonly messageService: MessageService,
protected readonly menuService: MenuService,
private readonly websocketGateway: WebsocketGateway,
protected readonly websocketGateway: WebsocketGateway,
) {
super(name, settingService, channelService, logger);
}
@ -98,42 +98,32 @@ export default abstract class BaseWebChannelHandler<
*/
@OnEvent('hook:websocket:connection', { async: true })
async onWebSocketConnection(client: Socket) {
const settings = await this.getSettings();
const handshake = client.handshake;
const { channel } = handshake.query;
if (channel !== this.getName()) {
return;
}
try {
const { verification_token } = client.handshake.query;
await this.verifyToken(verification_token.toString());
try {
this.logger.debug(
'Web Channel Handler : WS connected .. sending settings',
);
try {
const menu = await this.menuService.getTree();
return client.emit('settings', { menu, ...settings });
} catch (err) {
this.logger.warn(
'Web Channel Handler : Unable to retrieve menu ',
err,
);
return client.emit('settings', settings);
}
} catch (err) {
this.logger.warn(
'Web Channel Handler : Unable to verify token, disconnecting ...',
err,
);
client.disconnect();
const settings = await this.getSettings();
const handshake = client.handshake;
const { channel } = handshake.query;
if (channel !== this.getName()) {
return;
}
this.logger.debug(
'Web Channel Handler : WS connected .. sending settings',
);
try {
const menu = await this.menuService.getTree();
return client.emit('settings', { menu, ...settings });
} catch (err) {
this.logger.warn('Web Channel Handler : Unable to retrieve menu ', err);
return client.emit('settings', settings);
}
} catch (err) {
this.logger.error(
'Web Channel Handler : Unable to initiate websocket connection',
err,
);
client.disconnect();
}
}
@ -218,7 +208,7 @@ export default abstract class BaseWebChannelHandler<
*
* @returns Formatted message
*/
private formatHistoryMessages(messages: AnyMessage[]): Web.Message[] {
protected formatMessages(messages: AnyMessage[]): Web.Message[] {
return messages.map((anyMessage: AnyMessage) => {
if ('sender' in anyMessage && anyMessage.sender) {
return {
@ -262,7 +252,7 @@ export default abstract class BaseWebChannelHandler<
until,
n,
);
return this.formatHistoryMessages(messages.reverse());
return this.formatMessages(messages.reverse());
}
return [];
}
@ -287,35 +277,11 @@ export default abstract class BaseWebChannelHandler<
since,
n,
);
return this.formatHistoryMessages(messages);
return this.formatMessages(messages);
}
return [];
}
/**
* Verify the received token.
*
* @param verificationToken - Verification Token
*/
private async verifyToken(verificationToken: string) {
const settings =
(await this.getSettings()) as unknown as Settings[typeof WEB_CHANNEL_NAMESPACE];
const verifyToken = settings.verification_token;
if (!verifyToken) {
throw new Error('You need to specify a verifyToken in your config.');
}
if (!verificationToken) {
throw new Error('Did not recieve any verification token.');
}
if (verificationToken !== verifyToken) {
throw new Error('Make sure the validation tokens match.');
}
this.logger.log(
'Web Channel Handler : Token has been verified successfully!',
);
}
/**
* Verify the origin against whitelisted domains.
*
@ -405,20 +371,12 @@ export default abstract class BaseWebChannelHandler<
* @param req
* @param res
*/
private async checkRequest(
protected async checkRequest(
req: Request | SocketRequest,
res: Response | SocketResponse,
) {
try {
await this.validateCors(req, res);
try {
const { verification_token } =
'verification_token' in req.query ? req.query : req.body;
await this.verifyToken(verification_token);
} catch (err) {
this.logger.warn('Web Channel Handler : Unable to verify token ', err);
throw new Error('Unauthorized, invalid token!');
}
} catch (err) {
this.logger.warn(
'Web Channel Handler : Attempt to access from an unauthorized origin',
@ -741,7 +699,7 @@ export default abstract class BaseWebChannelHandler<
*
* @returns IP Address
*/
private getIpAddress(req: Request | SocketRequest): string {
protected getIpAddress(req: Request | SocketRequest): string {
if ('isSocket' in req && req.isSocket) {
return req.socket.handshake.address;
} else if (Array.isArray(req.ips) && req.ips.length > 0) {

View File

@ -1,5 +1,4 @@
{
"verification_token": "Verification Token",
"allowed_domains": "Allowed Domains",
"start_button": "Enable `Get Started`",
"input_disabled": "Disable Input",

View File

@ -1,5 +1,4 @@
{
"verification_token": "Jeton de vérification",
"allowed_domains": "Domaines autorisés",
"start_button": "Activer `Démarrer`",
"input_disabled": "Désactiver la saisie",

View File

@ -16,12 +16,6 @@ export const WEB_CHANNEL_NAME = 'web-channel' as const;
export const WEB_CHANNEL_NAMESPACE = 'web_channel';
export default [
{
group: WEB_CHANNEL_NAMESPACE,
label: Web.SettingLabel.verification_token,
value: 'token123',
type: SettingType.secret,
},
{
group: WEB_CHANNEL_NAMESPACE,
label: Web.SettingLabel.allowed_domains,

View File

@ -13,8 +13,6 @@ import { StdQuickReply } from '@/chat/schemas/types/quick-reply';
export namespace Web {
export enum SettingLabel {
secret = 'secret',
verification_token = 'verification_token',
allowed_domains = 'allowed_domains',
start_button = 'start_button',
input_disabled = 'input_disabled',

View File

@ -224,7 +224,7 @@ export class WebsocketGateway
'Unable to load session, creating a new one ...',
err,
);
if (searchParams.get('channel') === 'web-channel') {
if (searchParams.get('channel') !== 'console-channel') {
return this.createAndStoreSession(client, next);
} else {
return next(new Error('Unauthorized: Unknown session ID'));

View File

@ -58,4 +58,3 @@ NEXT_PUBLIC_SSO_ENABLED=false
APP_WIDGET_PORT=5173
REACT_APP_WIDGET_API_URL=http://${APP_DOMAIN}:${API_PORT}
REACT_APP_WIDGET_CHANNEL=web-channel
REACT_APP_WIDGET_TOKEN=token123

View File

@ -34,7 +34,6 @@ export const ChatWidget = () => {
config={{
apiUrl,
channel: "console-channel",
token: "test",
language: i18n.language,
}}
CustomHeader={ChatWidgetHeader}

View File

@ -7,11 +7,9 @@ COPY package*.json ./
# Set the environment variables
ARG REACT_APP_WIDGET_API_URL
ARG REACT_APP_WIDGET_CHANNEL
ARG REACT_APP_WIDGET_TOKEN
ENV REACT_APP_WIDGET_API_URL=${REACT_APP_WIDGET_API_URL}
ENV REACT_APP_WIDGET_CHANNEL=${REACT_APP_WIDGET_CHANNEL}
ENV REACT_APP_WIDGET_TOKEN=${REACT_APP_WIDGET_TOKEN}
# Installer stage: Installs dependencies
FROM base AS installer

View File

@ -20,7 +20,6 @@ import { useColors } from '../providers/ColorProvider';
import { useConfig } from '../providers/ConfigProvider';
import { useSettings } from '../providers/SettingsProvider';
import { useSocket } from '../providers/SocketProvider';
import './UserSubscription.scss';
import { useWidget } from '../providers/WidgetProvider';
import {
Direction,
@ -28,6 +27,7 @@ import {
TMessage,
TOutgoingMessageType,
} from '../types/message.types';
import './UserSubscription.scss';
const UserSubscription: React.FC = () => {
const config = useConfig();
@ -55,7 +55,7 @@ const UserSubscription: React.FC = () => {
messages: TMessage[];
profile: ISubscriber;
}>(
`/webhook/${config.channel}/?verification_token=${config.token}&first_name=${firstName}&last_name=${lastName}`,
`/webhook/${config.channel}/?first_name=${firstName}&last_name=${lastName}`,
);
const { messages, profile } = body;

View File

@ -9,6 +9,5 @@
export const DEFAULT_CONFIG = {
apiUrl: process.env.REACT_APP_WIDGET_API_URL || 'http://localhost:4000',
channel: process.env.REACT_APP_WIDGET_CHANNEL || 'console-channel',
token: process.env.REACT_APP_WIDGET_TOKEN || 'test',
language: 'en',
};

View File

@ -19,7 +19,6 @@ ReactDOM.createRoot(document.getElementById('root')!).render(
{...{
apiUrl: process.env.REACT_APP_WIDGET_API_URL || 'http://localhost:4000',
channel: process.env.REACT_APP_WIDGET_CHANNEL || 'web-channel',
token: process.env.REACT_APP_WIDGET_TOKEN || 'token123',
language: 'en',
}}
/>

View File

@ -289,7 +289,7 @@ const ChatProvider: React.FC<{
);
setMessage('');
const sentMessage = await socketCtx.socket.post<TMessage>(
`/webhook/${config.channel}/?verification_token=${config.token}`,
`/webhook/${config.channel}/`,
{
data: {
...data,
@ -308,7 +308,7 @@ const ChatProvider: React.FC<{
messages: TMessage[];
profile: ISubscriber;
}>(
`/webhook/${config.channel}/?verification_token=${config.token}&first_name=${firstName}&last_name=${lastName}`,
`/webhook/${config.channel}/?first_name=${firstName}&last_name=${lastName}`,
);
localStorage.setItem('profile', JSON.stringify(body.profile));

View File

@ -13,7 +13,6 @@ import { DEFAULT_CONFIG } from '../constants/defaultConfig';
// Define the type for your config, including all possible properties
export type Config = {
apiUrl: string;
token: string;
channel: string;
language: string;
};
@ -23,7 +22,6 @@ const ConfigContext = createContext<Config>(DEFAULT_CONFIG);
export const ConfigProvider: React.FC<{
apiUrl?: string;
token?: string;
channel?: string;
language?: string;
children: ReactNode;

View File

@ -23,7 +23,6 @@ import { useSubscribe } from './SocketProvider';
type ChannelSettings = {
menu: IMenuNode[];
secret: string;
verification_token: string;
allowed_domains: string;
start_button: boolean;
input_disabled: boolean;

View File

@ -6,7 +6,7 @@
* 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file).
*/
import { io, Socket, ManagerOptions, SocketOptions } from 'socket.io-client';
import { io, ManagerOptions, Socket, SocketOptions } from 'socket.io-client';
import { Config } from '../providers/ConfigProvider';
import {
@ -195,14 +195,16 @@ let socketIoClient: SocketIoClient;
* @param handlers Event handlers
* @returns Socket io client instance
*/
export const getSocketIoClient = (config: Config, handlers: SocketIoEventHandlers) => {
export const getSocketIoClient = (
config: Config,
handlers: SocketIoEventHandlers,
) => {
if (!socketIoClient) {
socketIoClient = new SocketIoClient(
config.apiUrl,
{
query: {
channel: config.channel,
verification_token: config.token,
},
},
handlers,