Skip to content

Commit 120ec3e

Browse files
committed
saving credential in session cookies
1 parent 955082f commit 120ec3e

File tree

10 files changed

+107
-53
lines changed

10 files changed

+107
-53
lines changed

src/config/env.config.ts

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -113,9 +113,8 @@ export type EventsWebhook = {
113113
NEW_JWT_TOKEN: boolean;
114114
};
115115

116-
export type ApiKey = { KEY: string };
117116
export type Jwt = { EXPIRIN_IN: number; SECRET: string };
118-
export type Auth = { API_KEY: ApiKey; JWT: Jwt; TYPE: 'jwt' | 'apikey' };
117+
export type Auth = { API_KEY: string; JWT: Jwt; TYPE: 'jwt' | 'apikey' };
119118

120119
export type DelInstance = number | boolean;
121120

@@ -124,7 +123,6 @@ export type SslConf = { PRIVKEY: string; FULLCHAIN: string };
124123
export type Webhook = { GLOBAL?: GlobalWebhook; EVENTS: EventsWebhook };
125124
export type ConfigSessionPhone = { CLIENT: string; NAME: string };
126125
export type QrCode = { LIMIT: number };
127-
export type Production = boolean;
128126

129127
export interface Env {
130128
SERVER: HttpServer;
@@ -139,7 +137,8 @@ export interface Env {
139137
CONFIG_SESSION_PHONE: ConfigSessionPhone;
140138
QRCODE: QrCode;
141139
AUTHENTICATION: Auth;
142-
PRODUCTION?: Production;
140+
PRODUCTION?: boolean;
141+
SESSION_SECRET: string;
143142
}
144143

145144
export type Key = keyof Env;
@@ -256,16 +255,15 @@ export class ConfigService {
256255
},
257256
AUTHENTICATION: {
258257
TYPE: process.env.AUTHENTICATION_TYPE as 'jwt',
259-
API_KEY: {
260-
KEY: process.env.AUTHENTICATION_API_KEY,
261-
},
258+
API_KEY: process.env.AUTHENTICATION_API_KEY,
262259
JWT: {
263260
EXPIRIN_IN: Number.isInteger(process.env?.AUTHENTICATION_JWT_EXPIRIN_IN)
264261
? Number.parseInt(process.env.AUTHENTICATION_JWT_EXPIRIN_IN)
265262
: 3600,
266263
SECRET: process.env.AUTHENTICATION_JWT_SECRET,
267264
},
268265
},
266+
SESSION_SECRET: process.env.SESSION_SECRET,
269267
};
270268
}
271269
}

src/dev-env.yml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -143,10 +143,11 @@ QRCODE:
143143
AUTHENTICATION:
144144
TYPE: jwt # or apikey->(deprecated)
145145
# Define a global apikey to access all instances
146-
API_KEY:
147-
# OBS: This key must be inserted in the request header to create an instance.
148-
KEY: zYzP7ocstxh3SJ23D4FZTCu4ehnM8v4hu
146+
# OBS: This key must be inserted in the request header to create an instance.
147+
API_KEY: zYzP7ocstxh3SJ23D4FZTCu4ehnM8v4hu
149148
# Set the secret key to encrypt and decrypt your token and its expiration time.
150149
JWT:
151150
EXPIRIN_IN: 0 # seconds - 3600s === 1h | zero (0) - never expires
152151
SECRET: 3RFYiiRmvNiokSBrLZzx
152+
153+
SESSION_SECRET: W0NvZGVDaGF0XTpbU2Vzc2lvbiBTZWNyZXQgS2V5Ll0=

src/main.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ import { HttpStatus, router } from './whatsapp/routers/index.router';
4646
import 'express-async-errors';
4747
import { ServerUP } from './utils/server-up';
4848
import { swaggerRouter } from './docs/swagger.conf';
49+
import session from 'express-session';
4950

