@piggly/fastify-chassis
Version:
An ESM/CommonJS toolkit to help you to do common operations in your back-end applications with Fastify and NodeJS.
191 lines • 5.21 kB
JavaScript
import crypto from 'node:crypto';
import { getHeaderValue } from '../utils/index.js';
/**
* @file CSRF token service.
* @copyright Piggly Lab 2025
* @since 7.0.0
* @author Caique Araujo <caique@piggly.com.br>
*/
export class CSRFTokenService {
/**
* Cookie.
*
* @type {CookieBuilderService}
* @protected
* @memberof CSRFTokenService
* @since 7.0.0
* @author Caique Araujo <caique@piggly.com.br>
*/
_cookie;
/**
* Secret.
*
* @type {string}
* @protected
* @memberof CSRFTokenService
* @since 7.0.0
* @author Caique Araujo <caique@piggly.com.br>
*/
_secret;
/**
* Constructor.
*
* @param {CookieBuilderService} cookie
* @param {string} secret
* @memberof CSRFTokenService
* @since 7.0.0
* @author Caique Araujo <caique@piggly.com.br>
*/
constructor(cookie, secret) {
this._cookie = cookie;
this._secret = secret;
}
/**
* Set the CSRF token as a cookie.
*
* @param {FastifyReply} reply
* @param {string} name
* @param {CookieOptions} options
* @returns {FastifyReply}
* @memberof CSRFTokenService
* @since 7.0.0
* @author Caique Araujo <caique@piggly.com.br>
*/
asCookie(reply, name = 'csrf_token', options) {
const token = this.asRaw();
this._cookie.set(reply, name, token, options);
return reply;
}
/**
* Set the CSRF token as a header.
*
* @param {FastifyReply} reply
* @param {string} header
* @returns {FastifyReply}
* @memberof CSRFTokenService
* @since 7.0.0
* @author Caique Araujo <caique@piggly.com.br>
*/
asHeader(reply, header = 'X-Csrf-Token') {
const token = this.asRaw();
reply.header(header, token);
return reply;
}
/**
* Generate a CSRF token.
*
* It will be generated following the format:
* <token>.<signature>
*
* @returns {string}
* @memberof CSRFTokenService
* @since 7.0.0
* @author Caique Araujo <caique@piggly.com.br>
*/
asRaw() {
const token = CSRFTokenService.generateClientSecret(32);
const signature = CSRFTokenService.sign(token, this._secret);
return `${token}.${signature}`;
}
/**
* Verify a CSRF token.
*
* @param {string} token
* @returns {boolean}
* @memberof CSRFTokenService
* @since 7.0.0
* @author Caique Araujo <caique@piggly.com.br>
*/
verify(token) {
if (!token) {
return false;
}
const [_token = '', _signature = ''] = String(token).split('.');
return CSRFTokenService.verify(_token, _signature, this._secret);
}
/**
* Verify a CSRF token from a cookie.
*
* @param {FastifyRequest} request
* @param {string} cookie
* @returns {boolean}
* @memberof CSRFTokenService
* @since 7.0.0
* @author Caique Araujo <caique@piggly.com.br>
*/
verifyCookie(request, cookie = 'csrf_token') {
const token = this._cookie.get(request, cookie, '');
return this.verify(token);
}
/**
* Verify a CSRF token from a header.
*
* @param {FastifyRequest} request
* @param {string} header
* @returns {boolean}
* @memberof CSRFTokenService
* @since 7.0.0
* @author Caique Araujo <caique@piggly.com.br>
*/
verifyHeader(request, header = 'X-Csrf-Token') {
const token = getHeaderValue(request, header);
return this.verify(token);
}
/**
* Random generates a client secret.
*
* @param {number} [size=36]
* @public
* @static
* @memberof CSRFTokenService
* @since 7.6.0
* @author Caique Araujo <caique@piggly.com.br>
*/
static generateClientSecret(size = 36) {
const buffer = crypto.randomBytes(size);
return buffer
.toString('base64')
.replace(/\//g, '_')
.replace(/\+/g, '-')
.replace(/=/g, '');
}
/**
* Sign a string with a specific key HMAC sha256.
*
* @param {string} data
* @param {string} key
* @public
* @static
* @memberof CSRFTokenService
* @since 7.6.0
* @author Caique Araujo <caique@piggly.com.br>
*/
static sign(data, key) {
return crypto.createHmac('sha256', key).update(data).digest('hex');
}
/**
* Verify a string with a specific key HMAC sha256.
*
* @param {string} data
* @param {string} key
* @param {string} signature
* @public
* @static
* @memberof CSRFTokenService
* @since 7.6.0
* @author Caique Araujo <caique@piggly.com.br>
*/
static verify(data, signature, key, onError) {
try {
const generatedSignature = CSRFTokenService.sign(data, key);
return crypto.timingSafeEqual(Buffer.from(generatedSignature, 'hex'), Buffer.from(signature, 'hex'));
}
catch (err) {
if (onError) {
onError(err);
}
return false;
}
}
}
//# sourceMappingURL=CSRFTokenService.js.map