@benmore/bm 2.7.312 → 2.7.313
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/index.js +49 -1
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -45,7 +45,7 @@ async function request(method, path, body, options) {
|
|
|
45
45
|
}
|
|
46
46
|
}
|
|
47
47
|
observeSessionResponse(method, path, res.ok, data);
|
|
48
|
-
return { ok: res.ok, status: res.status, data };
|
|
48
|
+
return { ok: res.ok, status: res.status, data, redirected: !!res.redirected, url: res.url || '' };
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
// apiError builds the Error a failed request throws. The framework's JSON
|
|
@@ -200,9 +200,51 @@ async function refreshMe() {
|
|
|
200
200
|
async function signIn(identifier, password) {
|
|
201
201
|
const res = await request('POST', '/login', { email: identifier, password });
|
|
202
202
|
if (!res.ok) throw apiError(res, 'Invalid credentials');
|
|
203
|
+
const step = signInStepURL('/login', res, _pageLocation());
|
|
204
|
+
if (step) return _goToSignInStep(step);
|
|
203
205
|
return res.data?.user || null;
|
|
204
206
|
}
|
|
205
207
|
|
|
208
|
+
// A sign-in or sign-up that still needs a second step (the emailed or texted
|
|
209
|
+
// code with auth.otp, an authenticator code with MFA) has not made a session:
|
|
210
|
+
// the server answers {otp_required: true, redirect} (or mfa_required), or
|
|
211
|
+
// redirects the request to the step's page. signInStepURL returns that page,
|
|
212
|
+
// same-origin and carrying the current page's ?next=, else null (also when
|
|
213
|
+
// there is no page: outside a browser the calls resolve null as before).
|
|
214
|
+
// Callers send the browser there, as a form post would, and do not return: code written as
|
|
215
|
+
// `await bm.auth.signIn(...); location.href = '/'` would otherwise leave the
|
|
216
|
+
// person on a page with no session and the code already emailed.
|
|
217
|
+
function signInStepURL(path, res, loc) {
|
|
218
|
+
if (!loc || typeof loc.assign !== 'function') return null;
|
|
219
|
+
const data = res && res.data;
|
|
220
|
+
let to = '';
|
|
221
|
+
if (data && typeof data === 'object' && (data.otp_required || data.mfa_required) && typeof data.redirect === 'string') {
|
|
222
|
+
to = data.redirect;
|
|
223
|
+
} else if (res && res.redirected && typeof res.url === 'string' && loc.origin && res.url.startsWith(loc.origin + '/')) {
|
|
224
|
+
const target = res.url.slice(loc.origin.length);
|
|
225
|
+
if (target.split(/[?#]/, 1)[0] !== String(path).split(/[?#]/, 1)[0]) to = target;
|
|
226
|
+
}
|
|
227
|
+
if (!to || to[0] !== '/' || to[1] === '/' || to[1] === '\\') return null;
|
|
228
|
+
const m = /[?&]next=([^&#]*)/.exec(loc.search || '');
|
|
229
|
+
if (m && !/[?&]next=/.test(to)) {
|
|
230
|
+
let next = '';
|
|
231
|
+
try { next = decodeURIComponent(m[1].replace(/\+/g, ' ')); } catch { next = ''; }
|
|
232
|
+
if (next[0] === '/' && next[1] !== '/' && next[1] !== '\\') to += (to.includes('?') ? '&' : '?') + 'next=' + encodeURIComponent(next);
|
|
233
|
+
}
|
|
234
|
+
return to;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
function _pageLocation() {
|
|
238
|
+
return typeof location === 'undefined' ? null : location;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// The page is leaving for the step; the caller's code after the await must
|
|
242
|
+
// not run, so the promise never settles.
|
|
243
|
+
function _goToSignInStep(url) {
|
|
244
|
+
location.assign(url);
|
|
245
|
+
return new Promise(() => {});
|
|
246
|
+
}
|
|
247
|
+
|
|
206
248
|
// signUp - POST /signup. Pass any extra signup_fields (configured in
|
|
207
249
|
// app.yaml's `auth.signup_fields`) as additional keys on the object:
|
|
208
250
|
// await bm.auth.signUp({ email, password, first_name: 'Ada' })
|
|
@@ -211,6 +253,8 @@ async function signUp(fields) {
|
|
|
211
253
|
if (!res.ok) {
|
|
212
254
|
throw apiError(res, 'Sign-up failed');
|
|
213
255
|
}
|
|
256
|
+
const step = signInStepURL('/signup', res, _pageLocation());
|
|
257
|
+
if (step) return _goToSignInStep(step);
|
|
214
258
|
return res.data?.user || null;
|
|
215
259
|
}
|
|
216
260
|
|
|
@@ -285,6 +329,10 @@ const passkey = {
|
|
|
285
329
|
userHandle: r.userHandle ? _bufToB64u(r.userHandle) : '',
|
|
286
330
|
},
|
|
287
331
|
}, 'Passkey sign-in failed');
|
|
332
|
+
// With auth.otp the passkey proves the account and the emailed code
|
|
333
|
+
// still has to be entered before there is a session.
|
|
334
|
+
const step = signInStepURL('/api/_auth/passkeys/login', { data }, _pageLocation());
|
|
335
|
+
if (step) return _goToSignInStep(step);
|
|
288
336
|
return data.user || null;
|
|
289
337
|
},
|
|
290
338
|
async list() {
|