@qikdev/sdk 0.1.0
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 +13 -0
- package/README.md +25 -0
- package/jsdoc.conf +10 -0
- package/package.json +46 -0
- package/src/api/qik.access.js +92 -0
- package/src/api/qik.api.js +406 -0
- package/src/api/qik.auth.js +980 -0
- package/src/api/qik.cache.js +57 -0
- package/src/api/qik.core.js +173 -0
- package/src/api/qik.utils.js +667 -0
- package/src/index.js +13 -0
- package/test/index.spec.js +36 -0
- package/test/mocha.opts +0 -0
- package/webpack.config.js +63 -0
|
@@ -0,0 +1,980 @@
|
|
|
1
|
+
import axios from 'axios';
|
|
2
|
+
import _ from 'lodash';
|
|
3
|
+
import { EventDispatcher } from './qik.utils.js';
|
|
4
|
+
|
|
5
|
+
///////////////////////////////////////////////////
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Creates a new QikAuth instance.
|
|
9
|
+
* This module provides a number of helper functions for authentication, logging in, signing up, generating and refreshing tokens
|
|
10
|
+
* @alias auth
|
|
11
|
+
* @constructor
|
|
12
|
+
* @hideconstructor
|
|
13
|
+
* @param {QikCore} 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.
|
|
14
|
+
*/
|
|
15
|
+
var QikAuth = function(qik) {
|
|
16
|
+
|
|
17
|
+
if (!qik.api) {
|
|
18
|
+
throw new Error(`Can't Instantiate QikAuth before QikAPI exists`);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
//Keep track of any refresh requests
|
|
22
|
+
var inflightRefreshRequest;
|
|
23
|
+
|
|
24
|
+
///////////////////////////////////////////////////
|
|
25
|
+
|
|
26
|
+
var defaultStore = {};
|
|
27
|
+
var store = defaultStore;
|
|
28
|
+
|
|
29
|
+
///////////////////////////////////////////////////
|
|
30
|
+
///////////////////////////////////////////////////
|
|
31
|
+
|
|
32
|
+
var service = {
|
|
33
|
+
debug: false,
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
Object.defineProperty(service, 'store', {
|
|
37
|
+
value: store,
|
|
38
|
+
writable: false,
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
//Create a new dispatcher
|
|
43
|
+
var dispatcher = new EventDispatcher();
|
|
44
|
+
dispatcher.bootstrap(service);
|
|
45
|
+
|
|
46
|
+
// console.log('New Dispatcher!', dispatcher)
|
|
47
|
+
|
|
48
|
+
///////////////////////////////////////////////////
|
|
49
|
+
///////////////////////////////////////////////////
|
|
50
|
+
|
|
51
|
+
function dispatch(parameters) {
|
|
52
|
+
|
|
53
|
+
//Get the current user
|
|
54
|
+
var user = store.user;
|
|
55
|
+
|
|
56
|
+
//Dispatch the change to the listeners
|
|
57
|
+
if (service.onChange) {
|
|
58
|
+
service.onChange(user);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
//Dispatch the change event
|
|
62
|
+
dispatcher.dispatch('change', user, parameters);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
///////////////////////////////////////////////////
|
|
66
|
+
|
|
67
|
+
function log(message) {
|
|
68
|
+
if (service.debug) {
|
|
69
|
+
console.log(message);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
///////////////////////////////////////////////////
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
*
|
|
77
|
+
* Sets the current user data, often from localStorage or after new session data
|
|
78
|
+
* has been generated from the server after signing in
|
|
79
|
+
* @alias auth.set
|
|
80
|
+
* @param {Object} user The user session object
|
|
81
|
+
* @example
|
|
82
|
+
* QikAsset.set({firstName:'Jeff', lastName:'Andrews', ...})
|
|
83
|
+
*/
|
|
84
|
+
|
|
85
|
+
service.set = function(user, parameters, ignoreEvent) {
|
|
86
|
+
|
|
87
|
+
store.user = user;
|
|
88
|
+
|
|
89
|
+
log('qik.auth > user set');
|
|
90
|
+
return dispatch(parameters)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
///////////////////////////////////////////////////
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
*
|
|
99
|
+
* Deletes the user session object, clears all Qik caches and tokens
|
|
100
|
+
* from memory
|
|
101
|
+
* @alias auth.logout
|
|
102
|
+
* @example
|
|
103
|
+
* qik.auth.logout()
|
|
104
|
+
*/
|
|
105
|
+
|
|
106
|
+
service.logout = function() {
|
|
107
|
+
//Unauthenticated
|
|
108
|
+
// delete store.token;
|
|
109
|
+
|
|
110
|
+
delete store.user;
|
|
111
|
+
qik.cache.reset();
|
|
112
|
+
// delete store.refreshToken;
|
|
113
|
+
// delete store.expires;
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
log('qik.auth > user logout');
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
if (qik.withCredentials) {
|
|
121
|
+
|
|
122
|
+
//Logout of the current application
|
|
123
|
+
window.location.href = '/qik/logout';
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
// if(window && window.localStorage) {
|
|
130
|
+
// window.localStorage.removeItem('qik.user');
|
|
131
|
+
// }
|
|
132
|
+
|
|
133
|
+
return dispatch()
|
|
134
|
+
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
///////////////////////////////////////////////////
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
*
|
|
141
|
+
* Retrieves a new session object for a Qik global user for a specified organisation
|
|
142
|
+
* This will only work if the user has a persona in that organisation
|
|
143
|
+
* @alias auth.changeOrganisation
|
|
144
|
+
* @param {String} organisationID The _id of the organisation you wish to log in to
|
|
145
|
+
* @param {Object} options
|
|
146
|
+
* @param {Object} options.disableAutoAuthentication By default this function will set the current user session
|
|
147
|
+
* to organisation you are changing in to.
|
|
148
|
+
* If you want to generate the session without affecting your current session you can set disableAutoAuthentication to true
|
|
149
|
+
* @return {Promise} Resolves to the user session object, or rejects with the responding error
|
|
150
|
+
* @example
|
|
151
|
+
* qik.auth.changeOrganisation('5be504eabf33991239599d63').then(function(userSession) {
|
|
152
|
+
* //New user session will be set automatically
|
|
153
|
+
* var newUserSession = qik.auth.getCurrentUser();
|
|
154
|
+
* })
|
|
155
|
+
* qik.auth.changeOrganisation('5be504eabf33991239599d63', {disableAutoAuthentication:true}).then(function(userSession) {
|
|
156
|
+
* //Set the session manually
|
|
157
|
+
* qik.auth.set(userSession)
|
|
158
|
+
* })
|
|
159
|
+
*/
|
|
160
|
+
|
|
161
|
+
service.changeOrganisation = function(organisationID, options) {
|
|
162
|
+
|
|
163
|
+
//Ensure we just have the ID
|
|
164
|
+
organisationID = qik.utils.getStringID(organisationID);
|
|
165
|
+
|
|
166
|
+
//////////////////////////
|
|
167
|
+
|
|
168
|
+
if (!options) {
|
|
169
|
+
options = {};
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
//////////////////////////
|
|
173
|
+
|
|
174
|
+
//Change the users current tokens straight away
|
|
175
|
+
var autoAuthenticate = true;
|
|
176
|
+
|
|
177
|
+
if (options.disableAutoAuthentication) {
|
|
178
|
+
autoAuthenticate = false;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
//////////////////////////
|
|
182
|
+
|
|
183
|
+
var promise = qik.api.post(`/user/switch/${organisationID}`)
|
|
184
|
+
|
|
185
|
+
promise.then(function(res) {
|
|
186
|
+
|
|
187
|
+
if (autoAuthenticate) {
|
|
188
|
+
qik.cache.reset();
|
|
189
|
+
service.set(res.data);
|
|
190
|
+
}
|
|
191
|
+
}, function(err) {});
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
return promise;
|
|
195
|
+
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
///////////////////////////////////////////////////
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
*
|
|
204
|
+
* Impersonates a persona and sets the current session to match the specified persona's context
|
|
205
|
+
* @alias auth.impersonate
|
|
206
|
+
* @param {String} personaID The _id of the persona you wish to impersonate
|
|
207
|
+
* @param {Object} options
|
|
208
|
+
* @return {Promise} Resolves to the user session object, or rejects with the responding error
|
|
209
|
+
* @example
|
|
210
|
+
* qik.auth.impersonate('5be504eabf33991239599d63')
|
|
211
|
+
* .then(function(userSession) {
|
|
212
|
+
* //New user session will be set automatically
|
|
213
|
+
* var newUserSession = qik.auth.getCurrentUser();
|
|
214
|
+
* })
|
|
215
|
+
*/
|
|
216
|
+
|
|
217
|
+
service.impersonate = function(personaID, options) {
|
|
218
|
+
|
|
219
|
+
//Ensure we just have the ID
|
|
220
|
+
personaID = qik.utils.getStringID(personaID);
|
|
221
|
+
|
|
222
|
+
//////////////////////////
|
|
223
|
+
|
|
224
|
+
if (!options) {
|
|
225
|
+
options = {};
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
//////////////////////////
|
|
229
|
+
|
|
230
|
+
//Change the users current tokens straight away
|
|
231
|
+
var autoAuthenticate = true;
|
|
232
|
+
|
|
233
|
+
if (options.disableAutoAuthentication) {
|
|
234
|
+
autoAuthenticate = false;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
//////////////////////////
|
|
238
|
+
|
|
239
|
+
var promise = qik.api.post(`/user/impersonate/${personaID}`)
|
|
240
|
+
|
|
241
|
+
promise.then(function(res) {
|
|
242
|
+
|
|
243
|
+
if (autoAuthenticate) {
|
|
244
|
+
qik.cache.reset();
|
|
245
|
+
service.set(res.data);
|
|
246
|
+
}
|
|
247
|
+
}, function(err) {});
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
return promise;
|
|
251
|
+
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
///////////////////////////////////////////////////
|
|
255
|
+
/**
|
|
256
|
+
* Logs the user in to Qik and returns a new user session
|
|
257
|
+
* @alias auth.login
|
|
258
|
+
* @param {Object} credentials
|
|
259
|
+
* @param {String} credentials.email The email address of the user to login as
|
|
260
|
+
* @param {String} credentials.password The password for the user
|
|
261
|
+
* @param {Object} options Extra options and configuration for the request
|
|
262
|
+
* @param {Object} options.disableAutoAuthentication Disable automatic authentication, if true, will not set the current user session
|
|
263
|
+
* @return {Promise} Returns a promise that either resolves with the logged in user session, or rejects with the responding error from the server
|
|
264
|
+
*/
|
|
265
|
+
service.login = function(credentials, options) {
|
|
266
|
+
|
|
267
|
+
if (!options) {
|
|
268
|
+
options = {};
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
//////////////////////////
|
|
272
|
+
|
|
273
|
+
//Change the users current tokens straight away
|
|
274
|
+
var autoAuthenticate = true;
|
|
275
|
+
|
|
276
|
+
if (options.disableAutoAuthentication) {
|
|
277
|
+
autoAuthenticate = false;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
//////////////////////////////////////
|
|
281
|
+
|
|
282
|
+
var promise = new Promise(loginCheck)
|
|
283
|
+
|
|
284
|
+
function loginCheck(resolve, reject) {
|
|
285
|
+
|
|
286
|
+
if (!credentials) {
|
|
287
|
+
return reject({
|
|
288
|
+
message: 'Missing credentials!',
|
|
289
|
+
})
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
if (!credentials.email || !credentials.email.length) {
|
|
293
|
+
return reject({
|
|
294
|
+
message: 'Username was not provided',
|
|
295
|
+
})
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
if (!credentials.password || !credentials.password.length) {
|
|
299
|
+
return reject({
|
|
300
|
+
message: 'Password was not provided',
|
|
301
|
+
})
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/////////////////////////////////////////////
|
|
305
|
+
|
|
306
|
+
var postOptions = {
|
|
307
|
+
bypassInterceptor: true
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/////////////////////////////////////////////
|
|
311
|
+
|
|
312
|
+
var url = `${qik.apiURL}/user/login`;
|
|
313
|
+
|
|
314
|
+
/////////////////////////////////////////////
|
|
315
|
+
|
|
316
|
+
//If we have a specified url
|
|
317
|
+
if (options.url) {
|
|
318
|
+
url = options.url;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/////////////////////////////////////////////
|
|
322
|
+
|
|
323
|
+
qik.api.post(url, credentials, postOptions).then(function(res) {
|
|
324
|
+
|
|
325
|
+
if (autoAuthenticate) {
|
|
326
|
+
store.user = res.data;
|
|
327
|
+
// console.log('Persist user', store.user)
|
|
328
|
+
dispatch();
|
|
329
|
+
// if (service.onChange) {
|
|
330
|
+
// service.onChange(store.user);
|
|
331
|
+
// }
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
resolve(res);
|
|
335
|
+
}, reject);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
//////////////////////////////////////
|
|
339
|
+
|
|
340
|
+
return promise;
|
|
341
|
+
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
///////////////////////////////////////////////////
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* Signs up a new user to the current application, this will create a new managed user persona
|
|
348
|
+
* and automatically log in as that persona in the current application context. This function will
|
|
349
|
+
* only work when called in context of an application with the 'Application Token' authentication style.
|
|
350
|
+
* It will create a new user persona in the organisation of the application and return a session with all of the application's
|
|
351
|
+
* permissions and application's logged in user permissions
|
|
352
|
+
* @alias auth.signup
|
|
353
|
+
* @param {Object} credentials
|
|
354
|
+
* @param {String} credentials.firstName The first name for the new user persona
|
|
355
|
+
* @param {String} credentials.lastName The last name for the new user persona
|
|
356
|
+
* @param {String} credentials.email The email address for the new persona
|
|
357
|
+
* @param {String} credentials.password The password to set for the new persona
|
|
358
|
+
* @param {String} credentials.confirmPassword A double check to confirm the new password for the persona
|
|
359
|
+
* @param {Object} options Extra options and configuration for the request
|
|
360
|
+
* @return {Promise} Returns a promise that either resolves to the new authenticated session, or rejects with the responding error from the server
|
|
361
|
+
*/
|
|
362
|
+
service.signup = function(credentials, options) {
|
|
363
|
+
|
|
364
|
+
if (!options) {
|
|
365
|
+
options = {};
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
|
|
369
|
+
//////////////////////////
|
|
370
|
+
|
|
371
|
+
//Change the users current tokens straight away
|
|
372
|
+
var autoAuthenticate = true;
|
|
373
|
+
|
|
374
|
+
if (options.disableAutoAuthentication) {
|
|
375
|
+
autoAuthenticate = false;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
//////////////////////////////////////
|
|
379
|
+
|
|
380
|
+
var promise = new Promise(signupCheck)
|
|
381
|
+
|
|
382
|
+
function signupCheck(resolve, reject) {
|
|
383
|
+
|
|
384
|
+
if (!credentials) {
|
|
385
|
+
return reject({
|
|
386
|
+
message: 'No details provided',
|
|
387
|
+
})
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
if (!credentials.firstName || !credentials.firstName.length) {
|
|
391
|
+
return reject({
|
|
392
|
+
message: 'First Name was not provided',
|
|
393
|
+
})
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
if (!credentials.lastName || !credentials.lastName.length) {
|
|
397
|
+
return reject({
|
|
398
|
+
message: 'Last Name was not provided',
|
|
399
|
+
})
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
if (!credentials.email || !credentials.email.length) {
|
|
403
|
+
return reject({
|
|
404
|
+
message: 'Email/Username was not provided',
|
|
405
|
+
})
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
|
|
409
|
+
if (!credentials.password || !credentials.password.length) {
|
|
410
|
+
return reject({
|
|
411
|
+
message: 'Password was not provided',
|
|
412
|
+
})
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
if (!credentials.confirmPassword || !credentials.confirmPassword.length) {
|
|
416
|
+
return reject({
|
|
417
|
+
message: 'Confirm Password was not provided',
|
|
418
|
+
})
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
if (credentials.confirmPassword != credentials.password) {
|
|
422
|
+
return reject({
|
|
423
|
+
message: 'Your passwords do not match',
|
|
424
|
+
})
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
/////////////////////////////////////////////
|
|
428
|
+
|
|
429
|
+
var postOptions = {
|
|
430
|
+
bypassInterceptor: true
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
/////////////////////////////////////////////
|
|
434
|
+
|
|
435
|
+
var url = `${qik.apiURL}/user/signup`;
|
|
436
|
+
|
|
437
|
+
/////////////////////////////////////////////
|
|
438
|
+
|
|
439
|
+
//If we are authenticating as an application
|
|
440
|
+
if (options.application) {
|
|
441
|
+
|
|
442
|
+
//The url is relative to the domain
|
|
443
|
+
url = `${qik.domain || ''}/qik/application/signup`;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
//If we have a specified url
|
|
447
|
+
if (options.url) {
|
|
448
|
+
url = options.url;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
/////////////////////////////////////////////
|
|
452
|
+
|
|
453
|
+
qik.api.post(url, credentials, postOptions).then(function(res) {
|
|
454
|
+
|
|
455
|
+
if (autoAuthenticate) {
|
|
456
|
+
store.user = res.data;
|
|
457
|
+
dispatch();
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
resolve(res);
|
|
461
|
+
}, reject);
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
//////////////////////////////////////
|
|
465
|
+
|
|
466
|
+
return promise;
|
|
467
|
+
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
|
|
471
|
+
///////////////////////////////////////////////////
|
|
472
|
+
|
|
473
|
+
/**
|
|
474
|
+
* Retrieves a user's details by providing a password reset token
|
|
475
|
+
* @alias auth.retrieveUserFromResetToken
|
|
476
|
+
* @param {String} token The password reset token that was sent to the user's email address
|
|
477
|
+
* @param {Object} options other options for the request
|
|
478
|
+
* @param {Boolean} options.application If true will retrieve in the context of a managed persona in the same organisation as the current application.
|
|
479
|
+
* If not specified or false, will assume it's a Qik global user that is resetting their password.
|
|
480
|
+
* @return {Promise} Returns a promise that resolves with the reset session details
|
|
481
|
+
*/
|
|
482
|
+
service.retrieveUserFromResetToken = function(resetToken, options) {
|
|
483
|
+
|
|
484
|
+
if (!options) {
|
|
485
|
+
options = {};
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
//////////////////////////////////////
|
|
489
|
+
|
|
490
|
+
return new Promise(function(resolve, reject) {
|
|
491
|
+
|
|
492
|
+
var postOptions = {
|
|
493
|
+
bypassInterceptor: true
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
/////////////////////////////////////////////
|
|
497
|
+
|
|
498
|
+
//If a full fledged Qik User
|
|
499
|
+
//then send directly to the API auth endpoint
|
|
500
|
+
var url = `${qik.apiURL}/user/reset/${resetToken}`;
|
|
501
|
+
|
|
502
|
+
/////////////////////////////////////////////
|
|
503
|
+
|
|
504
|
+
//If we have a specified url
|
|
505
|
+
if (options.url) {
|
|
506
|
+
url = options.url;
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
/////////////////////////////////////////////
|
|
510
|
+
|
|
511
|
+
qik.api.get(url, postOptions).then(function(res) {
|
|
512
|
+
return resolve(res.data);
|
|
513
|
+
}, reject);
|
|
514
|
+
});
|
|
515
|
+
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
|
|
519
|
+
///////////////////////////////////////////////////
|
|
520
|
+
|
|
521
|
+
/**
|
|
522
|
+
* Updates a user's details including password by providing a password reset token
|
|
523
|
+
* @alias auth.updateUserWithToken
|
|
524
|
+
* @param {String} token The password reset token that was sent to the user's email address
|
|
525
|
+
* @param {Object} body The details to change for the user
|
|
526
|
+
* @param {Object} options other options for the request
|
|
527
|
+
* @return {Promise} Returns a promise that resolves with the reset session details
|
|
528
|
+
*/
|
|
529
|
+
service.updateUserWithToken = function(resetToken, body, options) {
|
|
530
|
+
|
|
531
|
+
if (!options) {
|
|
532
|
+
options = {};
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
//////////////////////////
|
|
536
|
+
|
|
537
|
+
//Change the users current tokens straight away
|
|
538
|
+
var autoAuthenticate = true;
|
|
539
|
+
|
|
540
|
+
if (options.disableAutoAuthentication) {
|
|
541
|
+
autoAuthenticate = false;
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
//////////////////////////////////////
|
|
545
|
+
|
|
546
|
+
return new Promise(function(resolve, reject) {
|
|
547
|
+
|
|
548
|
+
var postOptions = {
|
|
549
|
+
bypassInterceptor: true
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
/////////////////////////////////////////////
|
|
553
|
+
|
|
554
|
+
//If a full fledged Qik User
|
|
555
|
+
//then send directly to the API auth endpoint
|
|
556
|
+
var url = `${qik.apiURL}/user/reset/${resetToken}`;
|
|
557
|
+
|
|
558
|
+
/////////////////////////////////////////////
|
|
559
|
+
|
|
560
|
+
//If we have a specified url
|
|
561
|
+
if (options.url) {
|
|
562
|
+
url = options.url;
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
/////////////////////////////////////////////
|
|
566
|
+
|
|
567
|
+
qik.api.post(url, body, postOptions).then(function(res) {
|
|
568
|
+
|
|
569
|
+
//If we should automatically authenticate
|
|
570
|
+
//once the request is successful
|
|
571
|
+
//Then clear caches and update the session
|
|
572
|
+
if (autoAuthenticate) {
|
|
573
|
+
qik.cache.reset();
|
|
574
|
+
service.set(res.data);
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
return resolve(res.data);
|
|
578
|
+
}, reject);
|
|
579
|
+
});
|
|
580
|
+
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
|
|
584
|
+
///////////////////////////////////////////////////
|
|
585
|
+
|
|
586
|
+
/**
|
|
587
|
+
* Triggers a new Reset Password email request to the specified user.
|
|
588
|
+
* @alias auth.sendResetPasswordRequest
|
|
589
|
+
* @param {Object} body
|
|
590
|
+
* @param {String} body.email The email address of the user to reset the password for
|
|
591
|
+
* @param {String} body.redirect If the request is in the context of a managed user persona authenticated with an application, then you need to provide the url to direct the user to when they click the reset password link
|
|
592
|
+
* This is usually something like '/reset' for the current application, when the user clicks the link the reset token will be appended with ?token=RESET_TOKEN and your application should
|
|
593
|
+
* be ready on that url to handle the token and allow the user to use the token to reset their password
|
|
594
|
+
* @param {Object} options Extra options and configuration for the request
|
|
595
|
+
* @param {Boolean} options.application If true will send a reset email from the context of a managed persona in the same organisation as the current application.
|
|
596
|
+
* If not specified or false, will send a password reset request for a global Qik user organisation.
|
|
597
|
+
* @return {Promise} Returns a promise that either resolves if the password request was sent, or rejects if an error occurred
|
|
598
|
+
*/
|
|
599
|
+
service.sendResetPasswordRequest = function(body, options) {
|
|
600
|
+
|
|
601
|
+
if (!options) {
|
|
602
|
+
options = {};
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
//////////////////////////////////////
|
|
606
|
+
|
|
607
|
+
var promise = new Promise(signupCheck)
|
|
608
|
+
|
|
609
|
+
function signupCheck(resolve, reject) {
|
|
610
|
+
|
|
611
|
+
if (!body) {
|
|
612
|
+
return reject({
|
|
613
|
+
message: 'No details provided',
|
|
614
|
+
})
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
if (!body.email || !body.email.length) {
|
|
618
|
+
return reject({
|
|
619
|
+
message: 'Email was not provided',
|
|
620
|
+
})
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
//Set email as the email address
|
|
624
|
+
body.email = body.email;
|
|
625
|
+
|
|
626
|
+
/////////////////////////////////////////////
|
|
627
|
+
|
|
628
|
+
var postOptions = {
|
|
629
|
+
bypassInterceptor: true
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
/////////////////////////////////////////////
|
|
633
|
+
|
|
634
|
+
//If a full fledged Qik User
|
|
635
|
+
//then send directly to the API
|
|
636
|
+
var url = `${qik.apiURL}/user/forgot`;
|
|
637
|
+
|
|
638
|
+
/////////////////////////////////////////////
|
|
639
|
+
|
|
640
|
+
//If we have a specified url
|
|
641
|
+
if (options.url) {
|
|
642
|
+
url = options.url;
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
/////////////////////////////////////////////
|
|
646
|
+
|
|
647
|
+
qik.api.post(url, body, postOptions).then(resolve, reject);
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
//////////////////////////////////////
|
|
651
|
+
|
|
652
|
+
return promise;
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
|
|
656
|
+
///////////////////////////////////////////////////
|
|
657
|
+
|
|
658
|
+
/**
|
|
659
|
+
* Helper function to refresh an access token for an authenticated user session. This is usually handled automatically
|
|
660
|
+
* from the QikAuth service itself
|
|
661
|
+
* @alias auth.refreshAccessToken
|
|
662
|
+
* @param {String} refreshToken The refresh token to reactivate
|
|
663
|
+
* @param {Boolean} isManagedSession Whether or not the refresh token is for a managed persona session or a global Qik user session
|
|
664
|
+
* @return {Promise} A promise that either resolves with the refreshed token details or rejects with the responding error from the server
|
|
665
|
+
*/
|
|
666
|
+
|
|
667
|
+
const refreshContext = {};
|
|
668
|
+
|
|
669
|
+
|
|
670
|
+
service.refreshAccessToken = function(refreshToken) {
|
|
671
|
+
|
|
672
|
+
// /////////////////////////////////////////////
|
|
673
|
+
|
|
674
|
+
// if (appContext) {
|
|
675
|
+
// console.log('refresh token in app context')
|
|
676
|
+
// } else {
|
|
677
|
+
// console.log('refresh token in normal context')
|
|
678
|
+
// }
|
|
679
|
+
|
|
680
|
+
// /////////////////////////////////////////////
|
|
681
|
+
|
|
682
|
+
//If there is already a request in progress
|
|
683
|
+
if (refreshContext.inflightRefreshRequest) {
|
|
684
|
+
log(`qik.auth > use inflight request`);
|
|
685
|
+
return refreshContext.inflightRefreshRequest;
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
/////////////////////////////////////////////////////
|
|
689
|
+
|
|
690
|
+
//Create an refresh request
|
|
691
|
+
log(`qik.auth > refresh token new request`);
|
|
692
|
+
refreshContext.inflightRefreshRequest = new Promise(function(resolve, reject) {
|
|
693
|
+
|
|
694
|
+
|
|
695
|
+
log(`qik.auth > refresh token ${refreshToken}`);
|
|
696
|
+
|
|
697
|
+
//Bypass the interceptor on all token refresh calls
|
|
698
|
+
//Because we don't need to add the access token etc onto it
|
|
699
|
+
qik.api.post(`/user/refresh`, {
|
|
700
|
+
refreshToken: refreshToken
|
|
701
|
+
}, {
|
|
702
|
+
bypassInterceptor: true,
|
|
703
|
+
})
|
|
704
|
+
.then(function tokenRefreshComplete(res) {
|
|
705
|
+
|
|
706
|
+
//Update the user with any changes
|
|
707
|
+
//returned back from the refresh request
|
|
708
|
+
if (!res) {
|
|
709
|
+
log('qik.auth > no res');
|
|
710
|
+
refreshContext.inflightRefreshRequest = null;
|
|
711
|
+
return reject();
|
|
712
|
+
|
|
713
|
+
} else {
|
|
714
|
+
if (store.user) {
|
|
715
|
+
Object.assign(store.user, res.data);
|
|
716
|
+
} else {
|
|
717
|
+
store.user = res.data;
|
|
718
|
+
}
|
|
719
|
+
log(`qik.auth > token refreshed > ${res.data}`);
|
|
720
|
+
|
|
721
|
+
dispatch();
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
//Resolve with the new token
|
|
725
|
+
resolve(res.data.token);
|
|
726
|
+
|
|
727
|
+
//Remove the inflight request
|
|
728
|
+
setTimeout(function() {
|
|
729
|
+
refreshContext.inflightRefreshRequest = null;
|
|
730
|
+
})
|
|
731
|
+
|
|
732
|
+
|
|
733
|
+
})
|
|
734
|
+
.catch(function(err) {
|
|
735
|
+
console.log('TOKEN REFRESH FAILED', err);
|
|
736
|
+
|
|
737
|
+
//TODO Check if invalid_refresh_token
|
|
738
|
+
|
|
739
|
+
//console.log('Refresh request Failed')
|
|
740
|
+
setTimeout(function() {
|
|
741
|
+
refreshContext.inflightRefreshRequest = null;
|
|
742
|
+
});
|
|
743
|
+
reject(err);
|
|
744
|
+
|
|
745
|
+
|
|
746
|
+
|
|
747
|
+
});
|
|
748
|
+
});
|
|
749
|
+
|
|
750
|
+
//Return the refresh request
|
|
751
|
+
return refreshContext.inflightRefreshRequest;
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
|
|
755
|
+
|
|
756
|
+
///////////////////////////////////////////////////
|
|
757
|
+
|
|
758
|
+
|
|
759
|
+
|
|
760
|
+
/**
|
|
761
|
+
* Helper function to resync the user's session from the server. This is often used when first loading a webpage or app
|
|
762
|
+
* just to see if the user's permissions have changed since the user first logged in
|
|
763
|
+
* from the QikAuth service itself
|
|
764
|
+
* @alias auth.sync
|
|
765
|
+
* @return {Promise} A promise that either resolves with the user session
|
|
766
|
+
*/
|
|
767
|
+
|
|
768
|
+
var retryCount = 0;
|
|
769
|
+
|
|
770
|
+
service.sync = function() {
|
|
771
|
+
|
|
772
|
+
// console.log('Sync with server', store.user)
|
|
773
|
+
return qik.api.get('/user')
|
|
774
|
+
.then(function(res) {
|
|
775
|
+
|
|
776
|
+
// console.log('sync response', res);
|
|
777
|
+
|
|
778
|
+
if (res.data) {
|
|
779
|
+
if (store.user) {
|
|
780
|
+
Object.assign(store.user, res.data);
|
|
781
|
+
}
|
|
782
|
+
} else {
|
|
783
|
+
store.user = null;
|
|
784
|
+
}
|
|
785
|
+
log('qik.auth > server session refreshed');
|
|
786
|
+
retryCount = 0;
|
|
787
|
+
|
|
788
|
+
dispatch();
|
|
789
|
+
})
|
|
790
|
+
.catch(function(err) {
|
|
791
|
+
// if (retryCount > 2) {
|
|
792
|
+
console.log('auth sync not logged in');
|
|
793
|
+
store.user = null;
|
|
794
|
+
retryCount = 0;
|
|
795
|
+
dispatch();
|
|
796
|
+
// } else {
|
|
797
|
+
// console.log('Retry sync')
|
|
798
|
+
// retryCount++;
|
|
799
|
+
// service.sync();
|
|
800
|
+
// }
|
|
801
|
+
|
|
802
|
+
|
|
803
|
+
});
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
/////////////////////////////////////////////////////
|
|
807
|
+
|
|
808
|
+
/**
|
|
809
|
+
* Returns the current user's session data
|
|
810
|
+
* @alias auth.getCurrentUser
|
|
811
|
+
* @return {Object} The current user session
|
|
812
|
+
*/
|
|
813
|
+
service.getCurrentUser = function() {
|
|
814
|
+
return store.user;
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
|
|
818
|
+
service.getCurrentToken = function() {
|
|
819
|
+
return service.getCurrentUser().accessToken;
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
/////////////////////////////////////////////////////
|
|
823
|
+
|
|
824
|
+
qik.api.interceptors.request.use(function(config) {
|
|
825
|
+
|
|
826
|
+
//If we want to bypass the interceptor
|
|
827
|
+
//then just return the request
|
|
828
|
+
if (config.bypassInterceptor) {
|
|
829
|
+
console.log('auth interceptor was bypassed');
|
|
830
|
+
return config;
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
//////////////////////////////
|
|
834
|
+
//////////////////////////////
|
|
835
|
+
//////////////////////////////
|
|
836
|
+
//////////////////////////////
|
|
837
|
+
|
|
838
|
+
//Get the original request
|
|
839
|
+
var originalRequest = config;
|
|
840
|
+
|
|
841
|
+
//If we aren't logged in or don't have a token
|
|
842
|
+
var accessToken = _.get(store, 'user.accessToken');
|
|
843
|
+
var refreshToken = _.get(store, 'user.refreshToken');
|
|
844
|
+
|
|
845
|
+
//////////////////////////////
|
|
846
|
+
|
|
847
|
+
//If there is a user token
|
|
848
|
+
if (accessToken) {
|
|
849
|
+
//Set the token of the request as the user's access token
|
|
850
|
+
originalRequest.headers['Authorization'] = `Bearer ${accessToken}`;
|
|
851
|
+
log('qik.auth > using user token');
|
|
852
|
+
|
|
853
|
+
} else {
|
|
854
|
+
//Return the original request without a token
|
|
855
|
+
log('qik.auth > no token');
|
|
856
|
+
return originalRequest;
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
/////////////////////////////////////////////////////
|
|
860
|
+
|
|
861
|
+
//If no refresh token
|
|
862
|
+
if (!refreshToken) {
|
|
863
|
+
log('qik.auth > no refresh token');
|
|
864
|
+
|
|
865
|
+
//Continue with the original request
|
|
866
|
+
return originalRequest;
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
/////////////////////////////////////////////////////
|
|
870
|
+
|
|
871
|
+
//We have a refresh token so we need to check
|
|
872
|
+
//whether our access token is stale and needs to be refreshed
|
|
873
|
+
var now = new Date();
|
|
874
|
+
|
|
875
|
+
//Give us a bit of buffer incase some of our images
|
|
876
|
+
//are still loading
|
|
877
|
+
now.setSeconds(now.getSeconds() + 10);
|
|
878
|
+
|
|
879
|
+
/////////////////////////////////////////////////////
|
|
880
|
+
|
|
881
|
+
var expiryDate = _.get(store, 'user.expires');
|
|
882
|
+
var expires = new Date(expiryDate);
|
|
883
|
+
|
|
884
|
+
//If the token is still fresh
|
|
885
|
+
if (now < expires) {
|
|
886
|
+
//Return the original request
|
|
887
|
+
return originalRequest;
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
/////////////////////////////////////////////////////
|
|
891
|
+
|
|
892
|
+
log('qik.auth > token expired');
|
|
893
|
+
|
|
894
|
+
return new Promise(function(resolve, reject) {
|
|
895
|
+
|
|
896
|
+
//Refresh the token
|
|
897
|
+
service.refreshAccessToken(refreshToken)
|
|
898
|
+
.then(function(newToken) {
|
|
899
|
+
log('qik.auth > token refreshed', isManagedUser);
|
|
900
|
+
//Update the original request with our new token
|
|
901
|
+
originalRequest.headers['Authorization'] = `Bearer ${newToken}`;
|
|
902
|
+
//And continue onward
|
|
903
|
+
return resolve(originalRequest);
|
|
904
|
+
})
|
|
905
|
+
.catch(function(err) {
|
|
906
|
+
console.log('ERRRRRR', err);
|
|
907
|
+
log('qik.auth > token refresh rejected', err);
|
|
908
|
+
return reject(err);
|
|
909
|
+
});
|
|
910
|
+
});
|
|
911
|
+
|
|
912
|
+
|
|
913
|
+
}, function(error) {
|
|
914
|
+
return Promise.reject(error);
|
|
915
|
+
})
|
|
916
|
+
|
|
917
|
+
/////////////////////////////////////////////////////
|
|
918
|
+
|
|
919
|
+
qik.api.interceptors.response.use(function(response) {
|
|
920
|
+
return response;
|
|
921
|
+
}, function(err) {
|
|
922
|
+
|
|
923
|
+
//////////////////////////////
|
|
924
|
+
|
|
925
|
+
//Get the response status
|
|
926
|
+
var status = _.get(err, 'response.status') || err.status;
|
|
927
|
+
|
|
928
|
+
log('qik.auth > error', status);
|
|
929
|
+
switch (status) {
|
|
930
|
+
case 401:
|
|
931
|
+
console.log('logout from 401')
|
|
932
|
+
service.logout();
|
|
933
|
+
break;
|
|
934
|
+
default:
|
|
935
|
+
//Some other error
|
|
936
|
+
break;
|
|
937
|
+
}
|
|
938
|
+
|
|
939
|
+
/////////////////////////////////////////////////////
|
|
940
|
+
|
|
941
|
+
return Promise.reject(err);
|
|
942
|
+
})
|
|
943
|
+
|
|
944
|
+
|
|
945
|
+
/**
|
|
946
|
+
* @name auth.addEventListener
|
|
947
|
+
* @description Adds a callback that will be triggered whenever the specified event occurs
|
|
948
|
+
* @function
|
|
949
|
+
* @param {String} event The event to listen for
|
|
950
|
+
* @param {Function} callback The function to fire when this event is triggered
|
|
951
|
+
* @example
|
|
952
|
+
* //Listen for when the user session changes
|
|
953
|
+
* qik.auth.addEventListener('change', function(userSession) {})
|
|
954
|
+
*/
|
|
955
|
+
|
|
956
|
+
/**
|
|
957
|
+
* @name auth.removeEventListener
|
|
958
|
+
* @description Removes all a callback from the listener list
|
|
959
|
+
* @function
|
|
960
|
+
* @param {String} event The event to stop listening for
|
|
961
|
+
* @param {Function} callback The function to remove from the listener list
|
|
962
|
+
* @example
|
|
963
|
+
* //Stop listening for the change event
|
|
964
|
+
* qik.auth.removeEventListener('change', myFunction)
|
|
965
|
+
*/
|
|
966
|
+
|
|
967
|
+
/**
|
|
968
|
+
* @name auth.removeAllListeners
|
|
969
|
+
* @description Removes all listening callbacks for all events
|
|
970
|
+
* @function
|
|
971
|
+
* @example
|
|
972
|
+
* qik.auth.removeAllListeners()
|
|
973
|
+
*/
|
|
974
|
+
|
|
975
|
+
return service;
|
|
976
|
+
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
|
|
980
|
+
export default QikAuth;
|