2022-03-09 18:37:04 +00:00
|
|
|
import { Injectable } from '@nestjs/common';
|
|
|
|
import * as crypto from 'crypto';
|
2022-03-15 17:00:15 +00:00
|
|
|
import { ConfigurationService } from 'src/modules/config/config.service';
|
2022-03-09 18:37:04 +00:00
|
|
|
import { v4 } from 'uuid';
|
2022-03-20 15:06:04 +00:00
|
|
|
import * as CSRF from 'csrf';
|
|
|
|
import { Request } from 'express';
|
2022-03-09 18:37:04 +00:00
|
|
|
|
|
|
|
const IV_LENGTH = 16;
|
|
|
|
const ALGORITHM = 'aes-256-cbc';
|
|
|
|
|
|
|
|
@Injectable()
|
|
|
|
export class TokenService {
|
2022-03-20 17:05:21 +00:00
|
|
|
public csrf = new CSRF({
|
|
|
|
saltLength: 16,
|
|
|
|
secretLength: 32,
|
|
|
|
});
|
2022-03-20 15:06:04 +00:00
|
|
|
|
2022-03-15 17:00:15 +00:00
|
|
|
constructor(private config: ConfigurationService) {}
|
|
|
|
|
2022-03-20 18:21:15 +00:00
|
|
|
public verifyCSRF(req: Request, token?: string): boolean {
|
|
|
|
return this.csrf.verify(req.session.csrf, token || req.body._csrf);
|
2022-03-20 15:06:04 +00:00
|
|
|
}
|
|
|
|
|
2022-03-09 18:37:04 +00:00
|
|
|
public generateString(length: number): string {
|
|
|
|
return crypto.randomBytes(length).toString('hex').slice(0, length);
|
|
|
|
}
|
|
|
|
|
2022-03-16 18:37:50 +00:00
|
|
|
public generateSecret(): string {
|
|
|
|
return crypto.randomBytes(256 / 8).toString('hex');
|
|
|
|
}
|
|
|
|
|
2022-03-09 18:37:04 +00:00
|
|
|
public createUUID(): string {
|
|
|
|
return v4();
|
|
|
|
}
|
|
|
|
|
2022-03-09 20:03:29 +00:00
|
|
|
// https://stackoverflow.com/q/52212430
|
2022-03-09 18:37:04 +00:00
|
|
|
/**
|
|
|
|
* Symmetric encryption function
|
|
|
|
* @param text String to encrypt
|
|
|
|
* @param key Encryption key
|
|
|
|
* @returns Encrypted text
|
|
|
|
*/
|
|
|
|
public encrypt(text: string, key: string): string {
|
|
|
|
const iv = crypto.randomBytes(IV_LENGTH);
|
|
|
|
const cipher = crypto.createCipheriv(
|
|
|
|
ALGORITHM,
|
|
|
|
Buffer.from(key, 'hex'),
|
|
|
|
iv,
|
|
|
|
);
|
|
|
|
let encrypted = cipher.update(text);
|
|
|
|
encrypted = Buffer.concat([encrypted, cipher.final()]);
|
|
|
|
return `${iv.toString('hex')}:${encrypted.toString('hex')}`;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Symmetric decryption function
|
|
|
|
* @param text Encrypted string
|
|
|
|
* @param key Decryption key
|
|
|
|
* @returns Decrypted text
|
|
|
|
*/
|
|
|
|
public decrypt(text: string, key: string): string {
|
|
|
|
const [iv, encryptedText] = text
|
|
|
|
.split(':')
|
|
|
|
.map((part) => Buffer.from(part, 'hex'));
|
|
|
|
|
|
|
|
const decipher = crypto.createDecipheriv(
|
|
|
|
ALGORITHM,
|
|
|
|
Buffer.from(key, 'hex'),
|
|
|
|
iv,
|
|
|
|
);
|
|
|
|
|
|
|
|
let decrypted = decipher.update(encryptedText);
|
|
|
|
decrypted = Buffer.concat([decrypted, decipher.final()]);
|
|
|
|
return decrypted.toString();
|
|
|
|
}
|
|
|
|
|
|
|
|
public async encryptChallenge(
|
|
|
|
challenge: Record<string, any>,
|
|
|
|
): Promise<string> {
|
|
|
|
return this.encrypt(
|
|
|
|
JSON.stringify(challenge),
|
2022-03-15 17:00:15 +00:00
|
|
|
this.config.get<string>('app.challenge_secret'),
|
2022-03-09 18:37:04 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
public async decryptChallenge(
|
|
|
|
challenge: string,
|
|
|
|
): Promise<Record<string, any>> {
|
2022-03-15 17:00:15 +00:00
|
|
|
return JSON.parse(
|
|
|
|
this.decrypt(challenge, this.config.get<string>('app.challenge_secret')),
|
|
|
|
);
|
2022-03-09 18:37:04 +00:00
|
|
|
}
|
|
|
|
}
|