@zoreal/oauth2-js 0.1.9 → 0.1.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +29 -10
- package/dist/index.cjs +224 -19
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +97 -16
- package/dist/index.d.ts +97 -16
- package/dist/index.js +223 -19
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -14,11 +14,12 @@ function unsafeClaims(idToken) {
|
|
|
14
14
|
|
|
15
15
|
// src/wire.ts
|
|
16
16
|
var WIRE_VERSION = 1;
|
|
17
|
-
var SDK_VERSION = "0.1.
|
|
17
|
+
var SDK_VERSION = "0.1.11";
|
|
18
18
|
var SDK_NAME = "@zoreal/oauth2-js";
|
|
19
19
|
var DEFAULT_ISSUER = "https://id.zoreal.com";
|
|
20
20
|
var POLL_INTERVAL_MS = 2e3;
|
|
21
21
|
var POLL_INTERVAL_ENROLLING_MS = 5e3;
|
|
22
|
+
var DEFAULT_QR_REFRESH_SECONDS = 3;
|
|
22
23
|
|
|
23
24
|
// src/pairing.ts
|
|
24
25
|
var OAuthFlowError = class extends Error {
|
|
@@ -67,11 +68,15 @@ var sleep = (ms, signal) => new Promise((resolve, reject) => {
|
|
|
67
68
|
reject(new DOMException("aborted", "AbortError"));
|
|
68
69
|
return;
|
|
69
70
|
}
|
|
70
|
-
const
|
|
71
|
-
signal?.addEventListener("abort", () => {
|
|
71
|
+
const onAbort = () => {
|
|
72
72
|
clearTimeout(t);
|
|
73
73
|
reject(new DOMException("aborted", "AbortError"));
|
|
74
|
-
}
|
|
74
|
+
};
|
|
75
|
+
const t = setTimeout(() => {
|
|
76
|
+
signal?.removeEventListener("abort", onAbort);
|
|
77
|
+
resolve();
|
|
78
|
+
}, ms);
|
|
79
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
75
80
|
});
|
|
76
81
|
async function pollUntilApproved(issuer, requestId, onState, signal) {
|
|
77
82
|
for (; ; ) {
|
|
@@ -962,20 +967,110 @@ var CSS = `
|
|
|
962
967
|
color: var(--zrl-ink-soft);
|
|
963
968
|
}
|
|
964
969
|
|
|
970
|
+
/* The light around the well: a thin ring in the accent, one bright arc
|
|
971
|
+
travelling round it, and a soft glow of the same colour bleeding a few
|
|
972
|
+
pixels outward. It says the pairing is live without a word.
|
|
973
|
+
|
|
974
|
+
It sits BELOW the well, which is why the well is wrapped instead of given a
|
|
975
|
+
pseudo-element. Anything drawn inside the well paints over the white quiet
|
|
976
|
+
zone the camera needs, and a negative z-index from inside the well lands
|
|
977
|
+
wherever the card's stacking context happens to be that frame (the card's
|
|
978
|
+
entrance animation makes and unmakes one). The wrapper is the well's old
|
|
979
|
+
box exactly, same size, same margin, so nothing moves; the well keeps its
|
|
980
|
+
own stacking order on top, and the ring cannot touch the QR. */
|
|
981
|
+
.${PREFIX}-qr-halo {
|
|
982
|
+
position: relative;
|
|
983
|
+
isolation: isolate;
|
|
984
|
+
width: 204px;
|
|
985
|
+
height: 204px;
|
|
986
|
+
margin: 20px auto 0;
|
|
987
|
+
}
|
|
988
|
+
|
|
965
989
|
.${PREFIX}-qr-well {
|
|
966
990
|
position: relative;
|
|
991
|
+
z-index: 1;
|
|
967
992
|
display: grid;
|
|
968
993
|
place-items: center;
|
|
969
994
|
box-sizing: border-box;
|
|
970
995
|
width: 204px;
|
|
971
996
|
height: 204px;
|
|
972
|
-
margin:
|
|
997
|
+
margin: 0;
|
|
973
998
|
padding: 12px;
|
|
974
999
|
border: 1px solid var(--zrl-line);
|
|
975
1000
|
border-radius: 16px;
|
|
976
1001
|
background: var(--zrl-qr-bg);
|
|
977
1002
|
}
|
|
978
1003
|
|
|
1004
|
+
/* The glow is the ring's own alpha, blurred and drawn beneath it, so it
|
|
1005
|
+
follows the arc round and fades with it. It has to be a parent of the
|
|
1006
|
+
masked band: a filter is applied before a mask, so a shadow cast from the
|
|
1007
|
+
band itself would be cut off at the band's edge. Two shadows because one
|
|
1008
|
+
2px source blurred over 10px is too faint to read as light; the second
|
|
1009
|
+
blurs the first. */
|
|
1010
|
+
.${PREFIX}-qr-glow {
|
|
1011
|
+
position: absolute;
|
|
1012
|
+
inset: -2px;
|
|
1013
|
+
pointer-events: none;
|
|
1014
|
+
filter: drop-shadow(0 0 3px var(--zrl-accent)) drop-shadow(0 0 10px var(--zrl-accent));
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1017
|
+
/* The band: a 2px ring just outside the well, cut with a mask that keeps the
|
|
1018
|
+
padding and drops the content box. The gradient is on a pseudo-element
|
|
1019
|
+
rather than on the band, because in the fallback it has to rotate as a
|
|
1020
|
+
whole while the band stays put. The prefixed mask is for Chrome before 120;
|
|
1021
|
+
the unprefixed one, declared after it, wins everywhere else. */
|
|
1022
|
+
.${PREFIX}-qr-arc {
|
|
1023
|
+
position: absolute;
|
|
1024
|
+
inset: 0;
|
|
1025
|
+
padding: 2px;
|
|
1026
|
+
border-radius: 18px;
|
|
1027
|
+
overflow: hidden;
|
|
1028
|
+
-webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
|
|
1029
|
+
-webkit-mask-composite: xor;
|
|
1030
|
+
mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
|
|
1031
|
+
mask-composite: exclude;
|
|
1032
|
+
}
|
|
1033
|
+
.${PREFIX}-qr-arc::before,
|
|
1034
|
+
.${PREFIX}-qr-arc::after {
|
|
1035
|
+
content: '';
|
|
1036
|
+
position: absolute;
|
|
1037
|
+
transition: opacity 400ms cubic-bezier(0.23, 1, 0.32, 1);
|
|
1038
|
+
}
|
|
1039
|
+
|
|
1040
|
+
/* The light. Mostly transparent, a tail that builds over a quarter turn, a
|
|
1041
|
+
short full-strength head and a drop-off within a few degrees, so it reads
|
|
1042
|
+
as travelling rather than blinking. One revolution every 3s, linear:
|
|
1043
|
+
continuous motion has no ease. Oversized so its corners still cover the
|
|
1044
|
+
band mid-rotation in the transform fallback; the property path shrinks it
|
|
1045
|
+
back to the band. */
|
|
1046
|
+
.${PREFIX}-qr-arc::before {
|
|
1047
|
+
inset: -50%;
|
|
1048
|
+
background: conic-gradient(
|
|
1049
|
+
from var(--zrl-angle, 0deg),
|
|
1050
|
+
transparent 0deg 245deg,
|
|
1051
|
+
var(--zrl-accent) 332deg 346deg,
|
|
1052
|
+
transparent 356deg 360deg
|
|
1053
|
+
);
|
|
1054
|
+
animation: ${PREFIX}-orbit 3s linear infinite;
|
|
1055
|
+
}
|
|
1056
|
+
|
|
1057
|
+
/* The rest state: the same ring, evenly lit. Faint under the moving light so
|
|
1058
|
+
the ring reads as a ring; stronger once the light has stopped. */
|
|
1059
|
+
.${PREFIX}-qr-arc::after {
|
|
1060
|
+
inset: 0;
|
|
1061
|
+
background: var(--zrl-accent);
|
|
1062
|
+
opacity: 0.16;
|
|
1063
|
+
}
|
|
1064
|
+
|
|
1065
|
+
/* Spent: the light stops where it is and fades, and the ring settles to a
|
|
1066
|
+
dim, even glow. Paused rather than removed, so it does not jump back to
|
|
1067
|
+
its start on the way out. */
|
|
1068
|
+
.${PREFIX}-qr-halo[data-spent="true"] .${PREFIX}-qr-arc::before {
|
|
1069
|
+
animation-play-state: paused;
|
|
1070
|
+
opacity: 0;
|
|
1071
|
+
}
|
|
1072
|
+
.${PREFIX}-qr-halo[data-spent="true"] .${PREFIX}-qr-arc::after { opacity: 0.34; }
|
|
1073
|
+
|
|
979
1074
|
.${PREFIX}-qr {
|
|
980
1075
|
display: block;
|
|
981
1076
|
width: 100%;
|
|
@@ -1118,6 +1213,33 @@ var CSS = `
|
|
|
1118
1213
|
70%, 100% { transform: scale(2.6); opacity: 0 }
|
|
1119
1214
|
}
|
|
1120
1215
|
|
|
1216
|
+
/* Two ways to turn the light, one look.
|
|
1217
|
+
Where the browser can register a typed custom property, the gradient's own
|
|
1218
|
+
angle animates and the element never moves. Where it cannot (Firefox before
|
|
1219
|
+
128), an untyped custom property cannot interpolate and the light would
|
|
1220
|
+
snap once a cycle, so the oversized pseudo-element rotates instead and the
|
|
1221
|
+
band's mask keeps the ring in place. At rest the two are the same pixels:
|
|
1222
|
+
same gradient, same centre, angle zero.
|
|
1223
|
+
There is no direct query for @property support. transition-behavior
|
|
1224
|
+
shipped after it in every engine (Chrome 117 vs 85, Firefox 129 vs 128,
|
|
1225
|
+
Safari 17.4 vs 16.4), so it stands in: a yes is never wrong, and a no only
|
|
1226
|
+
sends a capable browser down the fallback, which looks the same.
|
|
1227
|
+
inherits: false so the angle stays on the element that animates it and
|
|
1228
|
+
never reaches the host page or the well. */
|
|
1229
|
+
@property --zrl-angle {
|
|
1230
|
+
syntax: '<angle>';
|
|
1231
|
+
inherits: false;
|
|
1232
|
+
initial-value: 0deg;
|
|
1233
|
+
}
|
|
1234
|
+
@keyframes ${PREFIX}-orbit { to { transform: rotate(360deg) } }
|
|
1235
|
+
@keyframes ${PREFIX}-orbit-angle { to { --zrl-angle: 360deg } }
|
|
1236
|
+
@supports (transition-behavior: allow-discrete) {
|
|
1237
|
+
.${PREFIX}-qr-arc::before {
|
|
1238
|
+
inset: 0;
|
|
1239
|
+
animation-name: ${PREFIX}-orbit-angle;
|
|
1240
|
+
}
|
|
1241
|
+
}
|
|
1242
|
+
|
|
1121
1243
|
@media (prefers-reduced-motion: reduce) {
|
|
1122
1244
|
.${PREFIX}-scrim,
|
|
1123
1245
|
.${PREFIX}-card,
|
|
@@ -1127,6 +1249,10 @@ var CSS = `
|
|
|
1127
1249
|
.${PREFIX}-cancel,
|
|
1128
1250
|
.${PREFIX}-close,
|
|
1129
1251
|
.${PREFIX}-timer { transition: none }
|
|
1252
|
+
/* No travelling light. The ring keeps a still, soft glow instead, a step
|
|
1253
|
+
brighter than the spent state so pending and spent still read apart. */
|
|
1254
|
+
.${PREFIX}-qr-arc::before { animation: none; opacity: 0 }
|
|
1255
|
+
.${PREFIX}-qr-arc::after { opacity: 0.5 }
|
|
1130
1256
|
}
|
|
1131
1257
|
`;
|
|
1132
1258
|
function ensureStyles() {
|
|
@@ -1231,6 +1357,10 @@ function mountPairingModal(state, options) {
|
|
|
1231
1357
|
const title = el("h2", cx("title"));
|
|
1232
1358
|
title.id = titleId;
|
|
1233
1359
|
const bodyText = el("p", cx("body-text"));
|
|
1360
|
+
const halo = el("div", cx("qr-halo"));
|
|
1361
|
+
const glow = el("span", cx("qr-glow"));
|
|
1362
|
+
glow.setAttribute("aria-hidden", "true");
|
|
1363
|
+
glow.appendChild(el("span", cx("qr-arc")));
|
|
1234
1364
|
const well = el("div", cx("qr-well"));
|
|
1235
1365
|
const qr = el("img", cx("qr"));
|
|
1236
1366
|
qr.alt = t.qrAlt;
|
|
@@ -1242,13 +1372,14 @@ function mountPairingModal(state, options) {
|
|
|
1242
1372
|
badge.appendChild(strokeIcon(24, ["M8.5 2h7a2.5 2.5 0 0 1 2.5 2.5v15a2.5 2.5 0 0 1-2.5 2.5h-7A2.5 2.5 0 0 1 6 19.5v-15A2.5 2.5 0 0 1 8.5 2Z", "M11 18.5h2"], "1.8"));
|
|
1243
1373
|
overlay.appendChild(badge);
|
|
1244
1374
|
well.append(qr, overlay);
|
|
1375
|
+
halo.append(glow, well);
|
|
1245
1376
|
const status = el("div", cx("status"));
|
|
1246
1377
|
const dot = el("span", cx("dot"));
|
|
1247
1378
|
dot.append(el("i"), el("i"));
|
|
1248
1379
|
const statusLabel = el("span");
|
|
1249
1380
|
status.append(dot, statusLabel);
|
|
1250
1381
|
const timer = el("p", cx("timer"));
|
|
1251
|
-
body.append(mark, title, bodyText,
|
|
1382
|
+
body.append(mark, title, bodyText, halo, status, timer);
|
|
1252
1383
|
const help = el("div", cx("help"));
|
|
1253
1384
|
help.append(el("p", cx("help-title"), t.noIdTitle), el("p", cx("help-body"), t.noIdBody));
|
|
1254
1385
|
const footer = el("div", cx("footer"));
|
|
@@ -1261,14 +1392,38 @@ function mountPairingModal(state, options) {
|
|
|
1261
1392
|
scrim.appendChild(card);
|
|
1262
1393
|
const serverMs = typeof state.expiresIn === "number" ? state.expiresIn * 1e3 : Infinity;
|
|
1263
1394
|
const deadline = Date.now() + Math.min(timeoutMs, serverMs);
|
|
1395
|
+
let shown = state.qrUrl;
|
|
1396
|
+
let loading = null;
|
|
1397
|
+
const spent = () => qr.dataset.spent === "true";
|
|
1398
|
+
const showFrame = (url) => {
|
|
1399
|
+
if (url === shown || spent()) return;
|
|
1400
|
+
if (!shown) {
|
|
1401
|
+
qr.src = url;
|
|
1402
|
+
shown = url;
|
|
1403
|
+
return;
|
|
1404
|
+
}
|
|
1405
|
+
const next = new Image();
|
|
1406
|
+
loading = next;
|
|
1407
|
+
next.onload = () => {
|
|
1408
|
+
if (loading !== next || spent()) return;
|
|
1409
|
+
loading = null;
|
|
1410
|
+
qr.src = url;
|
|
1411
|
+
shown = url;
|
|
1412
|
+
};
|
|
1413
|
+
next.onerror = () => {
|
|
1414
|
+
if (loading === next) loading = null;
|
|
1415
|
+
};
|
|
1416
|
+
next.src = url;
|
|
1417
|
+
};
|
|
1264
1418
|
const paint = (s) => {
|
|
1265
1419
|
const settled = s.status === "claimed" || s.status === "enrolling";
|
|
1266
1420
|
title.textContent = settled ? t.titleApprove : t.title;
|
|
1267
1421
|
bodyText.textContent = s.status === "enrolling" ? t.bodyEnrolling : settled ? t.bodyApprove : t.bodyScan;
|
|
1268
1422
|
statusLabel.textContent = settled ? t.waitingApproval : t.waiting;
|
|
1269
1423
|
qr.dataset.spent = String(settled);
|
|
1424
|
+
halo.dataset.spent = String(settled);
|
|
1270
1425
|
overlay.style.display = settled ? "" : "none";
|
|
1271
|
-
if (s.qrUrl
|
|
1426
|
+
if (s.qrUrl) showFrame(s.qrUrl);
|
|
1272
1427
|
};
|
|
1273
1428
|
const tick = () => {
|
|
1274
1429
|
const left = Math.max(0, Math.ceil((deadline - Date.now()) / 1e3));
|
|
@@ -1285,6 +1440,7 @@ function mountPairingModal(state, options) {
|
|
|
1285
1440
|
const close = () => {
|
|
1286
1441
|
if (closed) return;
|
|
1287
1442
|
closed = true;
|
|
1443
|
+
loading = null;
|
|
1288
1444
|
window.clearInterval(interval);
|
|
1289
1445
|
document.removeEventListener("keydown", onKey);
|
|
1290
1446
|
document.body.style.overflow = previousOverflow;
|
|
@@ -1334,10 +1490,15 @@ function startLogin(options) {
|
|
|
1334
1490
|
const flow = options.flow ?? "browser-direct";
|
|
1335
1491
|
const issuer = options.issuer ?? DEFAULT_ISSUER;
|
|
1336
1492
|
const controller = new AbortController();
|
|
1493
|
+
const useAppLink = options.display === "link" || options.display !== "qr" && isMobileUserAgent();
|
|
1337
1494
|
const surface = {};
|
|
1338
1495
|
const cancel = () => controller.abort();
|
|
1339
1496
|
let modal = null;
|
|
1340
|
-
|
|
1497
|
+
let stopRefresh = () => {
|
|
1498
|
+
};
|
|
1499
|
+
controller.signal.addEventListener("abort", () => stopRefresh());
|
|
1500
|
+
const teardown = () => {
|
|
1501
|
+
stopRefresh();
|
|
1341
1502
|
modal?.close();
|
|
1342
1503
|
modal = null;
|
|
1343
1504
|
};
|
|
@@ -1358,7 +1519,8 @@ function startLogin(options) {
|
|
|
1358
1519
|
acr_values: Array.isArray(options.acr_values) ? options.acr_values.join(" ") : options.acr_values,
|
|
1359
1520
|
max_age: options.max_age,
|
|
1360
1521
|
prompt: options.prompt,
|
|
1361
|
-
locale: options.locale
|
|
1522
|
+
locale: options.locale,
|
|
1523
|
+
display: useAppLink ? "link" : "qr"
|
|
1362
1524
|
},
|
|
1363
1525
|
controller.signal
|
|
1364
1526
|
);
|
|
@@ -1368,19 +1530,25 @@ function startLogin(options) {
|
|
|
1368
1530
|
code = started.code;
|
|
1369
1531
|
selectBy = "session";
|
|
1370
1532
|
} else {
|
|
1371
|
-
const useAppLink = options.display === "link" || options.display !== "qr" && isMobileUserAgent();
|
|
1372
1533
|
selectBy = useAppLink ? "app_link" : "qr";
|
|
1373
|
-
|
|
1534
|
+
const requestId = started.request_id;
|
|
1535
|
+
const qrBase = `${issuer}/pair/${encodeURIComponent(requestId)}/qr.svg`;
|
|
1536
|
+
const animated = !useAppLink && started.display !== "legacy";
|
|
1537
|
+
const qrRefreshSeconds = !animated ? void 0 : typeof started.qr_refresh_seconds === "number" && started.qr_refresh_seconds > 0 ? started.qr_refresh_seconds : DEFAULT_QR_REFRESH_SECONDS;
|
|
1538
|
+
surface.requestId = requestId;
|
|
1374
1539
|
surface.pairUrl = started.pair_url;
|
|
1375
|
-
surface.qrUrl =
|
|
1540
|
+
surface.qrUrl = qrBase;
|
|
1376
1541
|
surface.appLink = useAppLink;
|
|
1377
|
-
const
|
|
1542
|
+
const withSurface = (s) => ({
|
|
1543
|
+
...s,
|
|
1378
1544
|
pairUrl: surface.pairUrl,
|
|
1379
1545
|
qrUrl: surface.qrUrl,
|
|
1546
|
+
qrRefreshSeconds,
|
|
1380
1547
|
appLink: useAppLink,
|
|
1381
1548
|
cancel
|
|
1382
|
-
};
|
|
1383
|
-
|
|
1549
|
+
});
|
|
1550
|
+
let lastPolled = { status: "pending", expiresIn: started.expires_in };
|
|
1551
|
+
const initial = withSurface(lastPolled);
|
|
1384
1552
|
options.onState?.(initial);
|
|
1385
1553
|
if ((options.pairingUI ?? "modal") === "modal" && !useAppLink) {
|
|
1386
1554
|
modal = mountPairingModal(initial, {
|
|
@@ -1393,18 +1561,53 @@ function startLogin(options) {
|
|
|
1393
1561
|
if (useAppLink && typeof window !== "undefined") {
|
|
1394
1562
|
window.location.assign(started.pair_url);
|
|
1395
1563
|
}
|
|
1564
|
+
if (qrRefreshSeconds !== void 0) {
|
|
1565
|
+
const periodMs = qrRefreshSeconds * 1e3;
|
|
1566
|
+
let due = Date.now() + periodMs;
|
|
1567
|
+
let timer;
|
|
1568
|
+
let stopped = false;
|
|
1569
|
+
const emit = () => {
|
|
1570
|
+
timer = void 0;
|
|
1571
|
+
surface.qrUrl = `${qrBase}?t=${Date.now()}`;
|
|
1572
|
+
const next = withSurface(lastPolled);
|
|
1573
|
+
modal?.update(next);
|
|
1574
|
+
options.onState?.(next);
|
|
1575
|
+
if (stopped) return;
|
|
1576
|
+
due = Date.now() + periodMs;
|
|
1577
|
+
timer = setTimeout(emit, periodMs);
|
|
1578
|
+
};
|
|
1579
|
+
const onVisible = () => {
|
|
1580
|
+
if (document.visibilityState === "visible" && timer !== void 0 && Date.now() >= due) {
|
|
1581
|
+
clearTimeout(timer);
|
|
1582
|
+
emit();
|
|
1583
|
+
}
|
|
1584
|
+
};
|
|
1585
|
+
const hasDocument = typeof document !== "undefined";
|
|
1586
|
+
if (hasDocument) document.addEventListener("visibilitychange", onVisible);
|
|
1587
|
+
stopRefresh = () => {
|
|
1588
|
+
stopped = true;
|
|
1589
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
1590
|
+
timer = void 0;
|
|
1591
|
+
if (hasDocument) document.removeEventListener("visibilitychange", onVisible);
|
|
1592
|
+
stopRefresh = () => {
|
|
1593
|
+
};
|
|
1594
|
+
};
|
|
1595
|
+
timer = setTimeout(emit, Math.max(0, due - Date.now()));
|
|
1596
|
+
}
|
|
1396
1597
|
code = await pollUntilApproved(
|
|
1397
1598
|
issuer,
|
|
1398
|
-
|
|
1599
|
+
requestId,
|
|
1399
1600
|
(s) => {
|
|
1400
|
-
|
|
1601
|
+
lastPolled = s;
|
|
1602
|
+
if (s.status !== "pending") stopRefresh();
|
|
1603
|
+
const next = withSurface(s);
|
|
1401
1604
|
modal?.update(next);
|
|
1402
1605
|
options.onState?.(next);
|
|
1403
1606
|
},
|
|
1404
1607
|
controller.signal
|
|
1405
1608
|
);
|
|
1406
1609
|
}
|
|
1407
|
-
|
|
1610
|
+
teardown();
|
|
1408
1611
|
if (flow === "auth-code") {
|
|
1409
1612
|
const response2 = {
|
|
1410
1613
|
code,
|
|
@@ -1429,7 +1632,7 @@ function startLogin(options) {
|
|
|
1429
1632
|
};
|
|
1430
1633
|
return response;
|
|
1431
1634
|
} catch (e) {
|
|
1432
|
-
|
|
1635
|
+
teardown();
|
|
1433
1636
|
if (e instanceof DOMException && e.name === "AbortError") throw e;
|
|
1434
1637
|
if (e instanceof OAuthFlowError || e instanceof FlowAbandonedError) throw e;
|
|
1435
1638
|
throw new FlowAbandonedError({
|
|
@@ -1461,6 +1664,7 @@ function startLogin(options) {
|
|
|
1461
1664
|
export {
|
|
1462
1665
|
DEFAULT_ISSUER,
|
|
1463
1666
|
DEFAULT_PAIRING_TIMEOUT_MS,
|
|
1667
|
+
DEFAULT_QR_REFRESH_SECONDS,
|
|
1464
1668
|
FlowAbandonedError,
|
|
1465
1669
|
OAuthFlowError,
|
|
1466
1670
|
POLL_INTERVAL_ENROLLING_MS,
|