core-services-sdk 1.3.3 → 1.3.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +4 -1
- package/src/core/combine-unique-arrays.js +15 -0
- package/src/core/index.js +5 -0
- package/src/core/normalize-to-array.js +31 -0
- package/src/core/otp-generators.js +113 -0
- package/src/core/regex-utils.js +27 -0
- package/src/core/sanitize-objects.js +50 -0
- package/src/crypto/crypto.js +128 -0
- package/src/crypto/encryption.js +48 -0
- package/src/crypto/index.js +2 -0
- package/src/fastify/error-handlers/with-error-handling.js +5 -1
- package/src/http/http.js +111 -52
- package/src/ids/generators.js +73 -0
- package/src/ids/prefixes.js +28 -0
- package/src/index.js +8 -1
- package/src/logger/get-logger.js +64 -0
- package/src/logger/index.js +1 -0
- package/src/mongodb/index.js +3 -73
- package/src/mongodb/initialize-mongodb.js +73 -0
- package/src/mongodb/validate-mongo-uri.js +32 -0
- package/tests/core/combine-unique-arrays.unit.test.js +35 -0
- package/tests/core/normalize-to-array.unit.test.js +43 -0
- package/tests/core/otp-generators.unit.test.js +93 -0
- package/tests/core/regex-utils.unit.test.js +41 -0
- package/tests/core/sanitize-objects.unit.test.js +78 -0
- package/tests/crypto/crypto.unit.test.js +130 -0
- package/tests/crypto/encryption.unit.test.js +73 -0
- package/tests/ids/generators.unit.test.js +72 -0
- package/tests/ids/prefixes.unit.test.js +42 -0
- package/tests/logger/get-logger.unit.test.js +64 -0
- package/src/fastify/error-handlers/with-error-handling.types.js +0 -10
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "core-services-sdk",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.5",
|
|
4
4
|
"main": "src/index.js",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
@@ -21,12 +21,15 @@
|
|
|
21
21
|
"dependencies": {
|
|
22
22
|
"amqplib": "^0.10.8",
|
|
23
23
|
"aws-sdk": "^2.1692.0",
|
|
24
|
+
"crypto": "^1.0.1",
|
|
24
25
|
"dot": "^1.1.3",
|
|
26
|
+
"fastify": "^5.4.0",
|
|
25
27
|
"http-status": "^2.1.0",
|
|
26
28
|
"mongodb": "^6.17.0",
|
|
27
29
|
"node-fetch": "^3.3.2",
|
|
28
30
|
"nodemailer": "^7.0.5",
|
|
29
31
|
"nodemailer-sendgrid-transport": "^0.2.0",
|
|
32
|
+
"pino": "^9.7.0",
|
|
30
33
|
"uuid": "^11.1.0",
|
|
31
34
|
"xml2js": "^0.6.2"
|
|
32
35
|
},
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Combines multiple arrays into a single flat array with unique values.
|
|
3
|
+
*
|
|
4
|
+
* Removes duplicate entries and preserves the order of first appearance.
|
|
5
|
+
*
|
|
6
|
+
* @param {...Array<any>} lists - One or more arrays to combine.
|
|
7
|
+
* @returns {Array<any>} A new array containing unique values from all input arrays.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* combineUniqueArrays([1, 2, 3], [3, 4], [5]) // [1, 2, 3, 4, 5]
|
|
11
|
+
* combineUniqueArrays(['a', 'b'], ['b', 'c']) // ['a', 'b', 'c']
|
|
12
|
+
*/
|
|
13
|
+
export const combineUniqueArrays = (...lists) => {
|
|
14
|
+
return Array.from(new Set(lists.flat()))
|
|
15
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Normalizes an input value to an array of trimmed, non-empty strings.
|
|
3
|
+
*
|
|
4
|
+
* - If the input is `undefined` or `null`, returns an empty array.
|
|
5
|
+
* - If the input is an array, it trims each string element and filters out empty values.
|
|
6
|
+
* - If the input is a string (or coercible to string), it splits it by commas,
|
|
7
|
+
* trims each item, and filters out empty values.
|
|
8
|
+
*
|
|
9
|
+
* @param {any} value - The value to normalize. Can be a string, array, or any other type.
|
|
10
|
+
* @returns {string[]} An array of trimmed, non-empty strings.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* normalizeToArray('a,b, c , ,d') // ['a', 'b', 'c', 'd']
|
|
14
|
+
* normalizeToArray([' a ', 'b', '', ' ']) // ['a', 'b']
|
|
15
|
+
* normalizeToArray(null) // []
|
|
16
|
+
* normalizeToArray(123) // ['123']
|
|
17
|
+
*/
|
|
18
|
+
export const normalizeToArray = (value) => {
|
|
19
|
+
if (value === undefined || value === null) {
|
|
20
|
+
return []
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const isArray = Array.isArray(value)
|
|
24
|
+
const rawArray = isArray ? value : String(value).split(',')
|
|
25
|
+
|
|
26
|
+
const arrayFiltered = rawArray
|
|
27
|
+
.map((item) => (typeof item === 'string' ? item.trim() : ''))
|
|
28
|
+
.filter(Boolean)
|
|
29
|
+
|
|
30
|
+
return arrayFiltered
|
|
31
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
const numeric = '0123456789'
|
|
2
|
+
const symbols = '!@#$%^&*()_+-=[]{}|;:,.<>?'
|
|
3
|
+
const alphaLower = 'abcdefghijklmnopqrstuvwxyz'
|
|
4
|
+
const alphaUpper = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
|
|
5
|
+
|
|
6
|
+
const alphanumericSymbols = `${alphaLower}${alphaUpper}${numeric}${symbols}`
|
|
7
|
+
|
|
8
|
+
export const OTP_GENERATOR_TYPES = Object.freeze({
|
|
9
|
+
any: 'any',
|
|
10
|
+
alpha: 'alpha',
|
|
11
|
+
numeric: 'numeric',
|
|
12
|
+
symbols: 'symbols',
|
|
13
|
+
alphaLower: 'alphaLower',
|
|
14
|
+
alphaUpper: 'alphaUpper',
|
|
15
|
+
alphanumeric: 'alphanumeric',
|
|
16
|
+
alphanumericSymbols: 'alphanumericSymbols',
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
const types = Object.freeze({
|
|
20
|
+
[OTP_GENERATOR_TYPES.numeric]: numeric,
|
|
21
|
+
[OTP_GENERATOR_TYPES.symbols]: symbols,
|
|
22
|
+
[OTP_GENERATOR_TYPES.alphaLower]: alphaLower,
|
|
23
|
+
[OTP_GENERATOR_TYPES.alphaUpper]: alphaUpper,
|
|
24
|
+
[OTP_GENERATOR_TYPES.any]: alphanumericSymbols,
|
|
25
|
+
[OTP_GENERATOR_TYPES.alpha]: `${alphaLower}${alphaUpper}`,
|
|
26
|
+
[OTP_GENERATOR_TYPES.alphanumericSymbols]: alphanumericSymbols,
|
|
27
|
+
[OTP_GENERATOR_TYPES.alphanumeric]: `${alphaLower}${alphaUpper}${numeric}`,
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Generates a one-time password (OTP) code based on a specified type or character set.
|
|
32
|
+
*
|
|
33
|
+
* @param {Object} [options={}] - The options object.
|
|
34
|
+
* @param {number} [options.length=4] - The desired length of the generated code (1-30).
|
|
35
|
+
* @param {string} [options.type='numeric'] - The type of characters to use. One of:
|
|
36
|
+
* 'any', 'alpha', 'numeric', 'symbols', 'alphaLower', 'alphaUpper', 'alphanumeric', 'alphanumericSymbols'.
|
|
37
|
+
* @param {string} [options.charset] - A custom string of characters to use instead of the predefined types.
|
|
38
|
+
* @returns {string} The generated code.
|
|
39
|
+
* @throws {Error} If the length is not a number between 1 and 30.
|
|
40
|
+
* @throws {Error} If charset is provided and is not a non-empty string.
|
|
41
|
+
* @throws {Error} If the type is invalid and no valid charset is provided.
|
|
42
|
+
*/
|
|
43
|
+
export function generateCode({ length = 4, type = 'numeric', charset } = {}) {
|
|
44
|
+
const max = 30
|
|
45
|
+
const min = 1
|
|
46
|
+
|
|
47
|
+
if (typeof length !== 'number' || length < min || length > max) {
|
|
48
|
+
throw new Error(`length must be a number between ${min} and ${max}`)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (charset !== undefined) {
|
|
52
|
+
if (typeof charset !== 'string') {
|
|
53
|
+
throw new Error('charset must be a string if provided')
|
|
54
|
+
}
|
|
55
|
+
if (charset.length === 0) {
|
|
56
|
+
throw new Error('charset must not be empty')
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const chars = charset || types[type]
|
|
61
|
+
if (!chars) {
|
|
62
|
+
throw new Error(
|
|
63
|
+
`type must be one of: ${Object.keys(types)
|
|
64
|
+
.map((t) => `'${t}'`)
|
|
65
|
+
.join(', ')}`,
|
|
66
|
+
)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return Array.from(
|
|
70
|
+
{ length },
|
|
71
|
+
() => chars[Math.floor(Math.random() * chars.length)],
|
|
72
|
+
).join('')
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Generates an OTP code using alphabetic characters (both lowercase and uppercase).
|
|
77
|
+
*
|
|
78
|
+
* @param {number} [length=4] - The desired length of the code.
|
|
79
|
+
* @returns {string} The generated code.
|
|
80
|
+
*/
|
|
81
|
+
export function generateCodeAlpha(length = 4) {
|
|
82
|
+
return generateCode({ length, type: 'alpha' })
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Generates an OTP code using only numeric digits (0-9).
|
|
87
|
+
*
|
|
88
|
+
* @param {number} [length=4] - The desired length of the code.
|
|
89
|
+
* @returns {string} The generated code.
|
|
90
|
+
*/
|
|
91
|
+
export function generateCodeDigits(length = 4) {
|
|
92
|
+
return generateCode({ length, type: 'numeric' })
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Generates an OTP code using alphabetic characters and digits.
|
|
97
|
+
*
|
|
98
|
+
* @param {number} [length=4] - The desired length of the code.
|
|
99
|
+
* @returns {string} The generated code.
|
|
100
|
+
*/
|
|
101
|
+
export function generateCodeAlphaNumeric(length = 4) {
|
|
102
|
+
return generateCode({ length, type: 'alphanumeric' })
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Generates an OTP code using alphabetic characters, digits, and symbols.
|
|
107
|
+
*
|
|
108
|
+
* @param {number} [length=4] - The desired length of the code.
|
|
109
|
+
* @returns {string} The generated code.
|
|
110
|
+
*/
|
|
111
|
+
export function generateCodeAlphaNumericSymbols(length = 4) {
|
|
112
|
+
return generateCode({ length, type: 'any' })
|
|
113
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Checks whether the given input is a valid regular expression.
|
|
3
|
+
*
|
|
4
|
+
* - If the input is an instance of `RegExp`, returns `true`.
|
|
5
|
+
* - If the input is a string, attempts to create a `RegExp` object from it and returns `true` if valid.
|
|
6
|
+
* - Returns `false` for any other types or if the string is an invalid regex pattern.
|
|
7
|
+
*
|
|
8
|
+
* @param {string | RegExp} pattern - The regex pattern to validate, either as a string or a RegExp object.
|
|
9
|
+
* @returns {boolean} `true` if the pattern is a valid regular expression, otherwise `false`.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* isValidRegex('^[a-z]+$') // true
|
|
13
|
+
* isValidRegex(/^[a-z]+$/) // true
|
|
14
|
+
* isValidRegex('[') // false
|
|
15
|
+
* isValidRegex(123) // false
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
export const isValidRegex = (pattern) => {
|
|
19
|
+
if (pattern instanceof RegExp) return true
|
|
20
|
+
if (typeof pattern !== 'string') return false
|
|
21
|
+
try {
|
|
22
|
+
new RegExp(pattern)
|
|
23
|
+
return true
|
|
24
|
+
} catch {
|
|
25
|
+
return false
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Creates a new object containing only the key-value pairs that pass the provided filter function.
|
|
3
|
+
*
|
|
4
|
+
* @param {Object} obj - The object to sanitize.
|
|
5
|
+
* @param {(entry: [string, any]) => boolean} filter - A function that determines whether to include each `[key, value]` entry.
|
|
6
|
+
* @returns {Object} A new object with only the filtered entries.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* sanitizeObject({ a: 1, b: undefined }, ([, v]) => v !== undefined) // { a: 1 }
|
|
10
|
+
*/
|
|
11
|
+
export const sanitizeObject = (obj, filter) =>
|
|
12
|
+
Object.fromEntries(Object.entries(obj).filter(filter))
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Removes all properties from the object whose values are `undefined`.
|
|
16
|
+
*
|
|
17
|
+
* @param {Object} obj - The object to sanitize.
|
|
18
|
+
* @returns {Object} A new object without `undefined` values.
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* sanitizeUndefinedFields({ a: 1, b: undefined }) // { a: 1 }
|
|
22
|
+
*/
|
|
23
|
+
export const sanitizeUndefinedFields = (obj) =>
|
|
24
|
+
sanitizeObject(obj, ([, value]) => value !== undefined)
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Returns a new object containing only the specified allowed properties.
|
|
28
|
+
*
|
|
29
|
+
* @param {Object} obj - The original object.
|
|
30
|
+
* @param {string[]} [allowedFields=[]] - An array of property names to retain.
|
|
31
|
+
* @returns {Object} A new object containing only the allowed properties.
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* sanitizeObjectAllowProps({ a: 1, b: 2 }, ['a']) // { a: 1 }
|
|
35
|
+
*/
|
|
36
|
+
export const sanitizeObjectAllowProps = (obj, allowedFields = []) =>
|
|
37
|
+
sanitizeObject(obj, ([key]) => allowedFields.includes(key))
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Returns a new object excluding the specified disallowed properties.
|
|
41
|
+
*
|
|
42
|
+
* @param {Object} obj - The original object.
|
|
43
|
+
* @param {string[]} [disallowedFields=[]] - An array of property names to exclude.
|
|
44
|
+
* @returns {Object} A new object without the disallowed properties.
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* sanitizeObjectDisallowProps({ a: 1, b: 2 }, ['b']) // { a: 1 }
|
|
48
|
+
*/
|
|
49
|
+
export const sanitizeObjectDisallowProps = (obj, disallowedFields = []) =>
|
|
50
|
+
sanitizeObject(obj, ([key]) => !disallowedFields.includes(key))
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { promisify } from 'util'
|
|
2
|
+
import { randomBytes, scrypt } from 'crypto'
|
|
3
|
+
|
|
4
|
+
const scryptPromisify = promisify(scrypt)
|
|
5
|
+
|
|
6
|
+
const keyLength = 64
|
|
7
|
+
const baseFormat = 'hex'
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Generates a cryptographically secure random salt as a Buffer.
|
|
11
|
+
*
|
|
12
|
+
* @param {number} length - The length of the salt in bytes.
|
|
13
|
+
* @returns {Buffer} A Buffer containing random bytes.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* const salt = getSalt(16)
|
|
17
|
+
* console.log(salt.toString('hex')) // 'e4b1c9...'
|
|
18
|
+
*/
|
|
19
|
+
export const getSalt = (length) => {
|
|
20
|
+
return randomBytes(length)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Generates a cryptographically secure random salt and returns it as a hexadecimal string.
|
|
25
|
+
*
|
|
26
|
+
* @param {number} length - The length of the salt in bytes.
|
|
27
|
+
* @returns {string} The salt encoded as a hexadecimal string.
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* const saltHex = getSaltHex(16)
|
|
31
|
+
* console.log(saltHex) // 'e4b1c9...'
|
|
32
|
+
*/
|
|
33
|
+
export const getSaltHex = (length) => {
|
|
34
|
+
return getSalt(length).toString(baseFormat)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Encrypts a given expression using scrypt with a provided salt.
|
|
39
|
+
*
|
|
40
|
+
* @param {Object} options - The encryption options.
|
|
41
|
+
* @param {string} options.expression - The expression to encrypt (e.g., a password).
|
|
42
|
+
* @param {string} options.salt - The salt to use (as a string).
|
|
43
|
+
* @param {number} [options.length=64] - The desired key length in bytes for the derived key.
|
|
44
|
+
* @returns {Promise<Buffer>} A Promise that resolves to the derived key as a Buffer.
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* const buffer = await getEncryptedBuffer({
|
|
48
|
+
* expression: 'my-password',
|
|
49
|
+
* salt: 'somesalt',
|
|
50
|
+
* length: 32
|
|
51
|
+
* })
|
|
52
|
+
* console.log(buffer.toString('hex'))
|
|
53
|
+
*/
|
|
54
|
+
export const getEncryptedBuffer = async ({
|
|
55
|
+
salt,
|
|
56
|
+
expression,
|
|
57
|
+
length = keyLength,
|
|
58
|
+
}) => {
|
|
59
|
+
const encryptedExpression = await scryptPromisify(expression, salt, length)
|
|
60
|
+
|
|
61
|
+
return encryptedExpression
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Encrypts a string expression (e.g., password) using scrypt and returns the result as a hexadecimal string.
|
|
66
|
+
*
|
|
67
|
+
* Optionally appends a `passwordPrivateKey` to the salt for extra security.
|
|
68
|
+
*
|
|
69
|
+
* @param {Object} options
|
|
70
|
+
* @param {string} options.expression - The expression to encrypt.
|
|
71
|
+
* @param {string} options.salt - The salt string.
|
|
72
|
+
* @param {string} [options.passwordPrivateKey] - Optional extra key to enhance the salt.
|
|
73
|
+
* @returns {Promise<string>} A Promise that resolves to the encrypted expression as a hex string.
|
|
74
|
+
*
|
|
75
|
+
* @example
|
|
76
|
+
* const encrypted = await encrypt({
|
|
77
|
+
* expression: 'my-password',
|
|
78
|
+
* salt: 'abc123',
|
|
79
|
+
* passwordPrivateKey: 'my-secret-key'
|
|
80
|
+
* })
|
|
81
|
+
* console.log(encrypted) // '9af0a1b23c...'
|
|
82
|
+
*/
|
|
83
|
+
export const encrypt = async ({ salt, expression, passwordPrivateKey }) => {
|
|
84
|
+
const encryptedExpression = await getEncryptedBuffer({
|
|
85
|
+
expression,
|
|
86
|
+
salt: `${salt}${passwordPrivateKey ? passwordPrivateKey : ''}`,
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
return encryptedExpression.toString(baseFormat)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Compares a plain-text password to an already encrypted one using scrypt.
|
|
94
|
+
*
|
|
95
|
+
* Re-encrypts the input password using the provided salt and optional private key,
|
|
96
|
+
* then compares it to the stored encrypted password.
|
|
97
|
+
*
|
|
98
|
+
* @param {Object} options
|
|
99
|
+
* @param {string} options.salt - The salt used during encryption.
|
|
100
|
+
* @param {string} options.password - The plain-text password to validate.
|
|
101
|
+
* @param {string} options.encryptedPassword - The previously encrypted password (hex string).
|
|
102
|
+
* @param {string} [options.passwordPrivateKey] - Optional private key used in encryption.
|
|
103
|
+
* @returns {Promise<boolean>} A Promise that resolves to `true` if passwords match, otherwise `false`.
|
|
104
|
+
*
|
|
105
|
+
* @example
|
|
106
|
+
* const isValid = await isPasswordMatch({
|
|
107
|
+
* password: 'my-password',
|
|
108
|
+
* salt: 'abc123',
|
|
109
|
+
* encryptedPassword: '9af0a1b23c...',
|
|
110
|
+
* passwordPrivateKey: 'my-secret-key'
|
|
111
|
+
* })
|
|
112
|
+
* console.log(isValid) // true or false
|
|
113
|
+
*/
|
|
114
|
+
export const isPasswordMatch = async ({
|
|
115
|
+
salt,
|
|
116
|
+
password,
|
|
117
|
+
encryptedPassword,
|
|
118
|
+
passwordPrivateKey,
|
|
119
|
+
}) => {
|
|
120
|
+
const encryptedCurrentPassword = await getEncryptedBuffer({
|
|
121
|
+
expression: password,
|
|
122
|
+
salt: `${salt}${passwordPrivateKey ? passwordPrivateKey : ''}`,
|
|
123
|
+
})
|
|
124
|
+
const isMatch = encryptedCurrentPassword.equals(
|
|
125
|
+
Buffer.from(encryptedPassword, baseFormat),
|
|
126
|
+
)
|
|
127
|
+
return isMatch
|
|
128
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { encrypt, getSaltHex } from './crypto.js'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Encrypts a plain-text password using scrypt and a salt.
|
|
5
|
+
*
|
|
6
|
+
* This function generates a random salt (in hex) of the specified length,
|
|
7
|
+
* appends an optional passwordPrivateKey (if provided),
|
|
8
|
+
* and uses the Node.js `crypto.scrypt` algorithm to encrypt the password.
|
|
9
|
+
*
|
|
10
|
+
* @async
|
|
11
|
+
* @function encryptPassword
|
|
12
|
+
*
|
|
13
|
+
* @param {Object} input - The input object containing the password.
|
|
14
|
+
* @param {string} input.password - The plain-text password to encrypt.
|
|
15
|
+
*
|
|
16
|
+
* @param {Object} options - Configuration options for encryption.
|
|
17
|
+
* @param {number} options.saltLength - The desired length of the generated salt (in bytes).
|
|
18
|
+
* @param {string} [options.passwordPrivateKey] - An optional private key to strengthen the salt.
|
|
19
|
+
*
|
|
20
|
+
* @returns {Promise<Object>} An object containing:
|
|
21
|
+
* - `salt` {string} The generated salt in hex format.
|
|
22
|
+
* - `password` {string} The encrypted password in hex format.
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* const result = await encryptPassword(
|
|
26
|
+
* { password: 'mySecretPassword' },
|
|
27
|
+
* { saltLength: 16, passwordPrivateKey: 'abc123' }
|
|
28
|
+
* );
|
|
29
|
+
* console.log(result); // { salt: 'f3ab...', password: '8e1f...' }
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
export const encryptPassword = async (
|
|
33
|
+
{ password },
|
|
34
|
+
{ saltLength, passwordPrivateKey },
|
|
35
|
+
) => {
|
|
36
|
+
const salt = getSaltHex(saltLength)
|
|
37
|
+
|
|
38
|
+
const encrypted = await encrypt({
|
|
39
|
+
salt,
|
|
40
|
+
passwordPrivateKey,
|
|
41
|
+
expression: password,
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
return {
|
|
45
|
+
salt,
|
|
46
|
+
password: encrypted,
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import httpStatus from 'http-status'
|
|
2
2
|
|
|
3
|
-
import { HttpError } from '../../http/HttpError.js'
|
|
4
3
|
import { GENERAL_ERROR } from '../error-codes.js'
|
|
4
|
+
import { HttpError } from '../../http/HttpError.js'
|
|
5
|
+
|
|
6
|
+
/** @typedef {import('fastify').FastifyReply} Reply */
|
|
7
|
+
/** @typedef {import('fastify').FastifyRequest} Request */
|
|
8
|
+
/** @typedef {import('pino').Logger} Logger */
|
|
5
9
|
|
|
6
10
|
/**
|
|
7
11
|
* Generic error-handling wrapper that logs and throws a safe error.
|