pryv 3.13.0 → 3.14.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/README.md +3 -1
- package/package.json +1 -1
- package/src/Auth/AuthController.js +33 -8
- package/src/Auth/ProfileStore.js +19 -1
- package/src/Auth/index.js +5 -2
- package/src/Browser/LoginButton.js +127 -17
- package/src/Connection.js +19 -13
- package/src/Service.js +27 -15
- package/src/index.d.ts +33 -7
- package/src/lib/pollUrls.js +71 -0
- package/src/utils.js +7 -1
- package/test/AuthController.test.js +111 -5
- package/test/Connection.apiOneError.test.js +105 -0
- package/test/LoginButton.test.js +3 -1
- package/test/LoginButton.urlCleanup.test.js +453 -14
- package/test/Service.accessRequest.test.js +4 -4
- package/test/Service.errorBodies.test.js +82 -0
- package/test/Service.mfa.test.js +4 -4
- package/test/Service.pollUrlByKey.test.js +114 -0
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* [BSD-3-Clause](https://github.com/pryv/lib-js/blob/master/LICENSE)
|
|
4
|
+
*/
|
|
5
|
+
/* global describe, it, afterEach, expect, pryv */
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* [PUKX] Polling an auth request by key reaches the core that holds it.
|
|
9
|
+
*
|
|
10
|
+
* On a multi-core platform a pending auth request lives on the core that
|
|
11
|
+
* created it, and the server answers the access request with a poll URL on
|
|
12
|
+
* that core, while the service's `access` URL may reach any core. Polling by
|
|
13
|
+
* key must use the server-issued URL when this process started the request.
|
|
14
|
+
* Observed: the URLs actually fetched (a stubbed global fetch), not only the
|
|
15
|
+
* result.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
const pollUrls = require('../src/lib/pollUrls');
|
|
19
|
+
const AuthController = require('../src/Auth/AuthController');
|
|
20
|
+
const utils = require('../src/utils');
|
|
21
|
+
|
|
22
|
+
const SERVICE_INFO = {
|
|
23
|
+
register: 'https://reg.mc.test.local/',
|
|
24
|
+
access: 'https://access.mc.test.local/access/',
|
|
25
|
+
api: 'https://{username}.mc.test.local/',
|
|
26
|
+
name: 'Test'
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
let keyCounter = 0;
|
|
30
|
+
function freshKey () { return 'puk-key-' + (++keyCounter); }
|
|
31
|
+
function corePoll (key) { return 'https://core-a.mc.test.local/reg/access/' + key; }
|
|
32
|
+
|
|
33
|
+
describe('[PUKX] polling an auth request by key', function () {
|
|
34
|
+
let realFetch;
|
|
35
|
+
let gets;
|
|
36
|
+
|
|
37
|
+
/** service-info, POST <access> -> NEED_SIGNIN with a core poll URL, GET -> `pollBody` */
|
|
38
|
+
function stubFetch (key, pollBody) {
|
|
39
|
+
realFetch = global.fetch;
|
|
40
|
+
gets = [];
|
|
41
|
+
global.fetch = async function (url, options) {
|
|
42
|
+
const u = String(url);
|
|
43
|
+
if (options?.method === 'POST' && u === SERVICE_INFO.access) {
|
|
44
|
+
return { ok: true, status: 201, json: async () => ({ status: 'NEED_SIGNIN', key, poll: corePoll(key), authUrl: 'https://ui.mc.test.local/access?key=' + key, poll_rate_ms: 1000 }) };
|
|
45
|
+
}
|
|
46
|
+
if (u.includes('/access/')) {
|
|
47
|
+
gets.push(u);
|
|
48
|
+
return { ok: true, status: 200, json: async () => pollBody };
|
|
49
|
+
}
|
|
50
|
+
return { ok: true, status: 200, json: async () => SERVICE_INFO };
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
afterEach(function () {
|
|
55
|
+
if (realFetch) global.fetch = realFetch;
|
|
56
|
+
realFetch = null;
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('[PUK1] remembers valid entries only, keeps a bounded number and expires them', function () {
|
|
60
|
+
const realNow = Date.now;
|
|
61
|
+
pollUrls.clear();
|
|
62
|
+
try {
|
|
63
|
+
pollUrls.remember('puk1-a', 'https://core-a.example.com/reg/access/puk1-a');
|
|
64
|
+
pollUrls.remember('puk1-b', 'javascript:alert(1)');
|
|
65
|
+
pollUrls.remember(null, 'https://core-a.example.com/reg/access/x');
|
|
66
|
+
expect(pollUrls.lookup('puk1-a')).to.equal('https://core-a.example.com/reg/access/puk1-a');
|
|
67
|
+
expect(pollUrls.lookup('puk1-b')).to.equal(null);
|
|
68
|
+
for (let i = 0; i < pollUrls.MAX_ENTRIES; i++) pollUrls.remember('puk1-fill-' + i, 'https://c.example.com/' + i);
|
|
69
|
+
expect(pollUrls.lookup('puk1-a')).to.equal(null); // oldest dropped
|
|
70
|
+
expect(pollUrls.lookup('puk1-fill-' + (pollUrls.MAX_ENTRIES - 1))).to.equal('https://c.example.com/' + (pollUrls.MAX_ENTRIES - 1));
|
|
71
|
+
const later = realNow() + pollUrls.TTL_MS + 1000;
|
|
72
|
+
Date.now = () => later;
|
|
73
|
+
expect(pollUrls.lookup('puk1-fill-' + (pollUrls.MAX_ENTRIES - 1))).to.equal(null); // expired
|
|
74
|
+
} finally {
|
|
75
|
+
Date.now = realNow;
|
|
76
|
+
pollUrls.clear();
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('[PUK2] pollAccessRequest(key) and connectFromKey(key) poll the core URL the server issued', async function () {
|
|
81
|
+
const key = freshKey();
|
|
82
|
+
stubFetch(key, { status: 'ACCEPTED', username: 'alice', apiEndpoint: 'https://tok@alice.mc.test.local/', token: 'tok' });
|
|
83
|
+
const service = new pryv.Service(null, SERVICE_INFO);
|
|
84
|
+
const env = await service.startAccessRequest({ requestingAppId: 'puk', requestedPermissions: [] });
|
|
85
|
+
expect(env.poll).to.equal(corePoll(key));
|
|
86
|
+
|
|
87
|
+
await service.pollAccessRequest(key);
|
|
88
|
+
// a fresh Service, as pryv.connectFromKey(key, url) builds one
|
|
89
|
+
const connection = await new pryv.Service(null, SERVICE_INFO).connectFromKey(key);
|
|
90
|
+
expect(gets).to.deep.equal([corePoll(key), corePoll(key)]);
|
|
91
|
+
expect(connection.apiEndpoint).to.equal('https://tok@alice.mc.test.local/');
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it('[PUK3] a key this process never saw falls back to access + key', async function () {
|
|
95
|
+
const key = freshKey();
|
|
96
|
+
stubFetch(key, { status: 'NEED_SIGNIN' });
|
|
97
|
+
await new pryv.Service(null, SERVICE_INFO).pollAccessRequest(key);
|
|
98
|
+
expect(gets).to.deep.equal([SERVICE_INFO.access + key]);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it('[PUK4] a sign-in started by the button is remembered for connectFromKey', async function () {
|
|
102
|
+
const key = freshKey();
|
|
103
|
+
const { fetchPost, fetchGet } = utils;
|
|
104
|
+
utils.fetchPost = async () => ({ response: { ok: true }, body: { status: 'NEED_SIGNIN', key, poll: corePoll(key), poll_rate_ms: 1, authUrl: 'https://ui.mc.test.local/access?key=' + key } });
|
|
105
|
+
utils.fetchGet = async () => ({ response: { status: 200 }, body: { status: 'REFUSED' } });
|
|
106
|
+
const service = { infoSync: () => SERVICE_INFO, assets: async () => ({}) };
|
|
107
|
+
const auth = new AuthController({ authRequest: { requestingAppId: 'puk4', requestedPermissions: [] } }, service);
|
|
108
|
+
auth.serviceInfo = SERVICE_INFO;
|
|
109
|
+
try {
|
|
110
|
+
await auth.startAuthRequest();
|
|
111
|
+
} finally { utils.fetchPost = fetchPost; utils.fetchGet = fetchGet; }
|
|
112
|
+
expect(pollUrls.lookup(key)).to.equal(corePoll(key));
|
|
113
|
+
});
|
|
114
|
+
});
|