UNPKG

@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
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