@nest-boot/hash 7.1.0 → 7.2.1
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/dist/hash.module.d.ts +6 -2
- package/dist/hash.module.js +27 -3
- package/dist/hash.module.js.map +1 -1
- package/dist/hash.module.spec.d.ts +1 -0
- package/dist/hash.module.spec.js +76 -0
- package/dist/hash.module.spec.js.map +1 -0
- package/dist/hash.service.d.ts +35 -3
- package/dist/hash.service.js +48 -4
- package/dist/hash.service.js.map +1 -1
- package/dist/hash.service.spec.js +56 -75
- package/dist/hash.service.spec.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/dist/utils/estimate-entropy.d.ts +8 -0
- package/dist/utils/estimate-entropy.js +17 -0
- package/dist/utils/estimate-entropy.js.map +1 -0
- package/dist/utils/estimate-entropy.spec.d.ts +1 -0
- package/dist/utils/estimate-entropy.spec.js +17 -0
- package/dist/utils/estimate-entropy.spec.js.map +1 -0
- package/package.json +6 -6
package/dist/hash.module.d.ts
CHANGED
|
@@ -10,13 +10,17 @@ import { ASYNC_OPTIONS_TYPE, ConfigurableModuleClass, OPTIONS_TYPE } from "./has
|
|
|
10
10
|
* @Module({
|
|
11
11
|
* imports: [
|
|
12
12
|
* HashModule.register({
|
|
13
|
-
* secret:
|
|
14
|
-
* isGlobal: true,
|
|
13
|
+
* secret: process.env.HASH_SECRET
|
|
15
14
|
* }),
|
|
16
15
|
* ],
|
|
17
16
|
* })
|
|
18
17
|
* export class AppModule {}
|
|
19
18
|
* ```
|
|
19
|
+
*
|
|
20
|
+
* Generate a secure secret:
|
|
21
|
+
* ```bash
|
|
22
|
+
* node -e "console.log(require('crypto').randomBytes(32).toString('base64url'))"
|
|
23
|
+
* ```
|
|
20
24
|
*/
|
|
21
25
|
export declare class HashModule extends ConfigurableModuleClass {
|
|
22
26
|
/**
|
package/dist/hash.module.js
CHANGED
|
@@ -10,6 +10,7 @@ exports.HashModule = void 0;
|
|
|
10
10
|
const common_1 = require("@nestjs/common");
|
|
11
11
|
const hash_module_definition_1 = require("./hash.module-definition");
|
|
12
12
|
const hash_service_1 = require("./hash.service");
|
|
13
|
+
const estimate_entropy_1 = require("./utils/estimate-entropy");
|
|
13
14
|
/**
|
|
14
15
|
* Module that provides password hashing services using Argon2.
|
|
15
16
|
*
|
|
@@ -20,13 +21,17 @@ const hash_service_1 = require("./hash.service");
|
|
|
20
21
|
* @Module({
|
|
21
22
|
* imports: [
|
|
22
23
|
* HashModule.register({
|
|
23
|
-
* secret:
|
|
24
|
-
* isGlobal: true,
|
|
24
|
+
* secret: process.env.HASH_SECRET
|
|
25
25
|
* }),
|
|
26
26
|
* ],
|
|
27
27
|
* })
|
|
28
28
|
* export class AppModule {}
|
|
29
29
|
* ```
|
|
30
|
+
*
|
|
31
|
+
* Generate a secure secret:
|
|
32
|
+
* ```bash
|
|
33
|
+
* node -e "console.log(require('crypto').randomBytes(32).toString('base64url'))"
|
|
34
|
+
* ```
|
|
30
35
|
*/
|
|
31
36
|
let HashModule = class HashModule extends hash_module_definition_1.ConfigurableModuleClass {
|
|
32
37
|
/**
|
|
@@ -55,7 +60,26 @@ exports.HashModule = HashModule = __decorate([
|
|
|
55
60
|
provide: hash_service_1.HashService,
|
|
56
61
|
inject: [{ token: hash_module_definition_1.MODULE_OPTIONS_TOKEN, optional: true }],
|
|
57
62
|
useFactory: (options) => {
|
|
58
|
-
|
|
63
|
+
const secret = options?.secret ?? process.env.HASH_SECRET ?? process.env.APP_SECRET;
|
|
64
|
+
if (!secret) {
|
|
65
|
+
throw new Error("Hash secret is required.\n" +
|
|
66
|
+
"Set HASH_SECRET or APP_SECRET environment variable, or pass a secret option.\n" +
|
|
67
|
+
"Generate a secure secret with:\n" +
|
|
68
|
+
" node -e \"console.log(require('crypto').randomBytes(32).toString('base64url'))\"");
|
|
69
|
+
}
|
|
70
|
+
if (secret.length < 32) {
|
|
71
|
+
throw new Error("Hash secret must be at least 32 characters long.\n" +
|
|
72
|
+
"Set HASH_SECRET or APP_SECRET environment variable, or pass a secret option.\n" +
|
|
73
|
+
"Generate a secure secret with:\n" +
|
|
74
|
+
" node -e \"console.log(require('crypto').randomBytes(32).toString('base64url'))\"");
|
|
75
|
+
}
|
|
76
|
+
if ((0, estimate_entropy_1.estimateEntropy)(secret) < 120) {
|
|
77
|
+
throw new Error("Hash secret appears low-entropy.\n" +
|
|
78
|
+
"Use a randomly generated secret for production.\n" +
|
|
79
|
+
"Generate a secure secret with:\n" +
|
|
80
|
+
" node -e \"console.log(require('crypto').randomBytes(32).toString('base64url'))\"");
|
|
81
|
+
}
|
|
82
|
+
hash_service_1.HashService.init(secret);
|
|
59
83
|
return hash_service_1.HashService.instance;
|
|
60
84
|
},
|
|
61
85
|
},
|
package/dist/hash.module.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hash.module.js","sourceRoot":"","sources":["../src/hash.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAoE;AAEpE,qEAKkC;AAClC,iDAA6C;
|
|
1
|
+
{"version":3,"file":"hash.module.js","sourceRoot":"","sources":["../src/hash.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAoE;AAEpE,qEAKkC;AAClC,iDAA6C;AAE7C,+DAA2D;AAE3D;;;;;;;;;;;;;;;;;;;;;GAqBG;AA8CI,IAAM,UAAU,GAAhB,MAAM,UAAW,SAAQ,gDAAuB;IACrD;;;;OAIG;IACH,MAAM,CAAU,QAAQ,CAAC,OAA4B;QACnD,OAAO,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;IACjC,CAAC;IAED;;;;OAIG;IACH,MAAM,CAAU,aAAa,CAC3B,OAAkC;QAElC,OAAO,KAAK,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;IACtC,CAAC;CACF,CAAA;AApBY,gCAAU;qBAAV,UAAU;IA7CtB,IAAA,eAAM,GAAE;IACR,IAAA,eAAM,EAAC;QACN,SAAS,EAAE;YACT;gBACE,OAAO,EAAE,0BAAW;gBACpB,MAAM,EAAE,CAAC,EAAE,KAAK,EAAE,6CAAoB,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;gBACzD,UAAU,EAAE,CAAC,OAA2B,EAAE,EAAE;oBAC1C,MAAM,MAAM,GACV,OAAO,EAAE,MAAM,IAAI,OAAO,CAAC,GAAG,CAAC,WAAW,IAAI,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC;oBAEvE,IAAI,CAAC,MAAM,EAAE,CAAC;wBACZ,MAAM,IAAI,KAAK,CACb,4BAA4B;4BAC1B,gFAAgF;4BAChF,kCAAkC;4BAClC,oFAAoF,CACvF,CAAC;oBACJ,CAAC;oBAED,IAAI,MAAM,CAAC,MAAM,GAAG,EAAE,EAAE,CAAC;wBACvB,MAAM,IAAI,KAAK,CACb,oDAAoD;4BAClD,gFAAgF;4BAChF,kCAAkC;4BAClC,oFAAoF,CACvF,CAAC;oBACJ,CAAC;oBAED,IAAI,IAAA,kCAAe,EAAC,MAAM,CAAC,GAAG,GAAG,EAAE,CAAC;wBAClC,MAAM,IAAI,KAAK,CACb,oCAAoC;4BAClC,mDAAmD;4BACnD,kCAAkC;4BAClC,oFAAoF,CACvF,CAAC;oBACJ,CAAC;oBAED,0BAAW,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;oBAEzB,OAAO,0BAAW,CAAC,QAAQ,CAAC;gBAC9B,CAAC;aACF;SACF;QACD,OAAO,EAAE,CAAC,0BAAW,CAAC;KACvB,CAAC;GACW,UAAU,CAoBtB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const testing_1 = require("@nestjs/testing");
|
|
4
|
+
const _1 = require(".");
|
|
5
|
+
const TEST_SECRET = "myTestSecretThatIsAtLeast32Chars!";
|
|
6
|
+
describe("HashModule", () => {
|
|
7
|
+
const originalEnv = process.env;
|
|
8
|
+
beforeEach(() => {
|
|
9
|
+
// Reset static instance before each test
|
|
10
|
+
_1.HashService._instance = undefined;
|
|
11
|
+
// Reset environment variables
|
|
12
|
+
process.env = { ...originalEnv };
|
|
13
|
+
delete process.env.HASH_SECRET;
|
|
14
|
+
delete process.env.APP_SECRET;
|
|
15
|
+
});
|
|
16
|
+
afterAll(() => {
|
|
17
|
+
process.env = originalEnv;
|
|
18
|
+
});
|
|
19
|
+
describe("register", () => {
|
|
20
|
+
it("should throw error when secret is missing", async () => {
|
|
21
|
+
await expect(testing_1.Test.createTestingModule({
|
|
22
|
+
imports: [_1.HashModule.register({})],
|
|
23
|
+
}).compile()).rejects.toThrow("Hash secret is required.");
|
|
24
|
+
});
|
|
25
|
+
it("should use HASH_SECRET env when no secret provided", async () => {
|
|
26
|
+
process.env.HASH_SECRET = TEST_SECRET;
|
|
27
|
+
const moduleRef = await testing_1.Test.createTestingModule({
|
|
28
|
+
imports: [_1.HashModule.register({})],
|
|
29
|
+
}).compile();
|
|
30
|
+
const hashService = moduleRef.get(_1.HashService);
|
|
31
|
+
expect(hashService).toBeDefined();
|
|
32
|
+
const hashed = await hashService.hash("password");
|
|
33
|
+
expect(hashed).toContain("$argon2");
|
|
34
|
+
});
|
|
35
|
+
it("should use APP_SECRET env when no secret or HASH_SECRET provided", async () => {
|
|
36
|
+
process.env.APP_SECRET = TEST_SECRET;
|
|
37
|
+
const moduleRef = await testing_1.Test.createTestingModule({
|
|
38
|
+
imports: [_1.HashModule.register({})],
|
|
39
|
+
}).compile();
|
|
40
|
+
const hashService = moduleRef.get(_1.HashService);
|
|
41
|
+
expect(hashService).toBeDefined();
|
|
42
|
+
const hashed = await hashService.hash("password");
|
|
43
|
+
expect(hashed).toContain("$argon2");
|
|
44
|
+
});
|
|
45
|
+
it("should throw error when secret is too short", async () => {
|
|
46
|
+
await expect(testing_1.Test.createTestingModule({
|
|
47
|
+
imports: [_1.HashModule.register({ secret: "short" })],
|
|
48
|
+
}).compile()).rejects.toThrow("Hash secret must be at least 32 characters long.");
|
|
49
|
+
});
|
|
50
|
+
it("should throw error when secret has low entropy", async () => {
|
|
51
|
+
// A secret that is 32 chars but low entropy (all same character)
|
|
52
|
+
const lowEntropySecret = "a".repeat(32);
|
|
53
|
+
await expect(testing_1.Test.createTestingModule({
|
|
54
|
+
imports: [_1.HashModule.register({ secret: lowEntropySecret })],
|
|
55
|
+
}).compile()).rejects.toThrow("Hash secret appears low-entropy.");
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
describe("registerAsync", () => {
|
|
59
|
+
it("should register module with async factory", async () => {
|
|
60
|
+
const moduleRef = await testing_1.Test.createTestingModule({
|
|
61
|
+
imports: [
|
|
62
|
+
_1.HashModule.registerAsync({
|
|
63
|
+
useFactory: () => ({
|
|
64
|
+
secret: TEST_SECRET,
|
|
65
|
+
}),
|
|
66
|
+
}),
|
|
67
|
+
],
|
|
68
|
+
}).compile();
|
|
69
|
+
const hashService = moduleRef.get(_1.HashService);
|
|
70
|
+
expect(hashService).toBeDefined();
|
|
71
|
+
const hashed = await hashService.hash("password");
|
|
72
|
+
expect(hashed).toContain("$argon2");
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
//# sourceMappingURL=hash.module.spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hash.module.spec.js","sourceRoot":"","sources":["../src/hash.module.spec.ts"],"names":[],"mappings":"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"}
|
package/dist/hash.service.d.ts
CHANGED
|
@@ -42,6 +42,14 @@ export declare class HashService {
|
|
|
42
42
|
* @throws Error if HashService has not been initialized via `init()`
|
|
43
43
|
*/
|
|
44
44
|
static hash(value: string | Buffer, secret?: string): Promise<string>;
|
|
45
|
+
/**
|
|
46
|
+
* Creates a hash synchronously from the given value using the static instance.
|
|
47
|
+
* @param value - The value to hash (password or other sensitive data)
|
|
48
|
+
* @param secret - Optional secret key to use instead of the default
|
|
49
|
+
* @returns The hashed string
|
|
50
|
+
* @throws Error if HashService has not been initialized via `init()`
|
|
51
|
+
*/
|
|
52
|
+
static hashSync(value: string | Buffer, secret?: string): string;
|
|
45
53
|
/**
|
|
46
54
|
* Verifies a value against a hash using the static instance.
|
|
47
55
|
* @param hashed - The hash to verify against
|
|
@@ -51,6 +59,15 @@ export declare class HashService {
|
|
|
51
59
|
* @throws Error if HashService has not been initialized via `init()`
|
|
52
60
|
*/
|
|
53
61
|
static verify(hashed: string | Buffer, value: string | Buffer, secret?: string): Promise<boolean>;
|
|
62
|
+
/**
|
|
63
|
+
* Verifies a value against a hash synchronously using the static instance.
|
|
64
|
+
* @param hashed - The hash to verify against
|
|
65
|
+
* @param value - The value to verify
|
|
66
|
+
* @param secret - Optional secret key to use instead of the default
|
|
67
|
+
* @returns True if the value matches the hash, false otherwise
|
|
68
|
+
* @throws Error if HashService has not been initialized via `init()`
|
|
69
|
+
*/
|
|
70
|
+
static verifySync(hashed: string | Buffer, value: string | Buffer, secret?: string): boolean;
|
|
54
71
|
private readonly secret?;
|
|
55
72
|
/**
|
|
56
73
|
* Creates an instance of HashService.
|
|
@@ -62,16 +79,23 @@ export declare class HashService {
|
|
|
62
79
|
* @param value - The value to hash (password or other sensitive data)
|
|
63
80
|
* @param secret - Optional secret key to use instead of the default
|
|
64
81
|
* @returns The hashed string
|
|
82
|
+
* @deprecated Use `hash` instead
|
|
65
83
|
*/
|
|
66
|
-
|
|
84
|
+
create(value: string | Buffer, secret?: string): Promise<string>;
|
|
67
85
|
/**
|
|
68
86
|
* Creates a hash from the given value using Argon2.
|
|
69
87
|
* @param value - The value to hash (password or other sensitive data)
|
|
70
88
|
* @param secret - Optional secret key to use instead of the default
|
|
71
89
|
* @returns The hashed string
|
|
72
|
-
* @deprecated Use `hash` instead
|
|
73
90
|
*/
|
|
74
|
-
|
|
91
|
+
hash(value: string | Buffer, secret?: string): Promise<string>;
|
|
92
|
+
/**
|
|
93
|
+
* Creates a hash synchronously from the given value using Argon2.
|
|
94
|
+
* @param value - The value to hash (password or other sensitive data)
|
|
95
|
+
* @param secret - Optional secret key to use instead of the default
|
|
96
|
+
* @returns The hashed string
|
|
97
|
+
*/
|
|
98
|
+
hashSync(value: string | Buffer, secret?: string): string;
|
|
75
99
|
/**
|
|
76
100
|
* Verifies a value against a hash.
|
|
77
101
|
* @param hashed - The hash to verify against
|
|
@@ -80,4 +104,12 @@ export declare class HashService {
|
|
|
80
104
|
* @returns True if the value matches the hash, false otherwise
|
|
81
105
|
*/
|
|
82
106
|
verify(hashed: string | Buffer, value: string | Buffer, secret?: string): Promise<boolean>;
|
|
107
|
+
/**
|
|
108
|
+
* Verifies a value against a hash synchronously.
|
|
109
|
+
* @param hashed - The hash to verify against
|
|
110
|
+
* @param value - The value to verify
|
|
111
|
+
* @param secret - Optional secret key to use instead of the default
|
|
112
|
+
* @returns True if the value matches the hash, false otherwise
|
|
113
|
+
*/
|
|
114
|
+
verifySync(hashed: string | Buffer, value: string | Buffer, secret?: string): boolean;
|
|
83
115
|
}
|
package/dist/hash.service.js
CHANGED
|
@@ -54,6 +54,16 @@ class HashService {
|
|
|
54
54
|
static hash(value, secret) {
|
|
55
55
|
return this.instance.hash(value, secret);
|
|
56
56
|
}
|
|
57
|
+
/**
|
|
58
|
+
* Creates a hash synchronously from the given value using the static instance.
|
|
59
|
+
* @param value - The value to hash (password or other sensitive data)
|
|
60
|
+
* @param secret - Optional secret key to use instead of the default
|
|
61
|
+
* @returns The hashed string
|
|
62
|
+
* @throws Error if HashService has not been initialized via `init()`
|
|
63
|
+
*/
|
|
64
|
+
static hashSync(value, secret) {
|
|
65
|
+
return this.instance.hashSync(value, secret);
|
|
66
|
+
}
|
|
57
67
|
/**
|
|
58
68
|
* Verifies a value against a hash using the static instance.
|
|
59
69
|
* @param hashed - The hash to verify against
|
|
@@ -65,6 +75,17 @@ class HashService {
|
|
|
65
75
|
static verify(hashed, value, secret) {
|
|
66
76
|
return this.instance.verify(hashed, value, secret);
|
|
67
77
|
}
|
|
78
|
+
/**
|
|
79
|
+
* Verifies a value against a hash synchronously using the static instance.
|
|
80
|
+
* @param hashed - The hash to verify against
|
|
81
|
+
* @param value - The value to verify
|
|
82
|
+
* @param secret - Optional secret key to use instead of the default
|
|
83
|
+
* @returns True if the value matches the hash, false otherwise
|
|
84
|
+
* @throws Error if HashService has not been initialized via `init()`
|
|
85
|
+
*/
|
|
86
|
+
static verifySync(hashed, value, secret) {
|
|
87
|
+
return this.instance.verifySync(hashed, value, secret);
|
|
88
|
+
}
|
|
68
89
|
/**
|
|
69
90
|
* Creates an instance of HashService.
|
|
70
91
|
* @param secret - Optional secret key to use for hashing
|
|
@@ -72,6 +93,16 @@ class HashService {
|
|
|
72
93
|
constructor(secret) {
|
|
73
94
|
this.secret = secret ? Buffer.from(secret) : undefined;
|
|
74
95
|
}
|
|
96
|
+
/**
|
|
97
|
+
* Creates a hash from the given value using Argon2.
|
|
98
|
+
* @param value - The value to hash (password or other sensitive data)
|
|
99
|
+
* @param secret - Optional secret key to use instead of the default
|
|
100
|
+
* @returns The hashed string
|
|
101
|
+
* @deprecated Use `hash` instead
|
|
102
|
+
*/
|
|
103
|
+
async create(value, secret) {
|
|
104
|
+
return await this.hash(value, secret);
|
|
105
|
+
}
|
|
75
106
|
/**
|
|
76
107
|
* Creates a hash from the given value using Argon2.
|
|
77
108
|
* @param value - The value to hash (password or other sensitive data)
|
|
@@ -84,14 +115,15 @@ class HashService {
|
|
|
84
115
|
});
|
|
85
116
|
}
|
|
86
117
|
/**
|
|
87
|
-
* Creates a hash from the given value using Argon2.
|
|
118
|
+
* Creates a hash synchronously from the given value using Argon2.
|
|
88
119
|
* @param value - The value to hash (password or other sensitive data)
|
|
89
120
|
* @param secret - Optional secret key to use instead of the default
|
|
90
121
|
* @returns The hashed string
|
|
91
|
-
* @deprecated Use `hash` instead
|
|
92
122
|
*/
|
|
93
|
-
|
|
94
|
-
return
|
|
123
|
+
hashSync(value, secret) {
|
|
124
|
+
return (0, argon2_1.hashSync)(value, {
|
|
125
|
+
secret: typeof secret !== "undefined" ? Buffer.from(secret) : this.secret,
|
|
126
|
+
});
|
|
95
127
|
}
|
|
96
128
|
/**
|
|
97
129
|
* Verifies a value against a hash.
|
|
@@ -105,6 +137,18 @@ class HashService {
|
|
|
105
137
|
secret: typeof secret !== "undefined" ? Buffer.from(secret) : this.secret,
|
|
106
138
|
});
|
|
107
139
|
}
|
|
140
|
+
/**
|
|
141
|
+
* Verifies a value against a hash synchronously.
|
|
142
|
+
* @param hashed - The hash to verify against
|
|
143
|
+
* @param value - The value to verify
|
|
144
|
+
* @param secret - Optional secret key to use instead of the default
|
|
145
|
+
* @returns True if the value matches the hash, false otherwise
|
|
146
|
+
*/
|
|
147
|
+
verifySync(hashed, value, secret) {
|
|
148
|
+
return (0, argon2_1.verifySync)(hashed, value, {
|
|
149
|
+
secret: typeof secret !== "undefined" ? Buffer.from(secret) : this.secret,
|
|
150
|
+
});
|
|
151
|
+
}
|
|
108
152
|
}
|
|
109
153
|
exports.HashService = HashService;
|
|
110
154
|
//# sourceMappingURL=hash.service.js.map
|
package/dist/hash.service.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hash.service.js","sourceRoot":"","sources":["../src/hash.service.ts"],"names":[],"mappings":";;;AAAA,
|
|
1
|
+
{"version":3,"file":"hash.service.js","sourceRoot":"","sources":["../src/hash.service.ts"],"names":[],"mappings":"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"}
|
|
@@ -3,8 +3,10 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
4
|
const testing_1 = require("@nestjs/testing");
|
|
5
5
|
const _1 = require(".");
|
|
6
|
+
const TEST_SECRET = "myTestSecretThatIsAtLeast32Chars!";
|
|
7
|
+
const TEST_SECRET_ALT = "anotherSecretThatIsAtLeast32Char";
|
|
6
8
|
describe("HashService", () => {
|
|
7
|
-
const globalSecret =
|
|
9
|
+
const globalSecret = TEST_SECRET;
|
|
8
10
|
describe("instance methods (via HashModule.register)", () => {
|
|
9
11
|
let hashService;
|
|
10
12
|
beforeEach(async () => {
|
|
@@ -16,7 +18,7 @@ describe("HashService", () => {
|
|
|
16
18
|
describe("hash", () => {
|
|
17
19
|
it("should create a hash with the provided value and secret", async () => {
|
|
18
20
|
const value = "password";
|
|
19
|
-
const secret =
|
|
21
|
+
const secret = TEST_SECRET_ALT;
|
|
20
22
|
const result = await hashService.hash(value, secret);
|
|
21
23
|
expect(result).toBeDefined();
|
|
22
24
|
expect(result).toContain("$argon2");
|
|
@@ -31,7 +33,7 @@ describe("HashService", () => {
|
|
|
31
33
|
describe("create (deprecated)", () => {
|
|
32
34
|
it("should create a hash with the provided value and secret", async () => {
|
|
33
35
|
const value = "password";
|
|
34
|
-
const secret =
|
|
36
|
+
const secret = TEST_SECRET_ALT;
|
|
35
37
|
const result = await hashService.create(value, secret);
|
|
36
38
|
expect(result).toBeDefined();
|
|
37
39
|
expect(result).toContain("$argon2");
|
|
@@ -46,7 +48,7 @@ describe("HashService", () => {
|
|
|
46
48
|
describe("verify", () => {
|
|
47
49
|
it("should verify the hashed value with the provided value and secret", async () => {
|
|
48
50
|
const value = "password";
|
|
49
|
-
const secret =
|
|
51
|
+
const secret = TEST_SECRET_ALT;
|
|
50
52
|
const hashed = await hashService.hash(value, secret);
|
|
51
53
|
const result = await hashService.verify(hashed, value, secret);
|
|
52
54
|
expect(result).toBe(true);
|
|
@@ -76,13 +78,13 @@ describe("HashService", () => {
|
|
|
76
78
|
expect(() => _1.HashService.instance).toThrow("HashService not initialized");
|
|
77
79
|
});
|
|
78
80
|
it("should return instance after initialization", () => {
|
|
79
|
-
_1.HashService.init(
|
|
81
|
+
_1.HashService.init(TEST_SECRET);
|
|
80
82
|
expect(_1.HashService.instance).toBeInstanceOf(_1.HashService);
|
|
81
83
|
});
|
|
82
84
|
});
|
|
83
85
|
describe("init", () => {
|
|
84
86
|
it("should initialize with secret", () => {
|
|
85
|
-
_1.HashService.init(
|
|
87
|
+
_1.HashService.init(TEST_SECRET);
|
|
86
88
|
expect(_1.HashService.instance).toBeDefined();
|
|
87
89
|
});
|
|
88
90
|
it("should initialize without secret", () => {
|
|
@@ -92,16 +94,16 @@ describe("HashService", () => {
|
|
|
92
94
|
});
|
|
93
95
|
describe("hash (static)", () => {
|
|
94
96
|
it("should create a hash using static method", async () => {
|
|
95
|
-
_1.HashService.init(
|
|
97
|
+
_1.HashService.init(TEST_SECRET);
|
|
96
98
|
const value = "password";
|
|
97
99
|
const result = await _1.HashService.hash(value);
|
|
98
100
|
expect(result).toBeDefined();
|
|
99
101
|
expect(result).toContain("$argon2");
|
|
100
102
|
});
|
|
101
103
|
it("should create a hash with custom secret using static method", async () => {
|
|
102
|
-
_1.HashService.init();
|
|
104
|
+
_1.HashService.init(TEST_SECRET);
|
|
103
105
|
const value = "password";
|
|
104
|
-
const secret =
|
|
106
|
+
const secret = TEST_SECRET_ALT;
|
|
105
107
|
const result = await _1.HashService.hash(value, secret);
|
|
106
108
|
expect(result).toBeDefined();
|
|
107
109
|
expect(result).toContain("$argon2");
|
|
@@ -109,25 +111,66 @@ describe("HashService", () => {
|
|
|
109
111
|
});
|
|
110
112
|
describe("verify (static)", () => {
|
|
111
113
|
it("should verify using static method", async () => {
|
|
112
|
-
_1.HashService.init(
|
|
114
|
+
_1.HashService.init(TEST_SECRET);
|
|
113
115
|
const value = "password";
|
|
114
116
|
const hashed = await _1.HashService.hash(value);
|
|
115
117
|
const result = await _1.HashService.verify(hashed, value);
|
|
116
118
|
expect(result).toBe(true);
|
|
117
119
|
});
|
|
118
120
|
it("should verify with custom secret using static method", async () => {
|
|
119
|
-
_1.HashService.init();
|
|
121
|
+
_1.HashService.init(TEST_SECRET);
|
|
120
122
|
const value = "password";
|
|
121
|
-
const secret =
|
|
123
|
+
const secret = TEST_SECRET_ALT;
|
|
122
124
|
const hashed = await _1.HashService.hash(value, secret);
|
|
123
125
|
const result = await _1.HashService.verify(hashed, value, secret);
|
|
124
126
|
expect(result).toBe(true);
|
|
125
127
|
});
|
|
126
128
|
});
|
|
129
|
+
describe("hashSync (static)", () => {
|
|
130
|
+
it("should create a hash synchronously using static method", () => {
|
|
131
|
+
_1.HashService.init(TEST_SECRET);
|
|
132
|
+
const value = "password";
|
|
133
|
+
const result = _1.HashService.hashSync(value);
|
|
134
|
+
expect(result).toBeDefined();
|
|
135
|
+
expect(result).toContain("$argon2");
|
|
136
|
+
});
|
|
137
|
+
it("should create a hash synchronously with custom secret using static method", () => {
|
|
138
|
+
_1.HashService.init(TEST_SECRET);
|
|
139
|
+
const value = "password";
|
|
140
|
+
const secret = TEST_SECRET_ALT;
|
|
141
|
+
const result = _1.HashService.hashSync(value, secret);
|
|
142
|
+
expect(result).toBeDefined();
|
|
143
|
+
expect(result).toContain("$argon2");
|
|
144
|
+
});
|
|
145
|
+
});
|
|
146
|
+
describe("verifySync (static)", () => {
|
|
147
|
+
it("should verify synchronously using static method", () => {
|
|
148
|
+
_1.HashService.init(TEST_SECRET);
|
|
149
|
+
const value = "password";
|
|
150
|
+
const hashed = _1.HashService.hashSync(value);
|
|
151
|
+
const result = _1.HashService.verifySync(hashed, value);
|
|
152
|
+
expect(result).toBe(true);
|
|
153
|
+
});
|
|
154
|
+
it("should verify synchronously with custom secret using static method", () => {
|
|
155
|
+
_1.HashService.init(TEST_SECRET);
|
|
156
|
+
const value = "password";
|
|
157
|
+
const secret = TEST_SECRET_ALT;
|
|
158
|
+
const hashed = _1.HashService.hashSync(value, secret);
|
|
159
|
+
const result = _1.HashService.verifySync(hashed, value, secret);
|
|
160
|
+
expect(result).toBe(true);
|
|
161
|
+
});
|
|
162
|
+
it("should return false for incorrect password synchronously", () => {
|
|
163
|
+
_1.HashService.init(TEST_SECRET);
|
|
164
|
+
const value = "password";
|
|
165
|
+
const hashed = _1.HashService.hashSync(value);
|
|
166
|
+
const result = _1.HashService.verifySync(hashed, "wrongpassword");
|
|
167
|
+
expect(result).toBe(false);
|
|
168
|
+
});
|
|
169
|
+
});
|
|
127
170
|
});
|
|
128
171
|
describe("constructor", () => {
|
|
129
172
|
it("should create instance with secret", () => {
|
|
130
|
-
const service = new _1.HashService(
|
|
173
|
+
const service = new _1.HashService(TEST_SECRET);
|
|
131
174
|
expect(service).toBeDefined();
|
|
132
175
|
});
|
|
133
176
|
it("should create instance without secret", () => {
|
|
@@ -136,66 +179,4 @@ describe("HashService", () => {
|
|
|
136
179
|
});
|
|
137
180
|
});
|
|
138
181
|
});
|
|
139
|
-
describe("HashModule", () => {
|
|
140
|
-
const originalEnv = process.env;
|
|
141
|
-
beforeEach(() => {
|
|
142
|
-
// Reset static instance before each test
|
|
143
|
-
_1.HashService._instance = undefined;
|
|
144
|
-
// Reset environment variables
|
|
145
|
-
process.env = { ...originalEnv };
|
|
146
|
-
delete process.env.HASH_SECRET;
|
|
147
|
-
delete process.env.APP_SECRET;
|
|
148
|
-
});
|
|
149
|
-
afterAll(() => {
|
|
150
|
-
process.env = originalEnv;
|
|
151
|
-
});
|
|
152
|
-
describe("registerAsync", () => {
|
|
153
|
-
it("should register module with async factory", async () => {
|
|
154
|
-
const moduleRef = await testing_1.Test.createTestingModule({
|
|
155
|
-
imports: [
|
|
156
|
-
_1.HashModule.registerAsync({
|
|
157
|
-
useFactory: () => ({
|
|
158
|
-
secret: "asyncSecret",
|
|
159
|
-
}),
|
|
160
|
-
}),
|
|
161
|
-
],
|
|
162
|
-
}).compile();
|
|
163
|
-
const hashService = moduleRef.get(_1.HashService);
|
|
164
|
-
expect(hashService).toBeDefined();
|
|
165
|
-
const hashed = await hashService.hash("password");
|
|
166
|
-
expect(hashed).toContain("$argon2");
|
|
167
|
-
});
|
|
168
|
-
});
|
|
169
|
-
describe("register with fallback secrets", () => {
|
|
170
|
-
it("should use HASH_SECRET env when no secret provided", async () => {
|
|
171
|
-
process.env.HASH_SECRET = "envHashSecret";
|
|
172
|
-
const moduleRef = await testing_1.Test.createTestingModule({
|
|
173
|
-
imports: [_1.HashModule.register({})],
|
|
174
|
-
}).compile();
|
|
175
|
-
const hashService = moduleRef.get(_1.HashService);
|
|
176
|
-
expect(hashService).toBeDefined();
|
|
177
|
-
const hashed = await hashService.hash("password");
|
|
178
|
-
expect(hashed).toContain("$argon2");
|
|
179
|
-
});
|
|
180
|
-
it("should use APP_SECRET env when no secret or HASH_SECRET provided", async () => {
|
|
181
|
-
process.env.APP_SECRET = "envAppSecret";
|
|
182
|
-
const moduleRef = await testing_1.Test.createTestingModule({
|
|
183
|
-
imports: [_1.HashModule.register({})],
|
|
184
|
-
}).compile();
|
|
185
|
-
const hashService = moduleRef.get(_1.HashService);
|
|
186
|
-
expect(hashService).toBeDefined();
|
|
187
|
-
const hashed = await hashService.hash("password");
|
|
188
|
-
expect(hashed).toContain("$argon2");
|
|
189
|
-
});
|
|
190
|
-
it("should work without any secret", async () => {
|
|
191
|
-
const moduleRef = await testing_1.Test.createTestingModule({
|
|
192
|
-
imports: [_1.HashModule.register({})],
|
|
193
|
-
}).compile();
|
|
194
|
-
const hashService = moduleRef.get(_1.HashService);
|
|
195
|
-
expect(hashService).toBeDefined();
|
|
196
|
-
const hashed = await hashService.hash("password");
|
|
197
|
-
expect(hashed).toContain("$argon2");
|
|
198
|
-
});
|
|
199
|
-
});
|
|
200
|
-
});
|
|
201
182
|
//# sourceMappingURL=hash.service.spec.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hash.service.spec.js","sourceRoot":"","sources":["../src/hash.service.spec.ts"],"names":[],"mappings":";AAAA,qDAAqD;;AAErD,6CAAuC;AAEvC,wBAA4C;AAE5C,QAAQ,CAAC,aAAa,EAAE,GAAG,EAAE;IAC3B,MAAM,YAAY,GAAG,
|
|
1
|
+
{"version":3,"file":"hash.service.spec.js","sourceRoot":"","sources":["../src/hash.service.spec.ts"],"names":[],"mappings":"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"}
|