@qikdev/sdk 1.0.10 → 1.0.13

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.
@@ -5,13 +5,26 @@ import { version } from "../version.js";
5
5
 
6
6
 
7
7
  /**
8
- * Creates a new instance of Axios, with automatic authentication and token refreshing functionality.
9
- * This module provides all the underlying functions for making http requests and interacting with the REST API.
10
- * It is a wrapper around the axios library, a popular promise based javascript request package. The api module is used by most other modules in the SDK and will handle authentication, token refreshing and other headers automatically.
8
+ * The HTTP client for the Qik REST API: an axios instance whose base URL is the API, so paths are relative
9
+ * ('/content/profile/list'). Every request automatically carries the current access token (refreshed
10
+ * first when it's about to expire), the caller's timezone and the SDK version. Responses are axios
11
+ * responses, so the body is in `.data`; errors are axios errors (the server's message is in
12
+ * err.response.data.message, or use sdk.utils.errorMessage(err)).
13
+ * Use the standard axios methods: get(url, config), delete(url, config), post(url, body, config),
14
+ * put(url, body, config), patch(url, body, config). Query string values go in config.params (arrays are
15
+ * sent as repeated keys: ?select=a&select=b). A request body for DELETE goes in config.data.
16
+ * Rate-limited requests (429) are retried up to 3 times honouring Retry-After, and 502/504 responses are retried.
17
+ * Identical requests made at the same time share one network call.
18
+ * Extra config options the SDK understands: `bypassInterceptor: true` skips the automatic token refresh
19
+ * (the current token is still sent); add `withoutToken: true` as well to send no access token or Qik headers at all.
11
20
  * @alias api
12
21
  * @constructor
13
22
  * @hideconstructor
14
- * @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.
23
+ * @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.
24
+ * @example
25
+ * const { data } = await sdk.api.get('/user');
26
+ * const { data: result } = await sdk.api.post('/content/profile/list', { select: ['firstName'], page: { size: 10 } });
27
+ * const { data: partial } = await sdk.api.get('/content/61eca4746971e75c1fc670cd', { params: { select: ['title', 'meta.created'] } });
15
28
  */
16
29
 
17
30
  const CancelToken = axios.CancelToken;
