impulse-api 3.0.3 → 3.0.4
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 +1 -1
- package/src/auth.js +19 -9
- package/src/server.js +5 -1
- package/test/custom-jwt-validation.js +6 -4
- package/test/server-test.js +144 -0
package/package.json
CHANGED
package/src/auth.js
CHANGED
|
@@ -1,11 +1,21 @@
|
|
|
1
1
|
const jwt = require('jsonwebtoken');
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
2
|
+
|
|
3
|
+
class Auth {
|
|
4
|
+
constructor(secretKey) {
|
|
5
|
+
if (!secretKey) {
|
|
6
|
+
throw new Error('Auth instance must be initialized with secretKey');
|
|
7
|
+
}
|
|
8
|
+
this.secretKey = secretKey;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
generateToken(params) {
|
|
12
|
+
return jwt.sign(params, this.secretKey);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
verifyToken(token) {
|
|
16
|
+
return jwt.verify(token, this.secretKey);
|
|
17
|
+
}
|
|
18
|
+
|
|
9
19
|
// Helper function to create custom validators
|
|
10
20
|
createCustomValidator(validatorFn) {
|
|
11
21
|
if (typeof validatorFn !== 'function') {
|
|
@@ -13,6 +23,6 @@ const auth = {
|
|
|
13
23
|
}
|
|
14
24
|
return validatorFn;
|
|
15
25
|
}
|
|
16
|
-
}
|
|
26
|
+
}
|
|
17
27
|
|
|
18
|
-
module.exports =
|
|
28
|
+
module.exports = Auth;
|
package/src/server.js
CHANGED
|
@@ -36,6 +36,7 @@ class Server {
|
|
|
36
36
|
this.heartbeat = config.heartbeat || null;
|
|
37
37
|
this.container = new Container(config.services);
|
|
38
38
|
this.secretKey = config.secretKey;
|
|
39
|
+
this.auth = config.secretKey ? new Auth(config.secretKey) : null;
|
|
39
40
|
this.tokenValidator = config.tokenValidator || null;
|
|
40
41
|
this.errors = config.errors || Errors;
|
|
41
42
|
process.env.NODE_ENV = this.env || 'dev';
|
|
@@ -225,7 +226,10 @@ class Server {
|
|
|
225
226
|
decoded = await this.tokenValidator(token, this.container);
|
|
226
227
|
} else {
|
|
227
228
|
// Default JWT validation
|
|
228
|
-
|
|
229
|
+
if (!this.auth) {
|
|
230
|
+
throw new Error(`Route ${route.name} requires token auth but server was not initialized with secretKey`);
|
|
231
|
+
}
|
|
232
|
+
decoded = this.auth.verifyToken(token);
|
|
229
233
|
}
|
|
230
234
|
|
|
231
235
|
// Allow route-specific validation to override the global validator
|
|
@@ -99,8 +99,9 @@ exports.testRoute = {
|
|
|
99
99
|
await api.init();
|
|
100
100
|
|
|
101
101
|
// Test default JWT validation
|
|
102
|
-
const
|
|
103
|
-
const
|
|
102
|
+
const auth = new Auth('test-secret');
|
|
103
|
+
const token = auth.generateToken({ userId: 'test-user' });
|
|
104
|
+
const decoded = auth.verifyToken(token);
|
|
104
105
|
assert.strictEqual(decoded.userId, 'test-user');
|
|
105
106
|
});
|
|
106
107
|
});
|
|
@@ -290,8 +291,9 @@ exports.testRoute = {
|
|
|
290
291
|
await api.init();
|
|
291
292
|
|
|
292
293
|
// Test that default JWT validation still works
|
|
293
|
-
const
|
|
294
|
-
const
|
|
294
|
+
const auth = new Auth('test-secret');
|
|
295
|
+
const token = auth.generateToken({ userId: 'legacy-user' });
|
|
296
|
+
const decoded = auth.verifyToken(token);
|
|
295
297
|
assert.strictEqual(decoded.userId, 'legacy-user');
|
|
296
298
|
});
|
|
297
299
|
});
|
package/test/server-test.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const Server = require('../src/server');
|
|
2
|
+
const Auth = require('../src/auth');
|
|
2
3
|
const assert = require('assert');
|
|
3
4
|
const sinon = require('sinon');
|
|
4
5
|
|
|
@@ -355,4 +356,147 @@ describe('server-test', () => {
|
|
|
355
356
|
sinon.assert.notCalled(spy);
|
|
356
357
|
});
|
|
357
358
|
});
|
|
359
|
+
|
|
360
|
+
describe('Auth module', () => {
|
|
361
|
+
describe('instantiation', () => {
|
|
362
|
+
it('should throw an error if secretKey is not provided', () => {
|
|
363
|
+
try {
|
|
364
|
+
new Auth();
|
|
365
|
+
assert.fail('Should have thrown an error');
|
|
366
|
+
} catch (e) {
|
|
367
|
+
assert.contains(e.message, 'must be initialized with secretKey');
|
|
368
|
+
}
|
|
369
|
+
});
|
|
370
|
+
|
|
371
|
+
it('should throw an error if secretKey is null', () => {
|
|
372
|
+
try {
|
|
373
|
+
new Auth(null);
|
|
374
|
+
assert.fail('Should have thrown an error');
|
|
375
|
+
} catch (e) {
|
|
376
|
+
assert.contains(e.message, 'must be initialized with secretKey');
|
|
377
|
+
}
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
it('should throw an error if secretKey is undefined', () => {
|
|
381
|
+
try {
|
|
382
|
+
new Auth(undefined);
|
|
383
|
+
assert.fail('Should have thrown an error');
|
|
384
|
+
} catch (e) {
|
|
385
|
+
assert.contains(e.message, 'must be initialized with secretKey');
|
|
386
|
+
}
|
|
387
|
+
});
|
|
388
|
+
|
|
389
|
+
it('should create an instance when secretKey is provided', () => {
|
|
390
|
+
const auth = new Auth('test-secret-key');
|
|
391
|
+
assert.strictEqual(auth.secretKey, 'test-secret-key');
|
|
392
|
+
});
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
describe('generateToken', () => {
|
|
396
|
+
it('should generate a token without requiring secretKey parameter', () => {
|
|
397
|
+
const auth = new Auth('test-secret-key');
|
|
398
|
+
const params = { userId: 'test-user', role: 'admin' };
|
|
399
|
+
const token = auth.generateToken(params);
|
|
400
|
+
assert.strictEqual(typeof token, 'string');
|
|
401
|
+
assert.strictEqual(token.length > 0, true);
|
|
402
|
+
});
|
|
403
|
+
|
|
404
|
+
it('should generate different tokens for different params', () => {
|
|
405
|
+
const auth = new Auth('test-secret-key');
|
|
406
|
+
const token1 = auth.generateToken({ userId: 'user1' });
|
|
407
|
+
const token2 = auth.generateToken({ userId: 'user2' });
|
|
408
|
+
assert.notStrictEqual(token1, token2);
|
|
409
|
+
});
|
|
410
|
+
});
|
|
411
|
+
|
|
412
|
+
describe('verifyToken', () => {
|
|
413
|
+
it('should verify a token without requiring secretKey parameter', () => {
|
|
414
|
+
const auth = new Auth('test-secret-key');
|
|
415
|
+
const params = { userId: 'test-user', role: 'admin' };
|
|
416
|
+
const token = auth.generateToken(params);
|
|
417
|
+
const decoded = auth.verifyToken(token);
|
|
418
|
+
assert.strictEqual(decoded.userId, 'test-user');
|
|
419
|
+
assert.strictEqual(decoded.role, 'admin');
|
|
420
|
+
});
|
|
421
|
+
|
|
422
|
+
it('should throw an error when verifying a token with wrong secretKey', () => {
|
|
423
|
+
const auth1 = new Auth('secret-key-1');
|
|
424
|
+
const auth2 = new Auth('secret-key-2');
|
|
425
|
+
const token = auth1.generateToken({ userId: 'test-user' });
|
|
426
|
+
try {
|
|
427
|
+
auth2.verifyToken(token);
|
|
428
|
+
assert.fail('Should have thrown an error');
|
|
429
|
+
} catch (e) {
|
|
430
|
+
assert.contains(e.message, 'invalid signature');
|
|
431
|
+
}
|
|
432
|
+
});
|
|
433
|
+
|
|
434
|
+
it('should throw an error when verifying an invalid token', () => {
|
|
435
|
+
const auth = new Auth('test-secret-key');
|
|
436
|
+
try {
|
|
437
|
+
auth.verifyToken('invalid-token-string');
|
|
438
|
+
assert.fail('Should have thrown an error');
|
|
439
|
+
} catch (e) {
|
|
440
|
+
assert.strictEqual(typeof e.message, 'string');
|
|
441
|
+
}
|
|
442
|
+
});
|
|
443
|
+
});
|
|
444
|
+
|
|
445
|
+
describe('createCustomValidator', () => {
|
|
446
|
+
it('should throw an error if validator is not a function', () => {
|
|
447
|
+
const auth = new Auth('test-secret-key');
|
|
448
|
+
try {
|
|
449
|
+
auth.createCustomValidator('not-a-function');
|
|
450
|
+
assert.fail('Should have thrown an error');
|
|
451
|
+
} catch (e) {
|
|
452
|
+
assert.contains(e.message, 'must be a function');
|
|
453
|
+
}
|
|
454
|
+
});
|
|
455
|
+
|
|
456
|
+
it('should return the validator function if valid', () => {
|
|
457
|
+
const auth = new Auth('test-secret-key');
|
|
458
|
+
const validatorFn = () => true;
|
|
459
|
+
const result = auth.createCustomValidator(validatorFn);
|
|
460
|
+
assert.strictEqual(result, validatorFn);
|
|
461
|
+
});
|
|
462
|
+
});
|
|
463
|
+
});
|
|
464
|
+
|
|
465
|
+
describe('Server Auth integration', () => {
|
|
466
|
+
it('should create auth instance when secretKey is provided', () => {
|
|
467
|
+
const server = new Server({
|
|
468
|
+
name: 'test-server',
|
|
469
|
+
routeDir: './test-routes',
|
|
470
|
+
port: 4000,
|
|
471
|
+
env: 'test',
|
|
472
|
+
secretKey: 'test-secret-key',
|
|
473
|
+
services: {}
|
|
474
|
+
});
|
|
475
|
+
assert.strictEqual(server.auth !== null, true);
|
|
476
|
+
assert.strictEqual(server.auth.secretKey, 'test-secret-key');
|
|
477
|
+
});
|
|
478
|
+
|
|
479
|
+
it('should not create auth instance when secretKey is not provided', () => {
|
|
480
|
+
const server = new Server({
|
|
481
|
+
name: 'test-server',
|
|
482
|
+
routeDir: './test-routes',
|
|
483
|
+
port: 4000,
|
|
484
|
+
env: 'test',
|
|
485
|
+
services: {}
|
|
486
|
+
});
|
|
487
|
+
assert.strictEqual(server.auth, null);
|
|
488
|
+
});
|
|
489
|
+
|
|
490
|
+
it('should not create auth instance when secretKey is null', () => {
|
|
491
|
+
const server = new Server({
|
|
492
|
+
name: 'test-server',
|
|
493
|
+
routeDir: './test-routes',
|
|
494
|
+
port: 4000,
|
|
495
|
+
env: 'test',
|
|
496
|
+
secretKey: null,
|
|
497
|
+
services: {}
|
|
498
|
+
});
|
|
499
|
+
assert.strictEqual(server.auth, null);
|
|
500
|
+
});
|
|
501
|
+
});
|
|
358
502
|
});
|