@piggly/fastify-chassis
Version:
An ESM/CommonJS toolkit to help you to do common operations in your back-end applications with Fastify and NodeJS.
130 lines • 3.74 kB
JavaScript
import crypto from 'node:crypto';
import { ServiceProvider, } from '@piggly/ddd-toolkit';
/**
* @file Nonce service.
* @copyright Piggly Lab 2025
* @since 7.0.0
* @author Caique Araujo <caique@piggly.com.br>
*/
export class NonceTokenService {
/**
* Repository.
*
* @type {IStoreService}
* @protected
* @memberof NonceTokenService
* @since 7.0.0
* @author Caique Araujo <caique@piggly.com.br>
*/
_repository;
/**
* Constructor.
*
* @param {IStoreService} repository
* @public
* @constructor
* @memberof NonceTokenService
* @since 7.0.0
* @author Caique Araujo <caique@piggly.com.br>
*/
constructor(repository) {
this._repository = repository;
}
/**
* Issue one or more nonces.
*
* @param {number} size
* @returns {Promise<Array<string>>}
* @throws {Error} If failed to issue nonce. Error may be produced by store service.
* @since 7.0.0
* @author Caique Araujo <caique@piggly.com.br>
*/
async issue(size = 1, ttl = 3600) {
const nonces = [];
for (let i = 0; i < size; i++) {
const nonce = NonceTokenService.generateClientSecret(32);
const response = await this._repository.set(`nonce:${nonce}`, '1', ttl);
if (response === false) {
throw new Error('Failed to issue nonce');
}
nonces.push(nonce);
}
return nonces;
}
/**
* Verify a nonce.
*
* If will return undefined if nonce is not found
* or the same nonce if found. But, if regenerate
* is true, it will regenerate the nonce and return
* the new nonce.
*
* @param {string} nonce
* @param {boolean} regenerate
* @param {number} ttl
* @returns {Promise<TOrUndefined<string>>}
* @throws {Error} If failed to verify/regenerate nonce. Error may be produced by store service.
* @since 7.0.0
* @author Caique Araujo <caique@piggly.com.br>
*/
async verify(nonce, regenerate, ttl) {
const response = await this._repository.getAndDelete(`nonce:${nonce}`);
if (!response) {
return undefined;
}
if (regenerate === false) {
return nonce;
}
const nonces = await this.issue(1, ttl);
return nonces[0];
}
/**
* Random generates a client secret.
*
* @param {number} [size=36]
* @public
* @static
* @memberof NonceTokenService
* @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, '');
}
/**
* Register application service.
*
* @param {NonceTokenService} store
* @public
* @static
* @memberof NonceTokenService
* @since 7.0.0
* @author Caique Araujo <caique@piggly.com.br>
*/
static register(store) {
if (ServiceProvider.has('NonceTokenService')) {
return;
}
ServiceProvider.register('NonceTokenService', new NonceTokenService(store));
}
/**
* Resolve application service.
*
* @returns {NonceTokenService}
* @throws {Error} If service is not registered.
* @public
* @static
* @memberof NonceTokenService
* @since 7.0.0
* @author Caique Araujo <caique@piggly.com.br>
*/
static resolve() {
return ServiceProvider.resolve('NonceTokenService');
}
}
//# sourceMappingURL=NonceTokenService.js.map