5051
function initWA() {
5152
waMonitor.loadInstance();
@@ -55,6 +56,8 @@ function bootstrap() {
5556
const logger = new Logger('SERVER');
5657
const app = express();
5758

59+
logger.debug(configService.get<string>('SESSION_SECRET'));
60+
5861
app.use(
5962
cors({
6063
origin(requestOrigin, callback) {
@@ -75,6 +78,15 @@ function bootstrap() {
7578
compression(),
7679
);
7780

81+
app.use(
82+
session({
83+
secret: configService.get<string>('SESSION_SECRET'),
84+
resave: false,
85+
saveUninitialized: false,
86+
name: 'codechat.api.sid',
87+
}),
88+
);
89+
7890
app.set('view engine', 'hbs');
7991
app.set('views', join(ROOT_DIR, 'views'));
8092
app.use(express.static(join(ROOT_DIR, 'public')));

src/whatsapp/controllers/instance.controller.ts

Lines changed: 35 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ import { WAMonitoringService } from '../services/monitor.service';
5050
import { WAStartupService } from '../services/whatsapp.service';
5151
import { Logger } from '../../config/logger.config';
5252
import { RedisCache } from '../../db/redis.client';
53+
import { Request } from 'express';
5354

5455
export class InstanceController {
5556
constructor(
@@ -63,28 +64,36 @@ export class InstanceController {
6364

6465
private readonly logger = new Logger(InstanceController.name);
6566

66-
public async createInstance({ instanceName }: InstanceDto) {
67-
const instance = new WAStartupService(
68-
this.configService,
69-
this.eventEmitter,
70-
this.repository,
71-
this.cache,
72-
);
73-
instance.instanceName = instanceName;
74-
this.waMonitor.waInstances[instance.instanceName] = instance;
75-
this.waMonitor.delInstanceTime(instance.instanceName);
76-
77-
const hash = await this.authService.generateHash({
78-
instanceName: instance.instanceName,
79-
});
67+
public async createInstance({ instanceName }: InstanceDto, req: Request) {
68+
try {
69+
const instance = new WAStartupService(
70+
this.configService,
71+
this.eventEmitter,
72+
this.repository,
73+
this.cache,
74+
);
75+
instance.instanceName = instanceName;
76+
this.waMonitor.waInstances[instance.instanceName] = instance;
77+
this.waMonitor.delInstanceTime(instance.instanceName);
8078

81-
return {
82-
instance: {
79+
const hash = await this.authService.generateHash({
8380
instanceName: instance.instanceName,
84-
status: 'created',
85-
},
86-
hash,
87-
};
81+
});
82+
83+
req.session[instance.instanceName] = Buffer.from(JSON.stringify(hash)).toString(
84+
'base64',
85+
);
86+
87+
return {
88+
instance: {
89+
instanceName: instance.instanceName,
90+
status: 'created',
91+
},
92+
hash,
93+
};
94+
} catch (error) {
95+
this.logger.error(error);
96+
}
8897
}
8998

9099
public async connectToWhatsapp({ instanceName }: InstanceDto) {
@@ -146,7 +155,11 @@ export class InstanceController {
146155
}
147156
}
148157

149-
public async refreshToken(_: InstanceDto, oldToken: OldToken) {
150-
return await this.authService.refreshToken(oldToken);
158+
public async refreshToken(instance: InstanceDto, oldToken: OldToken, req: Request) {
159+
const token = await this.authService.refreshToken(oldToken);
160+
161+
req.session[instance.instanceName] = Buffer.from(JSON.stringify(token)).toString(
162+
'base64',
163+
);
151164
}
152165
}

src/whatsapp/controllers/views.controller.ts

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,16 +36,17 @@
3636
*/
3737

3838
import { Request, Response } from 'express';
39-
import { Auth, ConfigService } from '../../config/env.config';
4039
import { BadRequestException } from '../../exceptions';
4140
import { InstanceDto } from '../dto/instance.dto';
4241
import { HttpStatus } from '../routers/index.router';
4342
import { WAMonitoringService } from '../services/monitor.service';
43+
import { AuthRaw } from '../models';
44+
import { RepositoryBroker } from '../repository/repository.manager';
4445

4546
export class ViewsController {
4647
constructor(
4748
private readonly waMonit: WAMonitoringService,
48-
private readonly configService: ConfigService,
49+
private readonly repository: RepositoryBroker,
4950
) {}
5051

5152
public async qrcode(request: Request, response: Response) {
@@ -55,9 +56,25 @@ export class ViewsController {
5556
if (instance.connectionStatus.state === 'open') {
5657
throw new BadRequestException('The instance is already connected');
5758
}
58-
const type = this.configService.get<Auth>('AUTHENTICATION').TYPE;
5959

60-
return response.status(HttpStatus.OK).render('qrcode', { type, ...param });
60+
let auth: AuthRaw;
61+
62+
if (!request?.session?.[param.instanceName]) {
63+
auth = await this.repository.auth.find(param.instanceName);
64+
} else {
65+
auth = JSON.parse(
66+
Buffer.from(request.session[param.instanceName], 'base64').toString('utf8'),
67+
) as AuthRaw;
68+
}
69+
70+
const type = auth?.jwt ? 'jwt' : 'apikey';
71+
72+
return response.status(HttpStatus.OK).render('qrcode', {
73+
...param,
74+
type,
75+
auth,
76+
connectionState: instance.connectionStatus.state,
77+
});
6178
} catch (error) {
6279
console.log('ERROR: ', error);
6380
}

src/whatsapp/guards/auth.guard.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ const logger = new Logger('GUARD');
5656
async function jwtGuard(req: Request, _: Response, next: NextFunction) {
5757
const key = req.get('apikey');
5858

59-
if (configService.get<Auth>('AUTHENTICATION').API_KEY.KEY === key) {
59+
if (configService.get<Auth>('AUTHENTICATION').API_KEY === key) {
6060
return next();
6161
}
6262

@@ -103,10 +103,10 @@ async function jwtGuard(req: Request, _: Response, next: NextFunction) {
103103
* @deprecated
104104
*/
105105
async function apikey(req: Request, _: Response, next: NextFunction) {
106-
const env = configService.get<Auth>('AUTHENTICATION').API_KEY;
106+
const API_KEY = configService.get<Auth>('AUTHENTICATION').API_KEY;
107107
const key = req.get('apikey');
108108

109-
if (env.KEY === key) {
109+
if (API_KEY === key) {
110110
return next();
111111
}
112112

src/whatsapp/repository/auth.repository.ts

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ import { join } from 'path';
3939
import { Auth, ConfigService } from '../../config/env.config';
4040
import { IInsert, Repository } from '../abstract/abstract.repository';
4141
import { IAuthModel, AuthRaw } from '../models';
42-
import { readFileSync } from 'fs';
42+
import { readFileSync, readdirSync } from 'fs';
4343
import { AUTH_DIR } from '../../config/path.config';
4444

4545
export class AuthRepository extends Repository {
@@ -82,11 +82,19 @@ export class AuthRepository extends Repository {
8282
return await this.authModel.findOne({ _id: instance });
8383
}
8484

85-
return JSON.parse(
86-
readFileSync(join(AUTH_DIR, this.auth.TYPE, instance + '.json'), {
85+
let authRaw: string;
86+
87+
if (readdirSync(join(AUTH_DIR, 'jwt')).find((i) => i === instance)) {
88+
authRaw = readFileSync(join(AUTH_DIR, 'jwt', instance + '.json'), {
89+
encoding: 'utf-8',
90+
});
91+
} else {
92+
authRaw = readFileSync(join(AUTH_DIR, 'apikey', instance + '.json'), {
8793
encoding: 'utf-8',
88-
}),
89-
) as AuthRaw;
94+
});
95+
}
96+
97+
return JSON.parse(authRaw) as AuthRaw;
9098
} catch (error) {
9199
return {};
92100
}

src/whatsapp/routers/instance.router.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ export class InstanceRouter extends RouterBroker {
5353
request: req,
5454
schema: instanceNameSchema,
5555
ClassRef: InstanceDto,
56-
execute: (instance) => instanceController.createInstance(instance),
56+
execute: (instance) => instanceController.createInstance(instance, req),
5757
});
5858

5959
return res.status(HttpStatus.CREATED).json(response);
@@ -115,7 +115,8 @@ export class InstanceRouter extends RouterBroker {
115115
request: req,
116116
schema: oldTokenSchema,
117117
ClassRef: OldToken,
118-
execute: (_, data) => instanceController.refreshToken(_, data),
118+
execute: (instance, data) =>
119+
instanceController.refreshToken(instance, data, req),
119120
});
120121

121122
return res.status(HttpStatus.CREATED).json(response);

src/whatsapp/services/whatsapp.service.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -466,7 +466,7 @@ export class WAStartupService {
466466
browser,
467467
version,
468468
connectTimeoutMs: 60_000,
469-
qrTimeout: 10_000,
469+
qrTimeout: 40_000,
470470
emitOwnEvents: false,
471471
msgRetryCounterCache: this.msgRetryCounterCache,
472472
getMessage: this.getMessage as any,

views/qrcode.hbs

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@
66
<meta http-equiv="X-UA-Compatible" content="IE=edge">
77
<meta name="viewport" content="width=device-width, initial-scale=1.0">
88
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet"
9-
integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" 'unsafe-inline' crossorigin="anonymous">
9+
integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" 'unsafe-inline'
10+
crossorigin="anonymous">
1011
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"
1112
integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p"
1213
crossorigin="anonymous"></script>
@@ -28,7 +29,6 @@
2829
<div id="display-qrcode">
2930
<h2 class="mb-3 text-secondary">Connect to whatsapp</h2>
3031
<div class="input-group mb-3">
31-
<input id="input-auth" type="text" class="form-control" aria-describedby="btn-qr-g" value="" placeholder="{{type}}">
3232
<input id="input-session" type="text" class="form-control" aria-describedby="btn-qr-g" disabled
3333
value="{{instanceName}}">
3434
<button id="gen-qrcode" class="btn btn-success" type="submit">Generate qrcode</button>
@@ -48,12 +48,16 @@
4848
4949
$('#gen-qrcode').click(() => {
5050
51-
const keyAuth = '{{type}}' === 'jwt'? 'authorization': 'apikey';
52-
const valueAuth = '{{type}}' === 'jwt'? `Bearer ${$('#input-auth').val()}`: $('#input-auth').val()
51+
const header = '{{type}}' === 'apikey'
52+
? { apikey: '{{auth.apikey}}' }
53+
: { authorization: 'Bearer {{auth.jwt}}' };
54+
55+
log({header})
56+
return
5357
5458
$.ajax({
5559
url: `/instance/connect/{{instanceName}}`,
56-
headers: { [keyAuth]: valueAuth },
60+
headers,
5761
type: 'GET',
5862
success: (qrcode, status) => {
5963
$(`#update-qrcode`).remove();
@@ -74,7 +78,7 @@
7478
},
7579
error: (error) => console.log(error),
7680
});
77-
81+
7882
});
7983
</script>
8084
</body>

0 commit comments

Comments
 (0)