@proteinjs/user-server 1.22.1 → 1.22.2
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/CHANGELOG.md +13 -0
- package/dist/generated/index.js +1 -1
- package/dist/generated/index.js.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -1
- package/dist/index.js.map +1 -1
- package/dist/src/authentication/PasswordHasher.d.ts +9 -0
- package/dist/src/authentication/PasswordHasher.d.ts.map +1 -1
- package/dist/src/authentication/PasswordHasher.js +25 -0
- package/dist/src/authentication/PasswordHasher.js.map +1 -1
- package/dist/src/authentication/authenticate.d.ts +15 -0
- package/dist/src/authentication/authenticate.d.ts.map +1 -1
- package/dist/src/authentication/authenticate.js +55 -20
- package/dist/src/authentication/authenticate.js.map +1 -1
- package/dist/src/routes/executePasswordReset.d.ts +4 -0
- package/dist/src/routes/executePasswordReset.d.ts.map +1 -1
- package/dist/src/routes/executePasswordReset.js +7 -0
- package/dist/src/routes/executePasswordReset.js.map +1 -1
- package/dist/src/routes/initiatePasswordReset.d.ts +7 -0
- package/dist/src/routes/initiatePasswordReset.d.ts.map +1 -1
- package/dist/src/routes/initiatePasswordReset.js +28 -10
- package/dist/src/routes/initiatePasswordReset.js.map +1 -1
- package/dist/src/routes/login.d.ts +8 -0
- package/dist/src/routes/login.d.ts.map +1 -1
- package/dist/src/routes/login.js +63 -22
- package/dist/src/routes/login.js.map +1 -1
- package/dist/src/throttle/ClientAddress.d.ts +35 -0
- package/dist/src/throttle/ClientAddress.d.ts.map +1 -0
- package/dist/src/throttle/ClientAddress.js +48 -0
- package/dist/src/throttle/ClientAddress.js.map +1 -0
- package/dist/src/throttle/PasswordResetThrottle.d.ts +39 -0
- package/dist/src/throttle/PasswordResetThrottle.d.ts.map +1 -0
- package/dist/src/throttle/PasswordResetThrottle.js +59 -0
- package/dist/src/throttle/PasswordResetThrottle.js.map +1 -0
- package/dist/src/throttle/RequestDigests.d.ts +42 -0
- package/dist/src/throttle/RequestDigests.d.ts.map +1 -0
- package/dist/src/throttle/RequestDigests.js +96 -0
- package/dist/src/throttle/RequestDigests.js.map +1 -0
- package/dist/src/throttle/SignInThrottle.d.ts +67 -0
- package/dist/src/throttle/SignInThrottle.d.ts.map +1 -0
- package/dist/src/throttle/SignInThrottle.js +91 -0
- package/dist/src/throttle/SignInThrottle.js.map +1 -0
- package/dist/src/throttle/SlidingWindow.d.ts +51 -0
- package/dist/src/throttle/SlidingWindow.d.ts.map +1 -0
- package/dist/src/throttle/SlidingWindow.js +85 -0
- package/dist/src/throttle/SlidingWindow.js.map +1 -0
- package/dist/test/ClientAddress.test.d.ts +2 -0
- package/dist/test/ClientAddress.test.d.ts.map +1 -0
- package/dist/test/ClientAddress.test.js +51 -0
- package/dist/test/ClientAddress.test.js.map +1 -0
- package/dist/test/InitiatePasswordReset.test.js +373 -5
- package/dist/test/InitiatePasswordReset.test.js.map +1 -1
- package/dist/test/LoginThrottle.integration.test.d.ts +2 -0
- package/dist/test/LoginThrottle.integration.test.d.ts.map +1 -0
- package/dist/test/LoginThrottle.integration.test.js +616 -0
- package/dist/test/LoginThrottle.integration.test.js.map +1 -0
- package/dist/test/RequestDigests.test.d.ts +2 -0
- package/dist/test/RequestDigests.test.d.ts.map +1 -0
- package/dist/test/RequestDigests.test.js +73 -0
- package/dist/test/RequestDigests.test.js.map +1 -0
- package/dist/test/SlidingWindow.test.d.ts +2 -0
- package/dist/test/SlidingWindow.test.d.ts.map +1 -0
- package/dist/test/SlidingWindow.test.js +72 -0
- package/dist/test/SlidingWindow.test.js.map +1 -0
- package/generated/index.ts +1 -1
- package/index.ts +5 -0
- package/package.json +2 -2
- package/src/authentication/PasswordHasher.ts +18 -1
- package/src/authentication/authenticate.ts +36 -9
- package/src/routes/executePasswordReset.ts +7 -0
- package/src/routes/initiatePasswordReset.ts +29 -11
- package/src/routes/login.ts +39 -8
- package/src/throttle/ClientAddress.ts +47 -0
- package/src/throttle/PasswordResetThrottle.ts +62 -0
- package/src/throttle/RequestDigests.ts +97 -0
- package/src/throttle/SignInThrottle.ts +98 -0
- package/src/throttle/SlidingWindow.ts +90 -0
- package/test/ClientAddress.test.ts +54 -0
- package/test/InitiatePasswordReset.test.ts +212 -6
- package/test/LoginThrottle.integration.test.ts +339 -0
- package/test/RequestDigests.test.ts +81 -0
- package/test/SlidingWindow.test.ts +85 -0
- package/test/setup.js +2 -0
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
var crypto_1 = require("crypto");
|
|
4
|
+
var RequestDigests_1 = require("../src/throttle/RequestDigests");
|
|
5
|
+
/**
|
|
6
|
+
* The two keyed digests the throttled doors key their windows on and write on their log lines:
|
|
7
|
+
* the account digest (a keyed hash of the lowercased address) and the coarse IP hash. Both are
|
|
8
|
+
* stable for one key (so an operator can say "one account, one device" across lines, pods and
|
|
9
|
+
* restarts) and useless without it (never the address, never a plain hash a list of addresses
|
|
10
|
+
* could reverse).
|
|
11
|
+
*/
|
|
12
|
+
describe('RequestDigests', function () {
|
|
13
|
+
var digests = new RequestDigests_1.RequestDigests({ secret: 'test-session-secret' });
|
|
14
|
+
it('the account digest is stable for one address whatever its case or surrounding spaces', function () {
|
|
15
|
+
var digest = digests.account('Ada.Lovelace@Example.com');
|
|
16
|
+
expect(digest).toMatch(/^[0-9a-f]{16}$/);
|
|
17
|
+
expect(digests.account('ada.lovelace@example.com')).toBe(digest);
|
|
18
|
+
expect(digests.account(' ADA.LOVELACE@EXAMPLE.COM ')).toBe(digest);
|
|
19
|
+
expect(digests.account('ada.lovelace@example.org')).not.toBe(digest);
|
|
20
|
+
});
|
|
21
|
+
it('the account digest is keyed: another key gives another digest, and it is no plain hash of the address', function () {
|
|
22
|
+
var address = 'ada.lovelace@example.com';
|
|
23
|
+
var digest = digests.account(address);
|
|
24
|
+
expect(new RequestDigests_1.RequestDigests({ secret: 'another-secret' }).account(address)).not.toBe(digest);
|
|
25
|
+
expect((0, crypto_1.createHash)('sha256').update(address).digest('hex')).not.toContain(digest);
|
|
26
|
+
expect(digest).not.toContain('ada');
|
|
27
|
+
});
|
|
28
|
+
it('the account digest and the coarse IP hash are separate: one value never reads as the other', function () {
|
|
29
|
+
expect(digests.account('203.0.113.7')).not.toBe(digests.coarseIp('203.0.113.7'));
|
|
30
|
+
});
|
|
31
|
+
it('the coarse IP hash is stable for one IPv4 address and differs for the next', function () {
|
|
32
|
+
var hash = digests.coarseIp('203.0.113.7');
|
|
33
|
+
expect(hash).toMatch(/^[0-9a-f]{16}$/);
|
|
34
|
+
expect(digests.coarseIp('203.0.113.7')).toBe(hash);
|
|
35
|
+
expect(digests.coarseIp('::ffff:203.0.113.7')).toBe(hash);
|
|
36
|
+
expect(digests.coarseIp('203.0.113.8')).not.toBe(hash);
|
|
37
|
+
});
|
|
38
|
+
it('IPv6 is coarsened to its /64 — one device rotates freely inside its own /64', function () {
|
|
39
|
+
var hash = digests.coarseIp('2001:db8:1:2:aaaa:bbbb:cccc:dddd');
|
|
40
|
+
expect(digests.coarseIp('2001:db8:1:2::1')).toBe(hash);
|
|
41
|
+
expect(digests.coarseIp('2001:0db8:0001:0002:ffff:0:0:1')).toBe(hash);
|
|
42
|
+
expect(digests.coarseIp('2001:db8:1:3::1')).not.toBe(hash);
|
|
43
|
+
});
|
|
44
|
+
it('with no key configured the digests refuse to run — never a plain hash, never a key of their own', function () {
|
|
45
|
+
var original = process.env.SESSION_SECRET;
|
|
46
|
+
delete process.env.SESSION_SECRET;
|
|
47
|
+
try {
|
|
48
|
+
expect(function () { return new RequestDigests_1.RequestDigests().account('ada.lovelace@example.com'); }).toThrow(/SESSION_SECRET/);
|
|
49
|
+
expect(function () { return new RequestDigests_1.RequestDigests({ secret: '' }).coarseIp('203.0.113.7'); }).toThrow(/SESSION_SECRET/);
|
|
50
|
+
}
|
|
51
|
+
finally {
|
|
52
|
+
if (original !== undefined) {
|
|
53
|
+
process.env.SESSION_SECRET = original;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
it('reads the key the deployment configures (SESSION_SECRET) when none is passed', function () {
|
|
58
|
+
var original = process.env.SESSION_SECRET;
|
|
59
|
+
process.env.SESSION_SECRET = 'test-session-secret';
|
|
60
|
+
try {
|
|
61
|
+
expect(new RequestDigests_1.RequestDigests().account('ada.lovelace@example.com')).toBe(digests.account('ada.lovelace@example.com'));
|
|
62
|
+
}
|
|
63
|
+
finally {
|
|
64
|
+
if (original === undefined) {
|
|
65
|
+
delete process.env.SESSION_SECRET;
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
process.env.SESSION_SECRET = original;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
//# sourceMappingURL=RequestDigests.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RequestDigests.test.js","sourceRoot":"","sources":["../../test/RequestDigests.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SlidingWindow.test.d.ts","sourceRoot":"","sources":["../../test/SlidingWindow.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
var SlidingWindow_1 = require("../src/throttle/SlidingWindow");
|
|
4
|
+
/**
|
|
5
|
+
* The per-key sliding window every throttled door shares (the sign-in and reset doors here, the
|
|
6
|
+
* invite-request door in the consuming app): `hit` records and answers whether the key is over,
|
|
7
|
+
* `isOver` only reads, `record` only counts, `clear` forgets. Attempts refused as over the window
|
|
8
|
+
* are not counted, so a key drains exactly one window after its last counted attempt.
|
|
9
|
+
*/
|
|
10
|
+
describe('SlidingWindow', function () {
|
|
11
|
+
var MINUTE = 60 * 1000;
|
|
12
|
+
var now = 1700000000000;
|
|
13
|
+
var clock = function () { return now; };
|
|
14
|
+
it('admits up to the limit inside the window and refuses the next', function () {
|
|
15
|
+
var window = new SlidingWindow_1.SlidingWindow({ windowMs: 10 * MINUTE, limit: 3, now: clock });
|
|
16
|
+
expect([window.hit('a'), window.hit('a'), window.hit('a')]).toEqual([false, false, false]);
|
|
17
|
+
expect(window.hit('a')).toBe(true);
|
|
18
|
+
expect(window.isOver('a')).toBe(true);
|
|
19
|
+
});
|
|
20
|
+
it('keeps each key in its own window', function () {
|
|
21
|
+
var window = new SlidingWindow_1.SlidingWindow({ windowMs: 10 * MINUTE, limit: 2, now: clock });
|
|
22
|
+
window.hit('a');
|
|
23
|
+
window.hit('a');
|
|
24
|
+
expect(window.hit('a')).toBe(true);
|
|
25
|
+
expect(window.hit('b')).toBe(false);
|
|
26
|
+
});
|
|
27
|
+
it('drains: an attempt older than the window no longer counts, and refused attempts never counted', function () {
|
|
28
|
+
var window = new SlidingWindow_1.SlidingWindow({ windowMs: 10 * MINUTE, limit: 2, now: clock });
|
|
29
|
+
window.hit('a');
|
|
30
|
+
now += 4 * MINUTE;
|
|
31
|
+
window.hit('a');
|
|
32
|
+
now += 5 * MINUTE;
|
|
33
|
+
expect(window.hit('a')).toBe(true); // both counted attempts are still inside the window
|
|
34
|
+
now += 1 * MINUTE + 1; // the first counted attempt left the window; the refused one never counted
|
|
35
|
+
expect(window.hit('a')).toBe(false);
|
|
36
|
+
expect(window.hit('a')).toBe(true);
|
|
37
|
+
});
|
|
38
|
+
it('isOver reads without counting; record counts without answering; clear forgets the key', function () {
|
|
39
|
+
var window = new SlidingWindow_1.SlidingWindow({ windowMs: 10 * MINUTE, limit: 2, now: clock });
|
|
40
|
+
expect(window.isOver('a')).toBe(false);
|
|
41
|
+
expect(window.isOver('a')).toBe(false);
|
|
42
|
+
window.record('a');
|
|
43
|
+
expect(window.isOver('a')).toBe(false);
|
|
44
|
+
window.record('a');
|
|
45
|
+
expect(window.isOver('a')).toBe(true);
|
|
46
|
+
window.clear('a');
|
|
47
|
+
expect(window.isOver('a')).toBe(false);
|
|
48
|
+
});
|
|
49
|
+
it('forgive uncounts the latest attempt only', function () {
|
|
50
|
+
var window = new SlidingWindow_1.SlidingWindow({ windowMs: 10 * MINUTE, limit: 2, now: clock });
|
|
51
|
+
window.hit('a');
|
|
52
|
+
window.hit('a');
|
|
53
|
+
expect(window.isOver('a')).toBe(true);
|
|
54
|
+
window.forgive('a');
|
|
55
|
+
expect(window.isOver('a')).toBe(false);
|
|
56
|
+
expect(window.hit('a')).toBe(false);
|
|
57
|
+
expect(window.hit('a')).toBe(true);
|
|
58
|
+
window.forgive('b'); // a key never seen: nothing to uncount, nothing thrown
|
|
59
|
+
expect(window.isOver('b')).toBe(false);
|
|
60
|
+
});
|
|
61
|
+
it('bounds the keys it tracks: at the bound the oldest-touched key is forgotten first', function () {
|
|
62
|
+
var window = new SlidingWindow_1.SlidingWindow({ windowMs: 10 * MINUTE, limit: 1, maxKeys: 2, now: clock });
|
|
63
|
+
window.hit('a');
|
|
64
|
+
window.hit('b');
|
|
65
|
+
window.hit('a'); // refused, but 'a' is now the most recently touched
|
|
66
|
+
window.hit('c'); // at the bound: 'b' (oldest-touched) is forgotten
|
|
67
|
+
expect(window.isOver('a')).toBe(true);
|
|
68
|
+
expect(window.isOver('b')).toBe(false);
|
|
69
|
+
expect(window.isOver('c')).toBe(true);
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
//# sourceMappingURL=SlidingWindow.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SlidingWindow.test.js","sourceRoot":"","sources":["../../test/SlidingWindow.test.ts"],"names":[],"mappings":"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"}
|