@qikdev/sdk 0.1.169 → 0.1.180
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/.gitlab-ci.yml +24 -0
- package/dist/main.js +1 -1
- package/jsdoc.conf +10 -0
- package/package.json +5 -24
- package/public/docs/access.html +3826 -0
- package/public/docs/api.html +1356 -0
- package/public/docs/auth.html +2180 -0
- package/public/docs/cache.html +351 -0
- package/public/docs/content.html +2560 -0
- package/public/docs/files.html +779 -0
- package/public/docs/filter.html +1355 -0
- package/public/docs/fonts/Montserrat/Montserrat-Bold.eot +0 -0
- package/public/docs/fonts/Montserrat/Montserrat-Bold.ttf +0 -0
- package/public/docs/fonts/Montserrat/Montserrat-Bold.woff +0 -0
- package/public/docs/fonts/Montserrat/Montserrat-Bold.woff2 +0 -0
- package/public/docs/fonts/Montserrat/Montserrat-Regular.eot +0 -0
- package/public/docs/fonts/Montserrat/Montserrat-Regular.ttf +0 -0
- package/public/docs/fonts/Montserrat/Montserrat-Regular.woff +0 -0
- package/public/docs/fonts/Montserrat/Montserrat-Regular.woff2 +0 -0
- package/public/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.eot +0 -0
- package/public/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.svg +978 -0
- package/public/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.ttf +0 -0
- package/public/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.woff +0 -0
- package/public/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.woff2 +0 -0
- package/public/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.eot +0 -0
- package/public/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.svg +1049 -0
- package/public/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.ttf +0 -0
- package/public/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.woff +0 -0
- package/public/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.woff2 +0 -0
- package/public/docs/index.html +96 -0
- package/public/docs/qik.access.js.html +790 -0
- package/public/docs/qik.api.js.html +511 -0
- package/public/docs/qik.auth.js.html +1006 -0
- package/public/docs/qik.cache.js.html +131 -0
- package/public/docs/qik.content.js.html +918 -0
- package/public/docs/qik.core.js.html +301 -0
- package/public/docs/qik.files.js.html +219 -0
- package/public/docs/qik.filter.js.html +611 -0
- package/public/docs/qik.socket.js.html +416 -0
- package/public/docs/qik.system.js.html +117 -0
- package/public/docs/qik.utils.js.html +1467 -0
- package/public/docs/scripts/collapse.js +20 -0
- package/public/docs/scripts/linenumber.js +25 -0
- package/public/docs/scripts/nav.js +12 -0
- package/public/docs/scripts/polyfill.js +4 -0
- package/public/docs/scripts/prettify/Apache-License-2.0.txt +202 -0
- package/public/docs/scripts/prettify/lang-css.js +2 -0
- package/public/docs/scripts/prettify/prettify.js +28 -0
- package/public/docs/scripts/search.js +83 -0
- package/public/docs/socket.html +810 -0
- package/public/docs/styles/jsdoc.css +765 -0
- package/public/docs/styles/prettify.css +79 -0
- package/public/docs/system.html +197 -0
- package/src/api/qik.access.js +790 -0
- package/src/api/qik.api.js +431 -0
- package/src/api/qik.auth.js +938 -0
- package/src/api/qik.cache.js +63 -0
- package/src/api/qik.content.js +858 -0
- package/src/api/qik.core.js +246 -0
- package/src/api/qik.files.js +150 -0
- package/src/api/qik.filter.js +543 -0
- package/src/api/qik.geo.js +895 -0
- package/src/api/qik.socket.js +348 -0
- package/src/api/qik.system.js +49 -0
- package/src/api/qik.utils.js +1410 -0
- package/src/index.js +20 -0
- package/src/version.js +1 -0
- package/test/index.spec.js +41 -0
- package/test/mocha.opts +0 -0
- package/dist/sdk.js +0 -2
- package/dist/sdk.js.LICENSE.txt +0 -8
|
@@ -0,0 +1,938 @@
|
|
|
1
|
+
import axios from 'axios';
|
|
2
|
+
import { EventDispatcher } from './qik.utils.js';
|
|
3
|
+
|
|
4
|
+
///////////////////////////////////////////////////
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Creates a new instance of QikAuth a module of the SDK
|
|
8
|
+
* that contains all helper functions to do with authentication and user session management
|
|
9
|
+
* @alias auth
|
|
10
|
+
* @constructor
|
|
11
|
+
* @hideconstructor
|
|
12
|
+
* @param {QikAPI} qik A reference to the parent instance of the QikCore module. This module is usually created by a QikCore instance that passes itself in as the first argument.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
var QikAuth = function(qik) {
|
|
16
|
+
|
|
17
|
+
if (!qik.api) {
|
|
18
|
+
throw new Error(`Please ensure that QikAPI exists before QikAuth`);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
//Keep track of any refresh requests
|
|
22
|
+
var inflightRefreshRequest;
|
|
23
|
+
|
|
24
|
+
///////////////////////////////////////////////////
|
|
25
|
+
|
|
26
|
+
var sessionStorage = {};
|
|
27
|
+
var store = sessionStorage;
|
|
28
|
+
const tokenBufferSeconds = 10;
|
|
29
|
+
|
|
30
|
+
///////////////////////////////////////////////////
|
|
31
|
+
|
|
32
|
+
const service = {
|
|
33
|
+
debug: false,
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
Object.defineProperty(service, 'store', {
|
|
37
|
+
value: store,
|
|
38
|
+
writable: false,
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
//Create a new dispatcher
|
|
42
|
+
const dispatcher = new EventDispatcher();
|
|
43
|
+
dispatcher.bootstrap(service);
|
|
44
|
+
|
|
45
|
+
///////////////////////////////////////////////////
|
|
46
|
+
|
|
47
|
+
function dispatch(parameters) {
|
|
48
|
+
|
|
49
|
+
//Get the current user
|
|
50
|
+
var user = store.user;
|
|
51
|
+
|
|
52
|
+
//Dispatch the change to the listeners
|
|
53
|
+
if (service.onChange) {
|
|
54
|
+
service.onChange(user);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
//Dispatch the change event
|
|
58
|
+
dispatcher.dispatch('change', user, parameters);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
///////////////////////////////////////////////////
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* @alias auth.set
|
|
66
|
+
* @description Manually set current user session
|
|
67
|
+
* @param {Object} user The user session object to set as the current user session
|
|
68
|
+
* @param {Object} parameters Additional parameters to dispatch
|
|
69
|
+
* @param {Boolean} stopDispatch Whether to supress dispatching a 'change' event.
|
|
70
|
+
* @example
|
|
71
|
+
*
|
|
72
|
+
* const userSession = {_id:'61eca4746971e75c1fc670cf', firstName:'Daffy', lastName:'Duck' ...};
|
|
73
|
+
* sdk.auth.set(userSession);
|
|
74
|
+
*/
|
|
75
|
+
|
|
76
|
+
service.set = function(user, parameters, stopDispatch) {
|
|
77
|
+
|
|
78
|
+
if (JSON.stringify(store.user) != JSON.stringify(user)) {
|
|
79
|
+
store.user = user;
|
|
80
|
+
if (!stopDispatch) {
|
|
81
|
+
return dispatch(parameters)
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
///////////////////////////////////////////////////
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* @alias auth.logout
|
|
92
|
+
* @description Clear the current user session from memory and erase all caches
|
|
93
|
+
* @example
|
|
94
|
+
* sdk.auth.logout();
|
|
95
|
+
*/
|
|
96
|
+
|
|
97
|
+
service.logout = function() {
|
|
98
|
+
delete store.user;
|
|
99
|
+
qik.cache.reset();
|
|
100
|
+
return dispatch()
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
///////////////////////////////////////////////////
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* @alias auth.changeOrganisation
|
|
107
|
+
* @description Manually set current user session
|
|
108
|
+
* @param {(String|Object)} organisation The id of the organisation to switch into
|
|
109
|
+
* @param {Object} options Additional options
|
|
110
|
+
* @param {Boolean} options.disableAutoAuthentication By default when switching organisation, the current user session
|
|
111
|
+
* will be updated to reflect a session in the new organisation, you can use this option to disable that behavior and instead return the
|
|
112
|
+
* user session without dispatching any events
|
|
113
|
+
* @example
|
|
114
|
+
*
|
|
115
|
+
* sdk.auth.changeOrganisation('61eca4746971e75c1fc670cf');
|
|
116
|
+
* // Current user session will be automatically updated
|
|
117
|
+
*
|
|
118
|
+
* const newSession = await sdk.auth.changeOrganisation('61eca4746971e75c1fc670cf', {disableAutoAuthentication:true});
|
|
119
|
+
* // Current user session will not be updated
|
|
120
|
+
* sdk.auth.set(newSession);
|
|
121
|
+
*/
|
|
122
|
+
service.changeOrganisation = function(organisationID, options) {
|
|
123
|
+
|
|
124
|
+
//Ensure we just have the ID
|
|
125
|
+
organisationID = qik.utils.id(organisationID);
|
|
126
|
+
|
|
127
|
+
//////////////////////////
|
|
128
|
+
|
|
129
|
+
if (!options) {
|
|
130
|
+
options = {};
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
//////////////////////////
|
|
134
|
+
|
|
135
|
+
//Change the users current tokens straight away
|
|
136
|
+
var autoAuthenticate = true;
|
|
137
|
+
|
|
138
|
+
if (options.disableAutoAuthentication) {
|
|
139
|
+
autoAuthenticate = false;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
//////////////////////////
|
|
143
|
+
|
|
144
|
+
return new Promise(function(resolve, reject) {
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
qik.api.post(`/user/switch/${organisationID}`)
|
|
148
|
+
.then(function(response) {
|
|
149
|
+
|
|
150
|
+
if (autoAuthenticate) {
|
|
151
|
+
qik.cache.reset();
|
|
152
|
+
service.set(response.data);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
resolve(response.data);
|
|
156
|
+
})
|
|
157
|
+
.catch(reject)
|
|
158
|
+
|
|
159
|
+
})
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
///////////////////////////////////////////////////
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* @alias auth.impersonate
|
|
166
|
+
* @description Impersonate another user within your organisation
|
|
167
|
+
* @param {(String|Object)} persona The id of the user persona you want to impersonate
|
|
168
|
+
* @param {Object} options Additional options
|
|
169
|
+
* @param {Boolean} options.disableAutoAuthentication By default when impersonating a user, the current user session
|
|
170
|
+
* will be updated automatically to reflect the new session, you can use this option to disable that behavior and instead return the
|
|
171
|
+
* new impersonation user session without dispatching any events
|
|
172
|
+
* @example
|
|
173
|
+
*
|
|
174
|
+
* sdk.auth.impersonate('61eca4746971e75c1fc670cf');
|
|
175
|
+
* // Current user session will be automatically updated
|
|
176
|
+
*
|
|
177
|
+
* const newSession = await sdk.auth.impersonate('61eca4746971e75c1fc670cf', {disableAutoAuthentication:true});
|
|
178
|
+
* // Current user session will not be updated
|
|
179
|
+
* sdk.auth.set(newSession);
|
|
180
|
+
*/
|
|
181
|
+
service.impersonate = function(personaID, options) {
|
|
182
|
+
|
|
183
|
+
//Ensure we just have the ID
|
|
184
|
+
personaID = qik.utils.id(personaID);
|
|
185
|
+
|
|
186
|
+
//////////////////////////
|
|
187
|
+
|
|
188
|
+
if (!options) {
|
|
189
|
+
options = {};
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
//////////////////////////
|
|
193
|
+
|
|
194
|
+
//Change the users current tokens straight away
|
|
195
|
+
var autoAuthenticate = true;
|
|
196
|
+
|
|
197
|
+
if (options.disableAutoAuthentication) {
|
|
198
|
+
autoAuthenticate = false;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
//////////////////////////
|
|
202
|
+
|
|
203
|
+
var promise = qik.api.post(`/user/impersonate/${personaID}`)
|
|
204
|
+
|
|
205
|
+
promise.then(function(res) {
|
|
206
|
+
|
|
207
|
+
if (autoAuthenticate) {
|
|
208
|
+
qik.cache.reset();
|
|
209
|
+
service.set(res.data);
|
|
210
|
+
}
|
|
211
|
+
}, function(err) {});
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
return promise;
|
|
215
|
+
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
///////////////////////////////////////////////////
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* @alias auth.login
|
|
222
|
+
* @description Login and authenticate as a user
|
|
223
|
+
* @param {Object} credentials The credentials used to login
|
|
224
|
+
* @param {String} credentials.email The email address to login to
|
|
225
|
+
* @param {String} credentials.password The password to login with
|
|
226
|
+
* @param {String} credentials.mfa The MFA (Multi Factor Authentication) code
|
|
227
|
+
* @param {Object} options Additional options
|
|
228
|
+
* @param {Boolean} options.disableAutoAuthentication By default when logging in the current user session
|
|
229
|
+
* will be updated automatically to reflect the new session, you can use this option to disable that behavior and instead return the
|
|
230
|
+
* session that was logged in to without dispatching any events
|
|
231
|
+
* @example
|
|
232
|
+
*
|
|
233
|
+
* const credentials = {
|
|
234
|
+
* email:'me@email.com',
|
|
235
|
+
* password:'******',
|
|
236
|
+
* mfa:'1234',
|
|
237
|
+
* }
|
|
238
|
+
*
|
|
239
|
+
* sdk.auth.login(credentials);
|
|
240
|
+
* // Current user session will be automatically updated
|
|
241
|
+
*
|
|
242
|
+
* const newSession = await sdk.auth.login(credentials, {disableAutoAuthentication:true});
|
|
243
|
+
* // Current user session will not be updated
|
|
244
|
+
* sdk.auth.set(newSession);
|
|
245
|
+
*/
|
|
246
|
+
service.login = async function(credentials, options) {
|
|
247
|
+
|
|
248
|
+
if (!options) {
|
|
249
|
+
options = {};
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
//////////////////////////
|
|
253
|
+
|
|
254
|
+
//Change the users current tokens straight away
|
|
255
|
+
var autoAuthenticate = true;
|
|
256
|
+
|
|
257
|
+
if (options.disableAutoAuthentication) {
|
|
258
|
+
autoAuthenticate = false;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
//////////////////////////////////////
|
|
262
|
+
|
|
263
|
+
var promise = new Promise(loginCheck)
|
|
264
|
+
|
|
265
|
+
function loginCheck(resolve, reject) {
|
|
266
|
+
|
|
267
|
+
if (!credentials) {
|
|
268
|
+
return reject({
|
|
269
|
+
message: 'Missing credentials!',
|
|
270
|
+
})
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
if (!credentials.email || !credentials.email.length) {
|
|
274
|
+
return reject({
|
|
275
|
+
message: 'Username was not provided',
|
|
276
|
+
})
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
if (!credentials.password || !credentials.password.length) {
|
|
280
|
+
return reject({
|
|
281
|
+
message: 'Password was not provided',
|
|
282
|
+
})
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/////////////////////////////////////////////
|
|
286
|
+
|
|
287
|
+
var postOptions = {
|
|
288
|
+
bypassInterceptor: true
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/////////////////////////////////////////////
|
|
292
|
+
|
|
293
|
+
var url = `${qik.apiURL}/user/login`;
|
|
294
|
+
|
|
295
|
+
/////////////////////////////////////////////
|
|
296
|
+
|
|
297
|
+
//If we have a specified url
|
|
298
|
+
if (options.url) {
|
|
299
|
+
url = options.url;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/////////////////////////////////////////////
|
|
303
|
+
|
|
304
|
+
qik.api.post(url, credentials, postOptions).then(function(res) {
|
|
305
|
+
|
|
306
|
+
if (autoAuthenticate) {
|
|
307
|
+
service.set(res.data);
|
|
308
|
+
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
resolve(res);
|
|
312
|
+
}, reject);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
//////////////////////////////////////
|
|
316
|
+
|
|
317
|
+
return promise;
|
|
318
|
+
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
///////////////////////////////////////////////////
|
|
322
|
+
|
|
323
|
+
service.signup = async function(credentials, options) {
|
|
324
|
+
|
|
325
|
+
if (!options) {
|
|
326
|
+
options = {};
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
//////////////////////////
|
|
331
|
+
|
|
332
|
+
//Change the users current tokens straight away
|
|
333
|
+
var autoAuthenticate = true;
|
|
334
|
+
|
|
335
|
+
if (options.disableAutoAuthentication) {
|
|
336
|
+
autoAuthenticate = false;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
//////////////////////////////////////
|
|
340
|
+
|
|
341
|
+
var promise = new Promise(signupCheck)
|
|
342
|
+
|
|
343
|
+
function signupCheck(resolve, reject) {
|
|
344
|
+
|
|
345
|
+
if (!credentials) {
|
|
346
|
+
return reject({
|
|
347
|
+
message: 'No details provided',
|
|
348
|
+
})
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
if (!credentials.firstName || !credentials.firstName.length) {
|
|
352
|
+
return reject({
|
|
353
|
+
message: 'First Name was not provided',
|
|
354
|
+
})
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
if (!credentials.lastName || !credentials.lastName.length) {
|
|
358
|
+
return reject({
|
|
359
|
+
message: 'Last Name was not provided',
|
|
360
|
+
})
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
if (!credentials.email || !credentials.email.length) {
|
|
364
|
+
return reject({
|
|
365
|
+
message: 'Email/Username was not provided',
|
|
366
|
+
})
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
|
|
370
|
+
if (!credentials.password || !credentials.password.length) {
|
|
371
|
+
return reject({
|
|
372
|
+
message: 'Password was not provided',
|
|
373
|
+
})
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
if (!credentials.confirmPassword || !credentials.confirmPassword.length) {
|
|
377
|
+
return reject({
|
|
378
|
+
message: 'Confirm Password was not provided',
|
|
379
|
+
})
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
if (credentials.confirmPassword != credentials.password) {
|
|
383
|
+
return reject({
|
|
384
|
+
message: 'Your passwords do not match',
|
|
385
|
+
})
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
/////////////////////////////////////////////
|
|
389
|
+
|
|
390
|
+
var postOptions = {
|
|
391
|
+
bypassInterceptor: true
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
/////////////////////////////////////////////
|
|
395
|
+
|
|
396
|
+
var url = `${qik.apiURL}/user/signup`;
|
|
397
|
+
|
|
398
|
+
/////////////////////////////////////////////
|
|
399
|
+
|
|
400
|
+
//If we are authenticating as an application
|
|
401
|
+
if (options.application) {
|
|
402
|
+
|
|
403
|
+
//The url is relative to the domain
|
|
404
|
+
url = `${qik.domain || ''}/qik/application/signup`;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
//If we have a specified url
|
|
408
|
+
if (options.url) {
|
|
409
|
+
url = options.url;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
/////////////////////////////////////////////
|
|
413
|
+
|
|
414
|
+
qik.api.post(url, credentials, postOptions).then(function(res) {
|
|
415
|
+
|
|
416
|
+
if (autoAuthenticate) {
|
|
417
|
+
service.set(res.data);
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
resolve(res);
|
|
421
|
+
}, reject);
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
//////////////////////////////////////
|
|
425
|
+
|
|
426
|
+
return promise;
|
|
427
|
+
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
|
|
431
|
+
///////////////////////////////////////////////////
|
|
432
|
+
|
|
433
|
+
/**
|
|
434
|
+
* @alias auth.retrieveUserFromResetToken
|
|
435
|
+
* @description Retrieve user session through use of a valid reset token,
|
|
436
|
+
* Reset tokens are short lived tokens that can be generated when a user has forgotten their password or their
|
|
437
|
+
* password has been reset by an administrator
|
|
438
|
+
* @param {String} resetToken The token to use to authenticate
|
|
439
|
+
* @param {Object} options Additional options for the request
|
|
440
|
+
* @example
|
|
441
|
+
*
|
|
442
|
+
* const resetToken = 'XXX-324623-$$...';
|
|
443
|
+
*
|
|
444
|
+
* // Retrieve the user session by providing a reset token
|
|
445
|
+
* const user = await sdk.auth.retrieveUserFromResetToken(resetToken);
|
|
446
|
+
*/
|
|
447
|
+
service.retrieveUserFromResetToken = async function(resetToken, options) {
|
|
448
|
+
|
|
449
|
+
if (!options) {
|
|
450
|
+
options = {};
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
var postOptions = {
|
|
454
|
+
bypassInterceptor: true
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
return new Promise(function(resolve, reject) {
|
|
458
|
+
qik.api.get(options.url || `${qik.apiURL}/user/reset/${resetToken}`, postOptions).then(function(res) {
|
|
459
|
+
return resolve(res.data);
|
|
460
|
+
}, reject);
|
|
461
|
+
});
|
|
462
|
+
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
///////////////////////////////////////////////////
|
|
466
|
+
|
|
467
|
+
/**
|
|
468
|
+
* @alias auth.updateUserWithToken
|
|
469
|
+
* @description Update a user's credentials through use of a reset token
|
|
470
|
+
* @param {String} resetToken The token to use to authenticate
|
|
471
|
+
* @param {Object} body Updates to be made to the user
|
|
472
|
+
* @param {Object} options Additional options for the request
|
|
473
|
+
* @param {Boolean} options.disableAutoAuthentication By default the current user session
|
|
474
|
+
* will be updated automatically to reflect the new updated session, you can use this option to disable that behavior
|
|
475
|
+
* and instead return the result without dispatching any events
|
|
476
|
+
* @example
|
|
477
|
+
*
|
|
478
|
+
* const resetToken = 'XXX-324623-$$...';
|
|
479
|
+
*
|
|
480
|
+
* // Retrieve the user session by providing a reset token
|
|
481
|
+
* const user = await sdk.auth.retrieveUserFromResetToken(resetToken);
|
|
482
|
+
*/
|
|
483
|
+
service.updateUserWithToken = async function(resetToken, body, options) {
|
|
484
|
+
|
|
485
|
+
if (!options) {
|
|
486
|
+
options = {};
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
//////////////////////////
|
|
490
|
+
|
|
491
|
+
//Change the users current tokens straight away
|
|
492
|
+
var autoAuthenticate = true;
|
|
493
|
+
|
|
494
|
+
if (options.disableAutoAuthentication) {
|
|
495
|
+
autoAuthenticate = false;
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
//////////////////////////////////////
|
|
499
|
+
|
|
500
|
+
return new Promise(function(resolve, reject) {
|
|
501
|
+
|
|
502
|
+
var postOptions = {
|
|
503
|
+
bypassInterceptor: true
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
qik.api.post(options.url || `${qik.apiURL}/user/reset/${resetToken}`, body, postOptions)
|
|
507
|
+
.then(function(res) {
|
|
508
|
+
|
|
509
|
+
//If we should automatically authenticate
|
|
510
|
+
//once the request is successful
|
|
511
|
+
//Then clear caches and update the session
|
|
512
|
+
if (autoAuthenticate) {
|
|
513
|
+
qik.cache.reset();
|
|
514
|
+
service.set(res.data);
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
return resolve(res.data);
|
|
518
|
+
}, reject);
|
|
519
|
+
});
|
|
520
|
+
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
/**
|
|
524
|
+
* @alias auth.sendResetPasswordRequest
|
|
525
|
+
* @description This function allows a reset token to be generated and emailed to the requesting user
|
|
526
|
+
* allowing them to modify their user details
|
|
527
|
+
* @param {Object} body Details for the reset request
|
|
528
|
+
* @param {String} body.email The email of the user to generate a token for
|
|
529
|
+
* @example
|
|
530
|
+
*
|
|
531
|
+
* const resetToken = 'XXX-324623-$$...';
|
|
532
|
+
*
|
|
533
|
+
* // Retrieve the user session by providing a reset token
|
|
534
|
+
* const user = await sdk.auth.retrieveUserFromResetToken(resetToken);
|
|
535
|
+
*/
|
|
536
|
+
service.sendResetPasswordRequest = function(details, options) {
|
|
537
|
+
|
|
538
|
+
if (!options) {
|
|
539
|
+
options = {};
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
if (!details) {
|
|
543
|
+
return Promise.reject({
|
|
544
|
+
message: 'No details were provided for password reset request',
|
|
545
|
+
})
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
if (!details.email || !details.email.length) {
|
|
549
|
+
return Promise.reject({
|
|
550
|
+
message: 'Email is required but was not provided',
|
|
551
|
+
})
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
return new Promise(function(resolve, reject) {
|
|
555
|
+
|
|
556
|
+
var postOptions = {
|
|
557
|
+
bypassInterceptor: true
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
qik.api.post(options.url || `${qik.apiURL}/user/forgot`, details, postOptions).then(resolve, reject);
|
|
561
|
+
})
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
|
|
565
|
+
///////////////////////////////////////////////////
|
|
566
|
+
|
|
567
|
+
/**
|
|
568
|
+
* @alias auth.ensureValidToken
|
|
569
|
+
* @description This function forces a check to ensure that the current access token has not expired.
|
|
570
|
+
* If the token has expired, the user session will be refreshed with a new token.
|
|
571
|
+
* @param {Boolean} forceRefresh Whether to force the current token to be refreshed, even if it has not yet expired.
|
|
572
|
+
* @example
|
|
573
|
+
*
|
|
574
|
+
* // Check to ensure that the current access token is valid
|
|
575
|
+
* sdk.auth.ensureValidToken();
|
|
576
|
+
*
|
|
577
|
+
* // Force the token to be refreshed, even if the current token in use has not yet expired.
|
|
578
|
+
* sdk.auth.ensureValidToken(true);
|
|
579
|
+
*/
|
|
580
|
+
service.ensureValidToken = async function(forceRefresh) {
|
|
581
|
+
|
|
582
|
+
var currentUser = service.getCurrentUser();
|
|
583
|
+
if (!currentUser) {
|
|
584
|
+
|
|
585
|
+
return Promise.reject('No user');
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
var { token } = currentUser;
|
|
589
|
+
if (!token) {
|
|
590
|
+
|
|
591
|
+
return Promise.reject('No token');
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
/////////////////////////////////////////////////////
|
|
595
|
+
|
|
596
|
+
//Check our date
|
|
597
|
+
var now = new Date();
|
|
598
|
+
|
|
599
|
+
//Give us a bit of buffer so that the backend doesn't beat us to
|
|
600
|
+
//retiring the token
|
|
601
|
+
now.setSeconds(now.getSeconds() + tokenBufferSeconds);
|
|
602
|
+
|
|
603
|
+
/////////////////////////////////////////////////////
|
|
604
|
+
|
|
605
|
+
var expires = new Date(token.expires);
|
|
606
|
+
|
|
607
|
+
if (forceRefresh) {
|
|
608
|
+
console.log('force refresh valid token', token.refreshToken)
|
|
609
|
+
return await service.refreshAccessToken(token.refreshToken);
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
//If the token is still fresh
|
|
613
|
+
if (now < expires) {
|
|
614
|
+
|
|
615
|
+
return token;
|
|
616
|
+
} else {
|
|
617
|
+
|
|
618
|
+
return await service.refreshAccessToken(token.refreshToken);
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
///////////////////////////////////////////////////
|
|
625
|
+
|
|
626
|
+
const refreshContext = {};
|
|
627
|
+
|
|
628
|
+
service.refreshAccessToken = async function(refreshToken) {
|
|
629
|
+
|
|
630
|
+
// /////////////////////////////////////////////
|
|
631
|
+
|
|
632
|
+
// if (appContext) {
|
|
633
|
+
|
|
634
|
+
// } else {
|
|
635
|
+
|
|
636
|
+
// }
|
|
637
|
+
|
|
638
|
+
// /////////////////////////////////////////////
|
|
639
|
+
|
|
640
|
+
//If there is already a request in progress
|
|
641
|
+
if (refreshContext.inflightRefreshRequest) {
|
|
642
|
+
|
|
643
|
+
return refreshContext.inflightRefreshRequest;
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
/////////////////////////////////////////////////////
|
|
647
|
+
|
|
648
|
+
//Create an refresh request
|
|
649
|
+
|
|
650
|
+
refreshContext.inflightRefreshRequest = new Promise(function(resolve, reject) {
|
|
651
|
+
|
|
652
|
+
//Bypass the interceptor on all token refresh calls
|
|
653
|
+
//Because we don't need to add the access token etc onto it
|
|
654
|
+
|
|
655
|
+
qik.api.post(`/user/refresh`, {
|
|
656
|
+
refreshToken: refreshToken
|
|
657
|
+
}, {
|
|
658
|
+
bypassInterceptor: true,
|
|
659
|
+
withoutToken: true,
|
|
660
|
+
})
|
|
661
|
+
.then(function tokenRefreshComplete(res) {
|
|
662
|
+
|
|
663
|
+
//Update the user with any changes
|
|
664
|
+
//returned back from the refresh request
|
|
665
|
+
if (!res) {
|
|
666
|
+
|
|
667
|
+
refreshContext.inflightRefreshRequest = null;
|
|
668
|
+
return reject();
|
|
669
|
+
|
|
670
|
+
} else {
|
|
671
|
+
//Update with our new session
|
|
672
|
+
service.set(res.data);
|
|
673
|
+
|
|
674
|
+
|
|
675
|
+
dispatch();
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
//Resolve with the new token
|
|
679
|
+
resolve(res.data.token);
|
|
680
|
+
|
|
681
|
+
//Remove the inflight request
|
|
682
|
+
setTimeout(function() {
|
|
683
|
+
refreshContext.inflightRefreshRequest = null;
|
|
684
|
+
})
|
|
685
|
+
|
|
686
|
+
|
|
687
|
+
})
|
|
688
|
+
.catch(function(err) {
|
|
689
|
+
|
|
690
|
+
|
|
691
|
+
//TODO Check if invalid_refresh_token
|
|
692
|
+
|
|
693
|
+
|
|
694
|
+
|
|
695
|
+
setTimeout(function() {
|
|
696
|
+
refreshContext.inflightRefreshRequest = null;
|
|
697
|
+
});
|
|
698
|
+
reject(err);
|
|
699
|
+
|
|
700
|
+
|
|
701
|
+
|
|
702
|
+
});
|
|
703
|
+
});
|
|
704
|
+
|
|
705
|
+
//Return the refresh request
|
|
706
|
+
return refreshContext.inflightRefreshRequest;
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
///////////////////////////////////////////////////
|
|
710
|
+
|
|
711
|
+
var retryCount = 0;
|
|
712
|
+
|
|
713
|
+
/**
|
|
714
|
+
* @alias auth.sync
|
|
715
|
+
* @description A useful function to sync the current user session with the server.
|
|
716
|
+
* @example
|
|
717
|
+
*
|
|
718
|
+
* // Makes request to the API and updates the current user session to match the response
|
|
719
|
+
* sdk.auth.sync();
|
|
720
|
+
*/
|
|
721
|
+
service.sync = function() {
|
|
722
|
+
|
|
723
|
+
return qik.api.get('/user')
|
|
724
|
+
.then(function(res) {
|
|
725
|
+
|
|
726
|
+
|
|
727
|
+
if (res.data) {
|
|
728
|
+
if (store.user) {
|
|
729
|
+
Object.assign(store.user.session, res.data);
|
|
730
|
+
}
|
|
731
|
+
} else {
|
|
732
|
+
service.set(null);
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
|
|
736
|
+
retryCount = 0;
|
|
737
|
+
|
|
738
|
+
dispatch();
|
|
739
|
+
})
|
|
740
|
+
.catch(function(err) {
|
|
741
|
+
|
|
742
|
+
// if (retryCount > 2) {
|
|
743
|
+
service.set(null);
|
|
744
|
+
retryCount = 0;
|
|
745
|
+
dispatch();
|
|
746
|
+
// } else {
|
|
747
|
+
|
|
748
|
+
// retryCount++;
|
|
749
|
+
// service.sync();
|
|
750
|
+
// }
|
|
751
|
+
|
|
752
|
+
|
|
753
|
+
});
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
/////////////////////////////////////////////////////
|
|
757
|
+
|
|
758
|
+
/**
|
|
759
|
+
* @alias auth.getCurrentUser
|
|
760
|
+
* @description Retrieves the current user session
|
|
761
|
+
* @example
|
|
762
|
+
*
|
|
763
|
+
* const me = sdk.auth.getCurrentUser();
|
|
764
|
+
*/
|
|
765
|
+
service.getCurrentUser = function() {
|
|
766
|
+
return store.user;
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
|
|
770
|
+
/**
|
|
771
|
+
* @alias auth.getCurrentToken
|
|
772
|
+
* @description Retrieves the current access token. If the user is authenticated
|
|
773
|
+
* the response will be the current user's access token, otherwise will fall back to the
|
|
774
|
+
* applications token.
|
|
775
|
+
* @example
|
|
776
|
+
* const currentAccessToken = sdk.auth.getCurrentToken();
|
|
777
|
+
*/
|
|
778
|
+
service.getCurrentToken = function() {
|
|
779
|
+
|
|
780
|
+
|
|
781
|
+
var user = service.getCurrentUser();
|
|
782
|
+
|
|
783
|
+
//User is not logged in
|
|
784
|
+
if (!user) {
|
|
785
|
+
|
|
786
|
+
//But there is an application token
|
|
787
|
+
if (qik.applicationToken) {
|
|
788
|
+
//use that instead
|
|
789
|
+
return qik.applicationToken;
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
//No token
|
|
793
|
+
return;
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
var { token } = user;
|
|
797
|
+
if (!token) {
|
|
798
|
+
return;
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
return token.accessToken;
|
|
802
|
+
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
/////////////////////////////////////////////////////
|
|
806
|
+
|
|
807
|
+
qik.api.interceptors.request.use(async function(config) {
|
|
808
|
+
|
|
809
|
+
//If we want to bypass the interceptor
|
|
810
|
+
//then just return the request
|
|
811
|
+
if (config.bypassInterceptor) {
|
|
812
|
+
return config;
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
//////////////////////////////
|
|
816
|
+
|
|
817
|
+
//Get the original request
|
|
818
|
+
var originalRequest = config;
|
|
819
|
+
|
|
820
|
+
//////////////////////////////
|
|
821
|
+
|
|
822
|
+
var userDetails = await service.getCurrentUser();
|
|
823
|
+
var accessToken;
|
|
824
|
+
var refreshToken;
|
|
825
|
+
var expiryDate;
|
|
826
|
+
|
|
827
|
+
if (userDetails) {
|
|
828
|
+
var { token } = userDetails;
|
|
829
|
+
if (token) {
|
|
830
|
+
accessToken = token.accessToken;
|
|
831
|
+
refreshToken = token.refreshToken;
|
|
832
|
+
expiryDate = token.expires;
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
//////////////////////////////
|
|
837
|
+
|
|
838
|
+
//If there is a user token
|
|
839
|
+
if (accessToken) {
|
|
840
|
+
//Set the token of the request as the user's access token
|
|
841
|
+
originalRequest.headers['Authorization'] = `Bearer ${accessToken}`;
|
|
842
|
+
|
|
843
|
+
|
|
844
|
+
} else {
|
|
845
|
+
//Return the original request without a token
|
|
846
|
+
|
|
847
|
+
return originalRequest;
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
/////////////////////////////////////////////////////
|
|
851
|
+
|
|
852
|
+
//If no refresh token
|
|
853
|
+
if (!refreshToken) {
|
|
854
|
+
|
|
855
|
+
|
|
856
|
+
//Continue with the original request
|
|
857
|
+
return originalRequest;
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
/////////////////////////////////////////////////////
|
|
861
|
+
|
|
862
|
+
//We have a refresh token so we need to check
|
|
863
|
+
//whether our access token is stale and needs to be refreshed
|
|
864
|
+
var now = new Date();
|
|
865
|
+
|
|
866
|
+
//Give us a bit of buffer so that the backend doesn't beat us to
|
|
867
|
+
//retiring the token
|
|
868
|
+
now.setSeconds(now.getSeconds() + tokenBufferSeconds);
|
|
869
|
+
|
|
870
|
+
/////////////////////////////////////////////////////
|
|
871
|
+
|
|
872
|
+
var expires = new Date(expiryDate);
|
|
873
|
+
|
|
874
|
+
//If the token is still fresh
|
|
875
|
+
if (now < expires) {
|
|
876
|
+
//Return the original request
|
|
877
|
+
return originalRequest;
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
/////////////////////////////////////////////////////
|
|
881
|
+
|
|
882
|
+
return new Promise(async function(resolve, reject) {
|
|
883
|
+
|
|
884
|
+
//Refresh the token
|
|
885
|
+
await service.refreshAccessToken(refreshToken)
|
|
886
|
+
.then(function(newToken) {
|
|
887
|
+
|
|
888
|
+
//Update the original request with our new token
|
|
889
|
+
originalRequest.headers['Authorization'] = `Bearer ${newToken}`;
|
|
890
|
+
//And continue onward
|
|
891
|
+
return resolve(originalRequest);
|
|
892
|
+
})
|
|
893
|
+
.catch(function(err) {
|
|
894
|
+
|
|
895
|
+
return reject(err);
|
|
896
|
+
});
|
|
897
|
+
});
|
|
898
|
+
|
|
899
|
+
|
|
900
|
+
},
|
|
901
|
+
function(error) {
|
|
902
|
+
return Promise.reject(error);
|
|
903
|
+
})
|
|
904
|
+
|
|
905
|
+
|
|
906
|
+
|
|
907
|
+
/////////////////////////////////////////////////////
|
|
908
|
+
|
|
909
|
+
qik.api.interceptors.response.use(function(response) {
|
|
910
|
+
return response;
|
|
911
|
+
}, function(err) {
|
|
912
|
+
|
|
913
|
+
//////////////////////////////
|
|
914
|
+
|
|
915
|
+
//Get the response status
|
|
916
|
+
var status = (err && err.response && err.response.status) || err.status;
|
|
917
|
+
|
|
918
|
+
|
|
919
|
+
switch (status) {
|
|
920
|
+
case 401:
|
|
921
|
+
service.logout();
|
|
922
|
+
break;
|
|
923
|
+
default:
|
|
924
|
+
//Some other error
|
|
925
|
+
break;
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
/////////////////////////////////////////////////////
|
|
929
|
+
|
|
930
|
+
return Promise.reject(err);
|
|
931
|
+
})
|
|
932
|
+
|
|
933
|
+
return service;
|
|
934
|
+
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
|
|
938
|
+
export default QikAuth;
|