@@ -121,25 +134,19 @@ var QikAPI = function (qik) {
121
134
  //////////////////////////////////////////////////////////////////////////////
122
135
 
123
136
  /**
124
- *
125
- * Generate an Endpoint URL, complete with authentication.
126
- * this function is helpful for generating authenticated links for the user to open in another window
137
+ * Builds a full URL for an API endpoint, with the current access token added as ?access_token=, for
138
+ * links the browser opens by itself (a download link, an <img> src, a new window) where no
139
+ * Authorization header can be sent. The token in the URL grants the user's access, so don't share or log these URLs.
127
140
  * @alias api.generateEndpointURL
128
- * @param {String} endpoint The endpoint to generate a url for
129
- * @param {Object} params Extra parameters for the url
130
- * @param {Object} options Additional request configuration
131
- * @param {Object} options.withoutToken Don't append the current user's access token to the url
132
- * @param {Object} options.file Whether the response for this request is expected to be a binary file type
141
+ * @param {String} endpoint The path, starting with a slash, e.g. '/image/61eca4746971e75c1fc670cf'.
142
+ * @param {Object} [params] Query string values. Values that are undefined, null, false, 0 or '' are left out; arrays become repeated keys.
143
+ * @param {Object} [options] Options.
144
+ * @param {Boolean} [options.withoutToken] Don't add the access token.
145
+ * @param {Boolean} [options.file] Build the URL on the file host (sdk.fileAPI) when one is configured, instead of the API URL.
146
+ * @returns {String} The URL, e.g. 'https://api.qik.dev/image/61eca...?access_token=XXXX&w=100&h=50'.
133
147
  * @example
134
- *
135
- *
136
- * const result = sdk.api.generateEndpointURL('/image/61eca4746971e75c1fc670cf', {
137
- * w:100,
138
- * h:50,
139
- * }, {file:true})
140
- *
141
- * // Would return something similar to:
142
- * // https://api.qik.dev/image/61eca4746971e75c1fc670cf?w=100&h=50&access_token=XXXX...
148
+ * const url = sdk.api.generateEndpointURL('/image/61eca4746971e75c1fc670cf', { w: 100, h: 50 }, { file: true });
149
+ * // https://api.qik.dev/image/61eca4746971e75c1fc670cf?access_token=XXXX&w=100&h=50
143
150
  */
144
151
  service.generateEndpointURL = function (endpoint, params, options) {
145
152
  options = options || {};
@@ -172,6 +179,19 @@ var QikAPI = function (qik) {
172
179
 
173
180
  //////////////////////////////////////////////////////////////////////////////
174
181
 
182
+ /**
183
+ * Tells whether a request error came from cancelling the request (for example a cancellable content.list),
184
+ * so you can ignore it instead of showing an error.
185
+ * @alias api.wasCancelled
186
+ * @param {Error} err The error a request rejected with.
187
+ * @returns {Boolean} True when the request was cancelled.
188
+ * @example
189
+ * try {
190
+ * await request.promise;
191
+ * } catch (err) {
192
+ * if (!sdk.api.wasCancelled(err)) throw err;
193
+ * }
194
+ */
175
195
  service.wasCancelled = function (err) {
176
196
  return axios.isCancel(err);
177
197
  };
@@ -343,16 +363,23 @@ var QikAPI = function (qik) {
343
363
  ///////////////////////////////////////
344
364
 
345
365
  /**
346
- *
347
- * Reference to the underlying axios package (https://www.npmjs.com/package/axios)
348
- * Useful for creating new instances of axios if you want to create http requests to
349
- * external APIs and not send tokens etc..
350
- * @name api.axios
366
+ * The axios CancelToken class, for cancelling an in-flight request: create a source, pass
367
+ * source.token as the request's cancelToken, and call source.cancel() to abort it.
368
+ * @alias api.CancelToken
351
369
  * @example
352
- * const newRequest = await sdk.api.axios.get('https://otherapi.com/content/61eca4746971e75c1fc670cf');
370
+ * const source = sdk.api.CancelToken.source();
371
+ * const request = sdk.api.get('/user', { cancelToken: source.token });
372
+ * source.cancel('No longer needed');
353
373
  */
354
-
355
374
  service.CancelToken = CancelToken;
375
+
376
+ /**
377
+ * The plain axios library (https://www.npmjs.com/package/axios), for requests to other services:
378
+ * requests made with it carry no Qik token or headers and don't use the API base URL.
379
+ * @alias api.axios
380
+ * @example
381
+ * const { data } = await sdk.api.axios.get('https://example.com/feed.json');
382
+ */
356
383
  service.axios = axios;
357
384
 
358
385
  ///////////////////////////////////////
@@ -4,12 +4,23 @@ import { createStorageAdapter } from './qik.storage.js';
4
4
  ///////////////////////////////////////////////////
5
5
 
6
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
7
+ * Signing in and out and the current session. The SDK keeps the session in memory (or, with
8
+ * useHttpOnlyCookies, lets the browser hold the tokens in httpOnly cookies) and attaches its access token to
9
+ * every sdk.api request, refreshing it shortly before it expires. A 401 response signs the user out.
10
+ * The session object returned by getCurrentUser() is { session, token }: `session` describes the signed-in
11
+ * user in the current organisation (_id, firstName, lastName, email, userType, organisation, persona,
12
+ * profiles, permissions, cacheKey...) and is what the sdk.access helpers expect; `token` is
13
+ * { accessToken, refreshToken, expires } (absent in cookie mode).
14
+ * Listen for 'change' with sdk.auth.addEventListener('change', handler): the handler gets the new stored
15
+ * value (or null after sign-out) and, as a second argument, any parameters passed to sdk.auth.set().
9
16
  * @alias auth
10
17
  * @constructor
11
18
  * @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.
19
+ * @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.
20
+ * @example
21
+ * sdk.auth.addEventListener('change', (user) => {
22
+ * console.log(user ? `Signed in as ${user.session.firstName}` : 'Signed out');
23
+ * });
13
24
  */
14
25
 
15
26
  var QikAuth = function(qik) {
@@ -59,10 +70,11 @@ var QikAuth = function(qik) {
59
70
 
60
71
  //Dispatch the change to the listeners
61
72
  if (service.onChange) {
62
- service.onChange(user);
73
+ service.onChange(user, parameters);
63
74
  }
64
75
 
65
- //Dispatch the change event
76
+ //Dispatch the change event: listeners get the user first and any
77
+ //parameters passed to set() second
66
78
  dispatcher.dispatch('change', user, parameters);
67
79
  }
68
80
 
@@ -70,15 +82,17 @@ var QikAuth = function(qik) {
70
82
  ///////////////////////////////////////////////////
71
83
 
72
84
  /**
85
+ * Stores a session as the current one (replacing any other) and fires 'change', unless it's identical
86
+ * to the session already stored. Use it with a session returned by a call made with
87
+ * { disableAutoAuthentication: true }.
73
88
  * @alias auth.set
74
- * @description Manually set current user session
75
- * @param {Object} user The user session object to set as the current user session
76
- * @param {Object} parameters Additional parameters to dispatch
77
- * @param {Boolean} stopDispatch Whether to supress dispatching a 'change' event.
89
+ * @param {Object|null} user The { session, token } object to store, as returned by login, changeOrganisation or impersonate. null clears it.
90
+ * @param {Object} [parameters] Extra details for 'change' listeners, passed to them as their second argument (the user is the first).
91
+ * @param {Boolean} [stopDispatch] Store the session without firing 'change'.
92
+ * @returns {undefined}
78
93
  * @example
79
- *
80
- * const userSession = {_id:'61eca4746971e75c1fc670cf', firstName:'Daffy', lastName:'Duck' ...};
81
- * sdk.auth.set(userSession);
94
+ * const { data } = await sdk.auth.login(credentials, { disableAutoAuthentication: true });
95
+ * sdk.auth.set(data);
82
96
  */
83
97
 
84
98
  service.set = function(user, parameters, stopDispatch) {
@@ -97,8 +111,10 @@ var QikAuth = function(qik) {
97
111
  ///////////////////////////////////////////////////
98
112
 
99
113
  /**
114
+ * Signs out locally: forgets the current session, clears every SDK cache (sdk.cache.reset()) and fires
115
+ * 'change' with null. No request is sent to the server.
100
116
  * @alias auth.logout
101
- * @description Clear the current user session from memory and erase all caches
117
+ * @returns {undefined}
102
118
  * @example
103
119
  * sdk.auth.logout();
104
120
  */
@@ -112,21 +128,19 @@ var QikAuth = function(qik) {
112
128
  ///////////////////////////////////////////////////
113
129
 
114
130
  /**
131
+ * Switches the signed-in user into another organisation they belong to (POST /user/switch/:organisationID).
132
+ * By default the new session replaces the current one, SDK caches are cleared and 'change' fires; an open
133
+ * socket reconnects under the new organisation.
115
134
  * @alias auth.changeOrganisation
116
- * @description Manually set current user session
117
- * @param {(String|Object)} organisation The id of the organisation to switch into
118
- * @param {Object} options Additional options
119
- * @param {Boolean} options.disableAutoAuthentication By default when switching organisation, the current user session
120
- * will be updated to reflect a session in the new organisation, you can use this option to disable that behavior and instead return the
121
- * user session without dispatching any events
135
+ * @param {String|Object} organisationID The organisation's id, or an object with an _id.
136
+ * @param {Object} [options] Options.
137
+ * @param {Boolean} [options.disableAutoAuthentication] Only return the new session; don't store it, clear caches or fire 'change'.
138
+ * @returns {Promise<Object>} The new { session, token } for that organisation.
122
139
  * @example
140
+ * await sdk.auth.changeOrganisation('61eca4746971e75c1fc670cf');
123
141
  *
124
- * sdk.auth.changeOrganisation('61eca4746971e75c1fc670cf');
125
- * // Current user session will be automatically updated
126
- *
127
- * const newSession = await sdk.auth.changeOrganisation('61eca4746971e75c1fc670cf', {disableAutoAuthentication:true});
128
- * // Current user session will not be updated
129
- * sdk.auth.set(newSession);
142
+ * // Get the session without switching to it
143
+ * const newSession = await sdk.auth.changeOrganisation('61eca4746971e75c1fc670cf', { disableAutoAuthentication: true });
130
144
  */
131
145
  service.changeOrganisation = function(organisationID, options) {
132
146
 
@@ -171,21 +185,18 @@ var QikAuth = function(qik) {
171
185
  ///////////////////////////////////////////////////
172
186
 
173
187
  /**
188
+ * Starts a session as another person (persona) in the organisation, for administrators checking what
189
+ * someone else sees (POST /user/impersonate with { persona, application } in the body).
190
+ * On success the new session replaces the current one and caches are cleared.
174
191
  * @alias auth.impersonate
175
- * @description Impersonate another user within your organisation
176
- * @param {(String|Object)} persona The id of the user persona you want to impersonate
177
- * @param {Object} options Additional options
178
- * @param {Boolean} options.disableAutoAuthentication By default when impersonating a user, the current user session
179
- * will be updated automatically to reflect the new session, you can use this option to disable that behavior and instead return the
180
- * new impersonation user session without dispatching any events
192
+ * @param {String|Object} personaID The persona's id, or an object with an _id.
193
+ * @param {Object} [options] Options.
194
+ * @param {String|Object} [options.application] An application id (or object with an _id), to impersonate the persona as an app user of that application.
195
+ * @param {Boolean} [options.disableAutoAuthentication] Only return the response; don't store the new session or clear caches.
196
+ * @returns {Promise<Object>} The axios response; its `data` is the impersonation { session, token }.
181
197
  * @example
182
- *
183
- * sdk.auth.impersonate('61eca4746971e75c1fc670cf');
184
- * // Current user session will be automatically updated
185
- *
186
- * const newSession = await sdk.auth.impersonate('61eca4746971e75c1fc670cf', {disableAutoAuthentication:true});
187
- * // Current user session will not be updated
188
- * sdk.auth.set(newSession);
198
+ * await sdk.auth.impersonate('61eca4746971e75c1fc670cf');
199
+ * console.log(sdk.auth.getCurrentUser().session.firstName);
189
200
  */
190
201
  service.impersonate = function(personaID, options) {
191
202
 
@@ -209,7 +220,13 @@ var QikAuth = function(qik) {
209
220
 
210
221
  //////////////////////////
211
222
 
212
- var promise = qik.api.post(`/user/impersonate/${personaID}`)
223
+ // The server reads the persona (and optional application) from the body
224
+ var body = { persona: personaID };
225
+ if (options.application) {
226
+ body.application = qik.utils.id(options.application);
227
+ }
228
+
229
+ var promise = qik.api.post(`/user/impersonate`, body)
213
230
 
214
231
  promise.then(function(res) {
215
232
 
@@ -227,30 +244,34 @@ var QikAuth = function(qik) {
227
244
  ///////////////////////////////////////////////////
228
245
 
229
246
  /**
247
+ * Signs in with an email and password (POST /user/login). When a one-time code is required (always for
248
+ * dashboard users; for app users when the application or the person requires it) the first call, without `mfa`,
249
+ * emails a code and returns { mfa: true, type: 'email', to, expires } instead of a session; call again with
250
+ * the same credentials plus the code in `mfa` to get the session.
251
+ * With an `application` id (or when running on an application's domain) the person signs in to that
252
+ * application's organisation as an app user; otherwise as a dashboard user.
253
+ * By default a returned session is stored as the current one and 'change' fires; the code-required
254
+ * response is only returned, not stored.
255
+ * With useHttpOnlyCookies the tokens are set as httpOnly cookies instead of being returned for storage.
230
256
  * @alias auth.login
231
- * @description Login and authenticate as a user
232
- * @param {Object} credentials The credentials used to login
233
- * @param {String} credentials.email The email address to login to
234
- * @param {String} credentials.password The password to login with
235
- * @param {String} credentials.mfa The MFA (Multi Factor Authentication) code
236
- * @param {Object} options Additional options
237
- * @param {Boolean} options.disableAutoAuthentication By default when logging in the current user session
238
- * will be updated automatically to reflect the new session, you can use this option to disable that behavior and instead return the
239
- * session that was logged in to without dispatching any events
257
+ * @param {Object} credentials The sign-in details.
258
+ * @param {String} credentials.email The email address.
259
+ * @param {String} credentials.password The password.
260
+ * @param {String} [credentials.mfa] The one-time code from the email, on the second call.
261
+ * @param {String} [credentials.application] Application id, to sign in to an application's organisation as an app user.
262
+ * @param {String} [credentials.organisation] Organisation id to sign in to (dashboard users who belong to several).
263
+ * @param {Object} [options] Options.
264
+ * @param {Boolean} [options.disableAutoAuthentication] Only return the response; don't store the session or fire 'change'.
265
+ * @param {String} [options.url] Post to this URL instead of the API's /user/login.
266
+ * @returns {Promise<Object>} The axios response. Its `data` is either the code request { mfa: true, type, to, expires } or the session { session, token }. Rejects with { message } when email or password is missing, or with the server error (400 for wrong credentials or code).
240
267
  * @example
241
- *
242
- * const credentials = {
243
- * email:'me@email.com',
244
- * password:'******',
245
- * mfa:'1234',
268
+ * const credentials = { email: 'me@example.com', password: '******' };
269
+ * let { data } = await sdk.auth.login(credentials);
270
+ * if (data.mfa) {
271
+ * // ask for the code emailed to data.to, then:
272
+ * ({ data } = await sdk.auth.login({ ...credentials, mfa: '123456' }));
246
273
  * }
247
- *
248
- * sdk.auth.login(credentials);
249
- * // Current user session will be automatically updated
250
- *
251
- * const newSession = await sdk.auth.login(credentials, {disableAutoAuthentication:true});
252
- * // Current user session will not be updated
253
- * sdk.auth.set(newSession);
274
+ * console.log(data.session.firstName);
254
275
  */
255
276
  service.login = async function(credentials, options) {
256
277
 
@@ -321,7 +342,9 @@ var QikAuth = function(qik) {
321
342
 
322
343
  qik.api.post(url, credentials, postOptions).then(function(res) {
323
344
 
324
- if (autoAuthenticate) {
345
+ // A { mfa: true } response asks for a one-time code; it isn't a session,
346
+ // so only store real sessions
347
+ if (autoAuthenticate && !(res.data && res.data.mfa)) {
325
348
  service.set(res.data);
326
349
  }
327
350
 
@@ -337,6 +360,33 @@ var QikAuth = function(qik) {
337
360
 
338
361
  ///////////////////////////////////////////////////
339
362
 
363
+ /**
364
+ * Creates a new account (POST /user/signup). Checks locally that firstName, lastName, email, password and
365
+ * a matching confirmPassword were given. If the email already has an account and the password matches, the
366
+ * person is signed in instead. With an `application` id in the credentials the account is created as an app
367
+ * user in that application's organisation. By default the returned session is stored and 'change' fires.
368
+ * @alias auth.signup
369
+ * @param {Object} credentials The new account's details.
370
+ * @param {String} credentials.firstName First name.
371
+ * @param {String} credentials.lastName Last name.
372
+ * @param {String} credentials.email Email address.
373
+ * @param {String} credentials.password Password.
374
+ * @param {String} credentials.confirmPassword Must equal password.
375
+ * @param {String} [credentials.application] Application id, to sign up to an application's organisation as an app user.
376
+ * @param {Object} [options] Options.
377
+ * @param {Boolean} [options.disableAutoAuthentication] Only return the response; don't store the session or fire 'change'.
378
+ * @param {Boolean} [options.application] Post to the current site's /qik/application/signup route (sdk.domain) instead of the API.
379
+ * @param {String} [options.url] Post to this URL instead.
380
+ * @returns {Promise<Object>} The axios response; its `data` is the new { session, token }. Rejects with { message } when a detail is missing or the passwords differ.
381
+ * @example
382
+ * const { data } = await sdk.auth.signup({
383
+ * firstName: 'Mickey',
384
+ * lastName: 'Mouse',
385
+ * email: 'mickey@example.com',
386
+ * password: '******',
387
+ * confirmPassword: '******',
388
+ * });
389
+ */
340
390
  service.signup = async function(credentials, options) {
341
391
 
342
392
  if (!options) {
@@ -457,18 +507,16 @@ var QikAuth = function(qik) {
457
507
  ///////////////////////////////////////////////////
458
508
 
459
509
  /**
510
+ * Looks up who a password reset token belongs to (GET /user/reset/:token), e.g. to greet them on the
511
+ * reset page. Reset tokens are short-lived and come from the link in a reset email.
460
512
  * @alias auth.retrieveUserFromResetToken
461
- * @description Retrieve user session through use of a valid reset token,
462
- * Reset tokens are short lived tokens that can be generated when a user has forgotten their password or their
463
- * password has been reset by an administrator
464
- * @param {String} resetToken The token to use to authenticate
465
- * @param {Object} options Additional options for the request
513
+ * @param {String} resetToken The token from the reset link.
514
+ * @param {Object} [options] Options.
515
+ * @param {String} [options.url] Request this URL instead.
516
+ * @returns {Promise<Object>} For a dashboard user { _id, firstName, lastName, email }; for an app user the persona { _id, firstName, lastName, username, application: { title, primaryDomain } }. Rejects with 400 when the token is used up or expired.
466
517
  * @example
467
- *
468
- * const resetToken = 'XXX-324623-$$...';
469
- *
470
- * // Retrieve the user session by providing a reset token
471
- * const user = await sdk.auth.retrieveUserFromResetToken(resetToken);
518
+ * const person = await sdk.auth.retrieveUserFromResetToken(token);
519
+ * console.log(`Choose a new password, ${person.firstName}`);
472
520
  */
473
521
  service.retrieveUserFromResetToken = async function(resetToken, options) {
474
522
 
@@ -491,20 +539,19 @@ var QikAuth = function(qik) {
491
539
  ///////////////////////////////////////////////////
492
540
 
493
541
  /**
542
+ * Sets a new password (and optionally name) using a reset token (POST /user/reset/:token). The token
543
+ * works once. For a dashboard user the response is a new session, which by default is stored, caches are
544
+ * cleared and 'change' fires; for an app user the response is { done: true } (sign in afterwards) — note the
545
+ * SDK currently stores that as the session too unless disableAutoAuthentication is set.
494
546
  * @alias auth.updateUserWithToken
495
- * @description Update a user's credentials through use of a reset token
496
- * @param {String} resetToken The token to use to authenticate
497
- * @param {Object} body Updates to be made to the user
498
- * @param {Object} options Additional options for the request
499
- * @param {Boolean} options.disableAutoAuthentication By default the current user session
500
- * will be updated automatically to reflect the new updated session, you can use this option to disable that behavior
501
- * and instead return the result without dispatching any events
547
+ * @param {String} resetToken The token from the reset link.
548
+ * @param {Object} body The changes: { password, firstName, lastName }, plus email (dashboard users) or username (app users).
549
+ * @param {Object} [options] Options.
550
+ * @param {Boolean} [options.disableAutoAuthentication] Only return the result; don't store it or fire 'change'.
551
+ * @param {String} [options.url] Post to this URL instead.
552
+ * @returns {Promise<Object>} A new { session, token } for a dashboard user, or { done: true } for an app user.
502
553
  * @example
503
- *
504
- * const resetToken = 'XXX-324623-$$...';
505
- *
506
- * // Retrieve the user session by providing a reset token
507
- * const user = await sdk.auth.retrieveUserFromResetToken(resetToken);
554
+ * await sdk.auth.updateUserWithToken(token, { password: 'a new password' });
508
555
  */
509
556
  service.updateUserWithToken = async function(resetToken, body, options) {
510
557
 
@@ -547,17 +594,16 @@ var QikAuth = function(qik) {
547
594
  }
548
595
 
549
596
  /**
597
+ * Emails the person a password reset link (POST /user/forgot). On an application's domain (or with an
598
+ * application in the details) the link is for that application's app users.
550
599
  * @alias auth.sendResetPasswordRequest
551
- * @description This function allows a reset token to be generated and emailed to the requesting user
552
- * allowing them to modify their user details
553
- * @param {Object} body Details for the reset request
554
- * @param {String} body.email The email of the user to generate a token for
600
+ * @param {Object} details The request.
601
+ * @param {String} details.email The account's email address.
602
+ * @param {Object} [options] Options.
603
+ * @param {String} [options.url] Post to this URL instead.
604
+ * @returns {Promise<Object>} The axios response; for dashboard users its `data` is { done: true }. Rejects with { message } when no email is given.
555
605
  * @example
556
- *
557
- * const resetToken = 'XXX-324623-$$...';
558
- *
559
- * // Retrieve the user session by providing a reset token
560
- * const user = await sdk.auth.retrieveUserFromResetToken(resetToken);
606
+ * await sdk.auth.sendResetPasswordRequest({ email: 'me@example.com' });
561
607
  */
562
608
  service.sendResetPasswordRequest = function(details, options) {
563
609
 
@@ -591,17 +637,15 @@ var QikAuth = function(qik) {
591
637
  ///////////////////////////////////////////////////
592
638
 
593
639
  /**
640
+ * Makes sure the current access token is still valid, refreshing it (POST /user/refresh) when it expires
641
+ * within the next few seconds. Requests through sdk.api already do this automatically; call it before handing
642
+ * the token to something else, such as a URL or another library.
594
643
  * @alias auth.ensureValidToken
595
- * @description This function forces a check to ensure that the current access token has not expired.
596
- * If the token has expired, the user session will be refreshed with a new token.
597
- * @param {Boolean} forceRefresh Whether to force the current token to be refreshed, even if it has not yet expired.
644
+ * @param {Boolean} [forceRefresh] Refresh even if the token hasn't expired yet.
645
+ * @returns {Promise<Object|String>} The current token object { accessToken, refreshToken, expires } when it is still valid, or the new access token string after a refresh. In cookie mode, a placeholder string. Rejects when nobody is signed in.
598
646
  * @example
599
- *
600
- * // Check to ensure that the current access token is valid
601
- * sdk.auth.ensureValidToken();
602
- *
603
- * // Force the token to be refreshed, even if the current token in use has not yet expired.
604
- * sdk.auth.ensureValidToken(true);
647
+ * await sdk.auth.ensureValidToken();
648
+ * const url = `${sdk.apiURL}/file/${fileID}?access_token=${sdk.auth.getCurrentToken()}`;
605
649
  */
606
650
  service.ensureValidToken = async function(forceRefresh) {
607
651
 
@@ -656,6 +700,16 @@ var QikAuth = function(qik) {
656
700
 
657
701
  const refreshContext = {};
658
702
 
703
+ /**
704
+ * Exchanges the refresh token for a new access token (POST /user/refresh), stores the refreshed session
705
+ * and fires 'change'. Calls made while a refresh is already running share it. Usually you want
706
+ * ensureValidToken() instead, which only refreshes when needed.
707
+ * @alias auth.refreshAccessToken
708
+ * @param {String} [refreshToken] The refresh token (getCurrentUser().token.refreshToken). Not needed in cookie mode.
709
+ * @returns {Promise<String>} The new access token (in cookie mode, the string 'cookie_token_refreshed').
710
+ * @example
711
+ * const accessToken = await sdk.auth.refreshAccessToken(sdk.auth.getCurrentUser().token.refreshToken);
712
+ */
659
713
  service.refreshAccessToken = async function(refreshToken) {
660
714
 
661
715
  //If there is already a request in progress
@@ -739,14 +793,21 @@ var QikAuth = function(qik) {
739
793
  ///////////////////////////////////////////////////
740
794
 
741
795
  /**
796
+ * Re-reads the current session from the server (GET /user) and stores it, e.g. after the user's
797
+ * permissions or profile changed. If the request fails the session is cleared (signed out), unless
798
+ * options.keepOnError is set, in which case the signed-in user is left exactly as it was.
799
+ * Fires 'change' when done.
742
800
  * @alias auth.sync
743
- * @description A useful function to sync the current user session with the server.
801
+ * @param {Object} [options]
802
+ * @param {Boolean} [options.keepOnError] Keep the current user if the request fails (e.g. a network
803
+ * blip) instead of signing out. The socket uses this when told the person's access changed.
804
+ * @returns {Promise<undefined>} Resolves when the stored session has been updated.
744
805
  * @example
745
- *
746
- * // Makes request to the API and updates the current user session to match the response
747
- * sdk.auth.sync();
806
+ * await sdk.auth.sync();
807
+ * const { session } = sdk.auth.getCurrentUser();
748
808
  */
749
- service.sync = function() {
809
+ service.sync = function(options) {
810
+ options = options || {};
750
811
 
751
812
  return qik.api.get('/user')
752
813
  .then(function(res) {
@@ -771,6 +832,9 @@ var QikAuth = function(qik) {
771
832
  dispatch();
772
833
  })
773
834
  .catch(function(err) {
835
+ if (options.keepOnError) {
836
+ return;
837
+ }
774
838
  service.set(null);
775
839
  dispatch();
776
840
  });
@@ -779,11 +843,15 @@ var QikAuth = function(qik) {
779
843
  /////////////////////////////////////////////////////
780
844
 
781
845
  /**
846
+ * Returns the stored session straight away (no request).
782
847
  * @alias auth.getCurrentUser
783
- * @description Retrieves the current user session
848
+ * @returns {Object|null} { session, token } when signed in, otherwise null. Pass `session` to the sdk.access helpers.
784
849
  * @example
785
- *
786
- * const me = sdk.auth.getCurrentUser();
850
+ * const current = sdk.auth.getCurrentUser();
851
+ * if (current) {
852
+ * const { session } = current;
853
+ * console.log(session.firstName, session.organisation.title);
854
+ * }
787
855
  */
788
856
  service.getCurrentUser = function() {
789
857
  return storageAdapter.getUser();
@@ -791,12 +859,12 @@ var QikAuth = function(qik) {
791
859
 
792
860
 
793
861
  /**
862
+ * Returns the access token requests are sent with: the signed-in user's token, or when nobody is signed in
863
+ * the application token the SDK was created with. Doesn't check expiry (see ensureValidToken).
794
864
  * @alias auth.getCurrentToken
795
- * @description Retrieves the current access token. If the user is authenticated
796
- * the response will be the current user's access token, otherwise will fall back to the
797
- * applications token.
865
+ * @returns {String|undefined} The access token, or undefined when there is none. In cookie mode a signed-in user's token can't be read, so this is undefined for them.
798
866
  * @example
799
- * const currentAccessToken = sdk.auth.getCurrentToken();
867
+ * const token = sdk.auth.getCurrentToken();
800
868
  */
801
869
  service.getCurrentToken = function() {
802
870