Skip to content

Commit e827e84

Browse files
committed
Added ability to manually configure the package
1 parent 4944b7c commit e827e84

File tree

9 files changed

+345
-90
lines changed

9 files changed

+345
-90
lines changed

README.md

Lines changed: 47 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,13 @@ ___
1111
**AuthCrypto** is a powerful library for handling cryptographic operations and JWT (JSON Web Tokens) in Node.js applications. It provides utilities for hashing passwords, generating JWT tokens, and more.
1212

1313
> [!IMPORTANT]
14+
>
1415
> 🌟 **Support Our Open-Source Development!** 🌟
15-
> We need your support to keep our projects going! If you find our > work valuable, please consider contributing. Your support helps us > continue to develop and maintain these tools.
16+
> We need your support to keep our projects going! If you find our work valuable, please consider contributing. Your support helps us continue to develop and maintain these tools.
1617
>
1718
> **[Click here to support us!](https://fund.nasriya.net/)**
1819
>
19-
> Every contribution, big or small, makes a difference. Thank you for > your generosity and support!
20+
> Every contribution, big or small, makes a difference. Thank you for your generosity and support!
2021
___
2122
## Features
2223

@@ -53,14 +54,10 @@ ___
5354

5455
## Configuration
5556

56-
**AuthCrypto** reads configuration values from environment variables:
57+
**AuthCrypto** reads configuration values from environment variables or by setting them up manually:
5758

58-
- `AuthCrypto_ROUNDS`: The number of hashing rounds for password hashing.
59-
- `AuthCrypto_PASSWORDS_MIN`: Minimum length for passwords (default: `8`).
60-
- `AuthCrypto_PASSWORDS_MAX`: Maximum length for passwords (default: `32`).
61-
- `AuthCrypto_SECRET`**`*`**: A secret phrase to generate and verify JWT. Can be generated from [crypto.generateSecret()](#generating-secrets).
62-
63-
You can set these values in your `.env` file:
59+
### A) Environment Variables
60+
If you have full control over the source code, you can setup a `.env` file with the following properties:
6461

6562
```env
6663
AuthCrypto_ROUNDS=10
@@ -69,9 +66,34 @@ AuthCrypto_PASSWORDS_MAX=32
6966
AuthCrypto_SECRET=Your_secret
7067
```
7168

69+
- `AuthCrypto_ROUNDS`: The number of hashing rounds for password hashing.
70+
- `AuthCrypto_PASSWORDS_MIN`: Minimum length for passwords (default: `8`, min: `8`).
71+
- `AuthCrypto_PASSWORDS_MAX`: Maximum length for passwords (default: `32`).
72+
- `AuthCrypto_SECRET`**`*`**: A secret phrase to generate and verify JWT. Can be generated from [crypto.generateSecret()](#generating-secrets).
73+
74+
### B) Manual Configuration
75+
You can manually set some or all configurations using the `config` module as follows:
76+
77+
```js
78+
authCrypto.config.hashingRounds = 500;
79+
authCrypto.config.minPasswordLength = 10;
80+
authCrypto.config.maxPasswordLength = 32;
81+
authCrypto.config.jwtSecret = '<a 64 bytes secret>';
82+
```
83+
84+
Or you can configure them all like this:
85+
86+
```js
87+
authCrypto.config.set({
88+
hashingRounds: 500,
89+
minPasswordLength: 10,
90+
maxPasswordLength: 32,
91+
jwtSecret: '<a 64 bytes secret>'
92+
})
93+
```
7294
> **:warning: Important Note**
7395
>
74-
> You must specify the `Crypto JWT_SECRET` variable in your environment, otherwise, your system might be at risk of forgery
96+
> You must specify the `Crypto JWT_SECRET` variable in your environment, or set it using `authCrypto.config.jwtSecret`, otherwise, your system might be at risk of forgery
7597
___
7698

7799
## Usage
@@ -158,7 +180,7 @@ Explanation:
158180
The `verify` method checks if a provided password matches a previously hashed password.
159181

160182
Example Usage:
161-
```ts
183+
```js
162184
const plainPassword = 'mySecretPassword';
163185
const hashedPassword = 'hashedPasswordFromDatabase'; // Assume this is a valid hashed password
164186

@@ -167,13 +189,25 @@ const isMatch = Passwords.verify(plainPassword, hashedPassword);
167189
console.log(isMatch); // ⇨ true if the password matches, otherwise false
168190
```
169191

192+
Example with different algorith:
193+
```js
194+
const options = {
195+
algorithm: 'SHA256' // Default: SHA512
196+
}
197+
198+
const isMatch = Passwords.verify(plainPassword, hashedPassword, options);
199+
200+
console.log(isMatch); // ⇨ true if the password matches, otherwise false
201+
```
170202
Example Usage with **salting**:
171203
```ts
172204
const plainPassword = 'mySecretPassword';
173205
const hashedPassword = 'hashedPasswordFromDatabase'; // Assume this is a valid hashed password
174-
const salt = 'optionalSalt'; // If a salt was used during hashing
206+
const options = {
207+
salt: 'optionalSalt', // If a salt was used during hashing
208+
}
175209

176-
const isMatch = Passwords.verify(plainPassword, hashedPassword, salt);
210+
const isMatch = Passwords.verify(plainPassword, hashedPassword, options);
177211

178212
console.log(isMatch); // ⇨ true if the password matches, otherwise false
179213
```

package-lock.json

Lines changed: 8 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@nasriya/authcrypto",
3-
"version": "1.0.1",
3+
"version": "1.1.0",
44
"description": "AuthCrypto is a versatile cryptographic toolkit for handling JSON Web Tokens (JWT), password hashing, and secure token generation and verification. It provides robust methods for creating and managing JWTs, hashing and verifying passwords with secure algorithms, and generating cryptographically strong random values for various use cases.",
55
"main": "./dist/cjs/manager.js",
66
"module": "./dist/esm/manager.js",
@@ -62,4 +62,4 @@
6262
"type": "individual",
6363
"url": "https://fund.nasriya.net/"
6464
}
65-
}
65+
}

src/assets/config/config.ts

Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
class ConfigManager {
2+
readonly #_helpers = {
3+
is: {
4+
validRounds: (r: number) => {
5+
if (isNaN(r)) { throw new SyntaxError(`The number of rounds in the .env file must be a valid number, instead got ${r}`) }
6+
if (!Number.isInteger(r)) { throw new SyntaxError('The number of rounds must be a valid number and cannot be less than one') }
7+
if (r < 1) { throw new RangeError(`The number of rounds cannot be less than one`) }
8+
}
9+
}
10+
}
11+
12+
/**
13+
* The number of hashing rounds to use for password hashing.
14+
* This value is read from the `AuthCrypto_ROUNDS` environment variable.
15+
* If the value is not a valid number or is less than one, a `SyntaxError` will be thrown.
16+
* If the value is not set, `undefined` will be returned.
17+
* @returns {number | undefined} The number of hashing rounds to use.
18+
*/
19+
get hashingRounds(): number | undefined {
20+
const rStr = process.env.AuthCrypto_ROUNDS;
21+
if (!(rStr && typeof rStr === 'string')) { return undefined }
22+
23+
const r = Number.parseInt(rStr, 10);
24+
this.#_helpers.is.validRounds(r);
25+
return r;
26+
}
27+
28+
/**
29+
* Sets the number of hashing rounds for AuthCrypto. This value must be a positive integer of at least 1.
30+
* @throws {TypeError} If the number of rounds is not a valid number.
31+
* @throws {RangeError} If the number of rounds is less than 1.
32+
* @param {number} r The number of hashing rounds to set.
33+
*/
34+
set hashingRounds(r: number) {
35+
if (typeof r !== 'number') { throw new TypeError(`The number of rounds must be a number, instead got ${typeof r}`) }
36+
this.#_helpers.is.validRounds(r);
37+
process.env.AuthCrypto_ROUNDS = r.toString();
38+
}
39+
40+
/**
41+
* The minimum length of a password for AuthCrypto.
42+
* This value is read from the `AuthCrypto_PASSWORDS_MIN` environment variable.
43+
* If the value is not a valid number or is less than 8, a `SyntaxError` or `RangeError` will be thrown.
44+
* If the value is not set, `undefined` will be returned.
45+
* @returns {number | undefined} The minimum length of a password.
46+
*/
47+
get minPasswordLength(): number | undefined {
48+
const minStr = process.env.AuthCrypto_PASSWORDS_MIN;
49+
if (minStr === undefined) { return undefined }
50+
if (!(minStr && typeof minStr === 'string')) { throw new TypeError(`The min password length in the .env file must be a valid number, instead got ${minStr}`) }
51+
52+
const min = Number.parseInt(minStr, 10);
53+
54+
if (isNaN(min)) { throw new SyntaxError(`The min password length in the .env file must be a valid number, instead got ${minStr}`) }
55+
if (!Number.isInteger(min)) { throw new SyntaxError('The min password length must be a valid number and cannot be less than 8') }
56+
if (min < 8) { throw new RangeError('The min password length cannot be less than 8') }
57+
58+
return min;
59+
}
60+
61+
/**
62+
* Sets the minimum length of a password for AuthCrypto. This value must be a positive integer of at least 8.
63+
* @throws {TypeError} If the min password length is not a valid number.
64+
* @throws {RangeError} If the min password length is less than 8.
65+
* @param {number} min The minimum length of a password to set.
66+
*/
67+
set minPasswordLength(min: number) {
68+
if (!Number.isInteger(min)) { throw new TypeError('The min password length must be a valid number and cannot be less than 8') }
69+
if (min < 8) { throw new RangeError('The min password length cannot be less than 8') }
70+
process.env.AuthCrypto_PASSWORDS_MIN = min.toString();
71+
}
72+
73+
74+
/**
75+
* The maximum length of a password for AuthCrypto.
76+
* This value is read from the `AuthCrypto_PASSWORDS_MAX` environment variable.
77+
* If the value is not a valid number or is greater than 32, a `SyntaxError` or `RangeError` will be thrown.
78+
* If the value is not set, `undefined` will be returned.
79+
* @returns {number | undefined} The maximum length of a password.
80+
*/
81+
get maxPasswordLength(): number | undefined {
82+
const maxStr = process.env.AuthCrypto_PASSWORDS_MAX;
83+
if (maxStr === undefined) { return undefined }
84+
if (!(maxStr && typeof maxStr === 'string')) { throw new TypeError(`The max password length in the .env file must be a valid number, instead got ${maxStr}`) }
85+
86+
const max = Number.parseInt(maxStr, 10);
87+
88+
if (isNaN(max)) { throw new SyntaxError(`The max password length in the .env file must be a valid number, instead got ${maxStr}`) }
89+
if (!Number.isInteger(max)) { throw new SyntaxError('The max password length must be a valid number and cannot be greater than 32') }
90+
if (max < 8) { throw new RangeError('The max password length cannot be less than 8') }
91+
92+
return max;
93+
}
94+
95+
/**
96+
* Sets the maximum length of a password for AuthCrypto. This value must be a positive integer of at most 32.
97+
* @throws {TypeError} If the max password length is not a valid number.
98+
* @throws {RangeError} If the max password length is greater than 32.
99+
* @param {number} max The maximum length of a password to set.
100+
*/
101+
set maxPasswordLength(max: number) {
102+
if (!Number.isInteger(max)) { throw new TypeError('The max password length must be a valid number and cannot be greater than 32') }
103+
if (max < 8) { throw new RangeError('The max password length cannot be less than 8') }
104+
if (this.minPasswordLength && max > this.minPasswordLength) { throw new RangeError('The max password length cannot be greater than the min password length') }
105+
process.env.AuthCrypto_PASSWORDS_MAX = max.toString();
106+
}
107+
108+
109+
/**
110+
* Returns the secret key used to sign and verify JSON Web Tokens.
111+
* This value is read from the `AuthCrypto_JWT_SECRET` environment variable.
112+
* If the value is not a valid string or is not at least 64 bytes long for HS512, a `TypeError` will be thrown.
113+
* If the value is not set, `undefined` will be returned.
114+
* @returns {string | undefined} The secret key used for JWT signing and verification.
115+
*/
116+
get jwtSecret(): string | undefined {
117+
const secret = process.env.AuthCrypto_JWT_SECRET;
118+
if (secret === undefined) { return undefined }
119+
if (typeof secret !== 'string') { throw new TypeError('The JWT secret must be a string') }
120+
121+
const keyBytes = Buffer.byteLength(secret, 'utf8');
122+
if (keyBytes < 64) { throw new Error(`The "AuthCrypto_JWT_SECRET" key must be at least 64 bytes long for HS512. Found: ${keyBytes} bytes.`); }
123+
124+
return secret;
125+
}
126+
127+
/**
128+
* Sets the secret key used to sign and verify JSON Web Tokens.
129+
* This value must be a string and at least 64 bytes long for HS512.
130+
* @throws {TypeError} If the JWT secret is not a string.
131+
* @throws {Error} If the JWT secret is less than 64 bytes long.
132+
* @param {string} secret The secret key used to sign and verify JSON Web Tokens.
133+
*/
134+
set jwtSecret(secret: string) {
135+
if (typeof secret !== 'string') { throw new TypeError('The JWT secret must be a string') }
136+
137+
const keyBytes = Buffer.byteLength(secret, 'utf8');
138+
if (keyBytes < 64) { throw new Error(`The "AuthCrypto_JWT_SECRET" key must be at least 64 bytes long for HS512. Found: ${keyBytes} bytes.`); }
139+
140+
process.env.AuthCrypto_JWT_SECRET = secret;
141+
}
142+
143+
/**
144+
* Sets the configuration options for AuthCrypto.
145+
* @param {Configs} configs An object with the configuration options.
146+
* @throws {TypeError} If the config is not an object.
147+
* @throws {Error} If any of the required properties are missing from the config object.
148+
*/
149+
set(configs: Configs) {
150+
if (configs && typeof configs === 'object' && !Array.isArray(configs) && configs !== null && Object.keys(configs).length > 0) {
151+
if ('minPasswordLength' in configs) {
152+
this.minPasswordLength = configs.minPasswordLength;
153+
}
154+
155+
if ('maxPasswordLength' in configs) {
156+
this.maxPasswordLength = configs.maxPasswordLength;
157+
}
158+
159+
if ('jwtSecret' in configs) {
160+
this.jwtSecret = configs.jwtSecret;
161+
}
162+
163+
if ('hashingRounds' in configs) {
164+
this.hashingRounds = configs.hashingRounds;
165+
}
166+
} else {
167+
throw new TypeError('The config must be an object with at least one property')
168+
}
169+
}
170+
}
171+
172+
interface Configs {
173+
minPasswordLength: number;
174+
maxPasswordLength: number;
175+
jwtSecret: string;
176+
hashingRounds: number;
177+
}
178+
179+
export default ConfigManager;

src/assets/crypto/crypto.ts

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,18 @@
11
import crypto from 'crypto';
22
import { HashAlgorithm } from '../../docs/docs';
3+
import ConfigManager from '../config/config';
34

45
class CryptoManager {
6+
readonly #_configManager: ConfigManager;
57
readonly #_supportedAlgorithms: HashAlgorithm[] = ['SHA256', 'SHA512', 'MD5', 'SHA1'];
6-
readonly #_config = Object.seal({
7-
rounds: 37,
8-
})
8+
readonly #_defaultRounds = 37;
99

10-
constructor() {
11-
const roundsStr = process.env.AuthCrypto_ROUNDS;
10+
constructor(configManager: ConfigManager) {
11+
this.#_configManager = configManager;
12+
}
1213

13-
if (roundsStr) {
14-
if (typeof roundsStr !== 'string') { throw new Error(`You must specify the number of rounds (AuthCrypto_ROUNDS) for the AuthCrypto module in the .env file`) }
15-
const rounds = Number.parseInt(roundsStr, 10);
16-
if (isNaN(rounds) || rounds < 1) { throw new SyntaxError('The number of rounds must be a valid number and cannot be less than one') }
17-
this.#_config.rounds = rounds;
18-
}
14+
get #_rounds() {
15+
return this.#_configManager.hashingRounds || this.#_defaultRounds
1916
}
2017

2118
/**
@@ -65,7 +62,7 @@ class CryptoManager {
6562
}
6663

6764
let hashedInput = input
68-
for (let i = 0; i < this.#_config.rounds; i++) {
65+
for (let i = 0; i < this.#_rounds; i++) {
6966
hashedInput = crypto.createHash(algorithm).update(hashedInput).digest('hex');
7067
}
7168

@@ -100,4 +97,4 @@ class CryptoManager {
10097
}
10198
}
10299

103-
export default new CryptoManager();
100+
export default CryptoManager;

0 commit comments

Comments
 (0)