@zoreal/oauth2-react 0.2.15 → 0.2.17
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 +1 -1
- package/dist/index.cjs +311 -71
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +0 -19
- package/dist/index.d.ts +0 -19
- package/dist/index.js +314 -74
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -243,25 +243,6 @@ declare function useZorealOAuth(): ZorealOAuthContextProps;
|
|
|
243
243
|
|
|
244
244
|
declare function ZorealLogin(props: ZorealLoginProps): react.JSX.Element;
|
|
245
245
|
|
|
246
|
-
/**
|
|
247
|
-
* The light of the pairing dialog's QR well, run around whatever this wraps
|
|
248
|
-
* while `busy` is true: the well's colours, line, halo and lap, on a dash
|
|
249
|
-
* that travels the outline at one speed whatever the shape.
|
|
250
|
-
*
|
|
251
|
-
* The SDK's own button uses it for the gap on a phone between the tap and the
|
|
252
|
-
* hand-over to the app. A site with its own sign-in button MAY wrap that
|
|
253
|
-
* button the same way: set your busy state on the tap, clear it when
|
|
254
|
-
* `onSuccess` or `onError` fires. `radius` is the wrapped control's corner
|
|
255
|
-
* radius in pixels, so the light follows its shape; `theme` picks the light
|
|
256
|
-
* and dark strengths the dialog uses, following the OS by default.
|
|
257
|
-
*
|
|
258
|
-
* It is a wrapper, and a wrapper is a change to your markup, so know what it
|
|
259
|
-
* does before you use it: it is inline by default and shrinks to the button,
|
|
260
|
-
* so a full-width button needs `block`; the light sits outside the button,
|
|
261
|
-
* so an ancestor with `overflow: hidden` clips it; and a selector that relies
|
|
262
|
-
* on the button's parent (`.card > button`) no longer matches. A site that
|
|
263
|
-
* would rather draw its own busy state needs nothing from here.
|
|
264
|
-
*/
|
|
265
246
|
declare function ZorealBusyRing({ busy, radius, theme, block, className, style, children, }: {
|
|
266
247
|
busy: boolean;
|
|
267
248
|
radius?: number;
|
package/dist/index.d.ts
CHANGED
|
@@ -243,25 +243,6 @@ declare function useZorealOAuth(): ZorealOAuthContextProps;
|
|
|
243
243
|
|
|
244
244
|
declare function ZorealLogin(props: ZorealLoginProps): react.JSX.Element;
|
|
245
245
|
|
|
246
|
-
/**
|
|
247
|
-
* The light of the pairing dialog's QR well, run around whatever this wraps
|
|
248
|
-
* while `busy` is true: the well's colours, line, halo and lap, on a dash
|
|
249
|
-
* that travels the outline at one speed whatever the shape.
|
|
250
|
-
*
|
|
251
|
-
* The SDK's own button uses it for the gap on a phone between the tap and the
|
|
252
|
-
* hand-over to the app. A site with its own sign-in button MAY wrap that
|
|
253
|
-
* button the same way: set your busy state on the tap, clear it when
|
|
254
|
-
* `onSuccess` or `onError` fires. `radius` is the wrapped control's corner
|
|
255
|
-
* radius in pixels, so the light follows its shape; `theme` picks the light
|
|
256
|
-
* and dark strengths the dialog uses, following the OS by default.
|
|
257
|
-
*
|
|
258
|
-
* It is a wrapper, and a wrapper is a change to your markup, so know what it
|
|
259
|
-
* does before you use it: it is inline by default and shrinks to the button,
|
|
260
|
-
* so a full-width button needs `block`; the light sits outside the button,
|
|
261
|
-
* so an ancestor with `overflow: hidden` clips it; and a selector that relies
|
|
262
|
-
* on the button's parent (`.card > button`) no longer matches. A site that
|
|
263
|
-
* would rather draw its own busy state needs nothing from here.
|
|
264
|
-
*/
|
|
265
246
|
declare function ZorealBusyRing({ busy, radius, theme, block, className, style, children, }: {
|
|
266
247
|
busy: boolean;
|
|
267
248
|
radius?: number;
|
package/dist/index.js
CHANGED
|
@@ -5,7 +5,7 @@ import { createContext, useContext, useMemo, useState as useState2 } from "react
|
|
|
5
5
|
|
|
6
6
|
// src/wire.ts
|
|
7
7
|
var WIRE_VERSION = 1;
|
|
8
|
-
var SDK_VERSION = "0.2.
|
|
8
|
+
var SDK_VERSION = "0.2.17";
|
|
9
9
|
var DEFAULT_ISSUER = "https://id.zoreal.com";
|
|
10
10
|
var POLL_INTERVAL_MS = 2e3;
|
|
11
11
|
var POLL_INTERVAL_ENROLLING_MS = 5e3;
|
|
@@ -1334,10 +1334,15 @@ var CSS = `
|
|
|
1334
1334
|
sides and lights two edges at once near the ends. So here the light is a
|
|
1335
1335
|
dash on an SVG outline, which moves at one speed the whole way round
|
|
1336
1336
|
whatever the shape, drawn with the well's tokens: its colour and head
|
|
1337
|
-
tint, its line width, its halo, its four second lap.
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1337
|
+
tint, its line width, its halo, its four second lap. The outline's length
|
|
1338
|
+
is measured by the component and set as --zrl-ring-len, and every dash
|
|
1339
|
+
and offset is a fraction of it, because pathLength does not scale dash
|
|
1340
|
+
values given from CSS. A stroke cannot fade along its length, so the tail
|
|
1341
|
+
is a stack of dashes sharing one head, each shorter and more opaque than
|
|
1342
|
+
the one under it, with opacities chosen so the stack composes to a
|
|
1343
|
+
straight fade from the head to nothing three tenths of the way back; the
|
|
1344
|
+
component sets each layer's length, offset and opacity. Shown only while
|
|
1345
|
+
busy. */
|
|
1341
1346
|
.${PREFIX}-ring {
|
|
1342
1347
|
position: relative;
|
|
1343
1348
|
display: inline-flex;
|
|
@@ -1356,6 +1361,7 @@ var CSS = `
|
|
|
1356
1361
|
}
|
|
1357
1362
|
.${PREFIX}-ring[data-busy="true"] > .${PREFIX}-ring-svg { opacity: 1; }
|
|
1358
1363
|
.${PREFIX}-ring-svg rect {
|
|
1364
|
+
--zrl-l: var(--zrl-ring-len, 600px);
|
|
1359
1365
|
x: 2px;
|
|
1360
1366
|
y: 2px;
|
|
1361
1367
|
width: calc(100% - 4px);
|
|
@@ -1364,23 +1370,18 @@ var CSS = `
|
|
|
1364
1370
|
stroke: var(--zrl-beam);
|
|
1365
1371
|
stroke-width: var(--zrl-beam-line);
|
|
1366
1372
|
stroke-linecap: round;
|
|
1373
|
+
stroke-dashoffset: var(--zrl-s, 0px);
|
|
1367
1374
|
animation: ${PREFIX}-dash var(--zrl-beam-time) linear infinite;
|
|
1368
1375
|
}
|
|
1369
|
-
.${PREFIX}-ring-
|
|
1370
|
-
.${PREFIX}-ring-body { stroke-dasharray: 16 84; stroke-dashoffset: -14; opacity: 0.8; }
|
|
1371
|
-
.${PREFIX}-ring-head { stroke-dasharray: 5 95; stroke-dashoffset: -25; stroke: var(--zrl-beam-head); }
|
|
1376
|
+
.${PREFIX}-ring-head { stroke: var(--zrl-beam-head); }
|
|
1372
1377
|
.${PREFIX}-ring-halo {
|
|
1373
|
-
stroke-dasharray: 30 70;
|
|
1374
|
-
stroke-dashoffset: 0;
|
|
1375
1378
|
stroke-width: calc(var(--zrl-glow-core) * 2 + var(--zrl-beam-line));
|
|
1376
|
-
opacity: calc(var(--zrl-glow-opacity) * 0.5);
|
|
1377
1379
|
filter: blur(var(--zrl-glow-blur));
|
|
1378
1380
|
}
|
|
1379
1381
|
@keyframes ${PREFIX}-dash {
|
|
1380
|
-
|
|
1382
|
+
from { stroke-dashoffset: var(--zrl-s, 0px); }
|
|
1383
|
+
to { stroke-dashoffset: calc(var(--zrl-s, 0px) - var(--zrl-l)); }
|
|
1381
1384
|
}
|
|
1382
|
-
.${PREFIX}-ring-body { --zrl-dash-start: -14; }
|
|
1383
|
-
.${PREFIX}-ring-head { --zrl-dash-start: -25; }
|
|
1384
1385
|
@media (prefers-reduced-motion: reduce) {
|
|
1385
1386
|
.${PREFIX}-ring-svg rect { animation: none; stroke-dasharray: none; opacity: 0.45; }
|
|
1386
1387
|
.${PREFIX}-ring-halo, .${PREFIX}-ring-head { display: none; }
|
|
@@ -1654,6 +1655,20 @@ async function startPairing(issuer, params) {
|
|
|
1654
1655
|
}
|
|
1655
1656
|
return body;
|
|
1656
1657
|
}
|
|
1658
|
+
function sameDeviceStartUrl(issuer, params) {
|
|
1659
|
+
const query = new URLSearchParams();
|
|
1660
|
+
const all = {
|
|
1661
|
+
...params,
|
|
1662
|
+
code_challenge_method: "S256",
|
|
1663
|
+
wire_version: WIRE_VERSION,
|
|
1664
|
+
sdk: `@zoreal/oauth2-react/${SDK_VERSION}`
|
|
1665
|
+
};
|
|
1666
|
+
for (const [key, value] of Object.entries(all)) {
|
|
1667
|
+
if (value === void 0 || value === null || value === "") continue;
|
|
1668
|
+
query.set(key, String(value));
|
|
1669
|
+
}
|
|
1670
|
+
return `${issuer}/pair/start?${query.toString()}`;
|
|
1671
|
+
}
|
|
1657
1672
|
function qrRefreshSecondsOf(started) {
|
|
1658
1673
|
const seconds = started.qr_refresh_seconds;
|
|
1659
1674
|
return typeof seconds === "number" && seconds > 0 ? seconds : DEFAULT_QR_REFRESH_SECONDS;
|
|
@@ -1710,6 +1725,11 @@ async function pollUntilApproved(issuer, requestId, onState, signal, options = {
|
|
|
1710
1725
|
signal
|
|
1711
1726
|
});
|
|
1712
1727
|
const body = await parseJson(response);
|
|
1728
|
+
if (response.status === 404 && (options.tolerateUnknownUntil ?? 0) > Date.now()) {
|
|
1729
|
+
emit({ status: "pending" });
|
|
1730
|
+
await sleep(POLL_INTERVAL_MS, signal);
|
|
1731
|
+
continue;
|
|
1732
|
+
}
|
|
1713
1733
|
if (!response.ok) {
|
|
1714
1734
|
throw new OAuthFlowError(
|
|
1715
1735
|
body.error ?? "server_error",
|
|
@@ -1791,11 +1811,154 @@ async function challengeS256(verifier) {
|
|
|
1791
1811
|
const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(verifier));
|
|
1792
1812
|
return base64url(new Uint8Array(digest));
|
|
1793
1813
|
}
|
|
1814
|
+
function challengeS256Sync(verifier) {
|
|
1815
|
+
return base64url(sha256(new TextEncoder().encode(verifier)));
|
|
1816
|
+
}
|
|
1817
|
+
var K = new Uint32Array([
|
|
1818
|
+
1116352408,
|
|
1819
|
+
1899447441,
|
|
1820
|
+
3049323471,
|
|
1821
|
+
3921009573,
|
|
1822
|
+
961987163,
|
|
1823
|
+
1508970993,
|
|
1824
|
+
2453635748,
|
|
1825
|
+
2870763221,
|
|
1826
|
+
3624381080,
|
|
1827
|
+
310598401,
|
|
1828
|
+
607225278,
|
|
1829
|
+
1426881987,
|
|
1830
|
+
1925078388,
|
|
1831
|
+
2162078206,
|
|
1832
|
+
2614888103,
|
|
1833
|
+
3248222580,
|
|
1834
|
+
3835390401,
|
|
1835
|
+
4022224774,
|
|
1836
|
+
264347078,
|
|
1837
|
+
604807628,
|
|
1838
|
+
770255983,
|
|
1839
|
+
1249150122,
|
|
1840
|
+
1555081692,
|
|
1841
|
+
1996064986,
|
|
1842
|
+
2554220882,
|
|
1843
|
+
2821834349,
|
|
1844
|
+
2952996808,
|
|
1845
|
+
3210313671,
|
|
1846
|
+
3336571891,
|
|
1847
|
+
3584528711,
|
|
1848
|
+
113926993,
|
|
1849
|
+
338241895,
|
|
1850
|
+
666307205,
|
|
1851
|
+
773529912,
|
|
1852
|
+
1294757372,
|
|
1853
|
+
1396182291,
|
|
1854
|
+
1695183700,
|
|
1855
|
+
1986661051,
|
|
1856
|
+
2177026350,
|
|
1857
|
+
2456956037,
|
|
1858
|
+
2730485921,
|
|
1859
|
+
2820302411,
|
|
1860
|
+
3259730800,
|
|
1861
|
+
3345764771,
|
|
1862
|
+
3516065817,
|
|
1863
|
+
3600352804,
|
|
1864
|
+
4094571909,
|
|
1865
|
+
275423344,
|
|
1866
|
+
430227734,
|
|
1867
|
+
506948616,
|
|
1868
|
+
659060556,
|
|
1869
|
+
883997877,
|
|
1870
|
+
958139571,
|
|
1871
|
+
1322822218,
|
|
1872
|
+
1537002063,
|
|
1873
|
+
1747873779,
|
|
1874
|
+
1955562222,
|
|
1875
|
+
2024104815,
|
|
1876
|
+
2227730452,
|
|
1877
|
+
2361852424,
|
|
1878
|
+
2428436474,
|
|
1879
|
+
2756734187,
|
|
1880
|
+
3204031479,
|
|
1881
|
+
3329325298
|
|
1882
|
+
]);
|
|
1883
|
+
var rotr = (x, n) => x >>> n | x << 32 - n;
|
|
1884
|
+
function sha256(message) {
|
|
1885
|
+
const H = new Uint32Array([
|
|
1886
|
+
1779033703,
|
|
1887
|
+
3144134277,
|
|
1888
|
+
1013904242,
|
|
1889
|
+
2773480762,
|
|
1890
|
+
1359893119,
|
|
1891
|
+
2600822924,
|
|
1892
|
+
528734635,
|
|
1893
|
+
1541459225
|
|
1894
|
+
]);
|
|
1895
|
+
const length = message.length;
|
|
1896
|
+
const padded = new Uint8Array(length + 9 + 63 >> 6 << 6);
|
|
1897
|
+
padded.set(message);
|
|
1898
|
+
padded[length] = 128;
|
|
1899
|
+
const view = new DataView(padded.buffer);
|
|
1900
|
+
const bits = length * 8;
|
|
1901
|
+
view.setUint32(padded.length - 8, Math.floor(bits / 4294967296));
|
|
1902
|
+
view.setUint32(padded.length - 4, bits >>> 0);
|
|
1903
|
+
const W = new Uint32Array(64);
|
|
1904
|
+
for (let offset = 0; offset < padded.length; offset += 64) {
|
|
1905
|
+
for (let i = 0; i < 16; i++) W[i] = view.getUint32(offset + i * 4);
|
|
1906
|
+
for (let i = 16; i < 64; i++) {
|
|
1907
|
+
const w15 = W[i - 15];
|
|
1908
|
+
const w2 = W[i - 2];
|
|
1909
|
+
const s0 = rotr(w15, 7) ^ rotr(w15, 18) ^ w15 >>> 3;
|
|
1910
|
+
const s1 = rotr(w2, 17) ^ rotr(w2, 19) ^ w2 >>> 10;
|
|
1911
|
+
W[i] = W[i - 16] + s0 + W[i - 7] + s1 >>> 0;
|
|
1912
|
+
}
|
|
1913
|
+
let [a, b, c, d, e, f, g, h] = H;
|
|
1914
|
+
for (let i = 0; i < 64; i++) {
|
|
1915
|
+
const S1 = rotr(e, 6) ^ rotr(e, 11) ^ rotr(e, 25);
|
|
1916
|
+
const ch = e & f ^ ~e & g;
|
|
1917
|
+
const t1 = h + S1 + ch + K[i] + W[i] >>> 0;
|
|
1918
|
+
const S0 = rotr(a, 2) ^ rotr(a, 13) ^ rotr(a, 22);
|
|
1919
|
+
const maj = a & b ^ a & c ^ b & c;
|
|
1920
|
+
const t2 = S0 + maj >>> 0;
|
|
1921
|
+
h = g;
|
|
1922
|
+
g = f;
|
|
1923
|
+
f = e;
|
|
1924
|
+
e = d + t1 >>> 0;
|
|
1925
|
+
d = c;
|
|
1926
|
+
c = b;
|
|
1927
|
+
b = a;
|
|
1928
|
+
a = t1 + t2 >>> 0;
|
|
1929
|
+
}
|
|
1930
|
+
H[0] = H[0] + a >>> 0;
|
|
1931
|
+
H[1] = H[1] + b >>> 0;
|
|
1932
|
+
H[2] = H[2] + c >>> 0;
|
|
1933
|
+
H[3] = H[3] + d >>> 0;
|
|
1934
|
+
H[4] = H[4] + e >>> 0;
|
|
1935
|
+
H[5] = H[5] + f >>> 0;
|
|
1936
|
+
H[6] = H[6] + g >>> 0;
|
|
1937
|
+
H[7] = H[7] + h >>> 0;
|
|
1938
|
+
}
|
|
1939
|
+
const out = new Uint8Array(32);
|
|
1940
|
+
const outView = new DataView(out.buffer);
|
|
1941
|
+
for (let i = 0; i < 8; i++) outView.setUint32(i * 4, H[i]);
|
|
1942
|
+
return out;
|
|
1943
|
+
}
|
|
1794
1944
|
function generateState() {
|
|
1795
1945
|
const bytes = new Uint8Array(16);
|
|
1796
1946
|
crypto.getRandomValues(bytes);
|
|
1797
1947
|
return base64url(bytes);
|
|
1798
1948
|
}
|
|
1949
|
+
var TOKEN_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
|
1950
|
+
function generateRequestId() {
|
|
1951
|
+
let out = "";
|
|
1952
|
+
const bytes = new Uint8Array(64);
|
|
1953
|
+
while (out.length < 32) {
|
|
1954
|
+
crypto.getRandomValues(bytes);
|
|
1955
|
+
for (const byte of bytes) {
|
|
1956
|
+
if (byte >= 248 || out.length === 32) continue;
|
|
1957
|
+
out += TOKEN_ALPHABET[byte % 62];
|
|
1958
|
+
}
|
|
1959
|
+
}
|
|
1960
|
+
return out;
|
|
1961
|
+
}
|
|
1799
1962
|
|
|
1800
1963
|
// src/useZorealLogin.ts
|
|
1801
1964
|
function useZorealFlow(options) {
|
|
@@ -1828,76 +1991,119 @@ function useZorealFlow(options) {
|
|
|
1828
1991
|
const useAppLink = display === "link";
|
|
1829
1992
|
const intent = resolveIntent(opts.intent, opts.scope, opts.acr_values);
|
|
1830
1993
|
try {
|
|
1831
|
-
const started = await startPairing(issuer, {
|
|
1832
|
-
client_id: clientId,
|
|
1833
|
-
scope: opts.scope ?? "openid",
|
|
1834
|
-
state,
|
|
1835
|
-
nonce,
|
|
1836
|
-
code_challenge: await challengeS256(verifier),
|
|
1837
|
-
redirect_uri: flow === "auth-code" ? opts.redirect_uri : void 0,
|
|
1838
|
-
acr_values: Array.isArray(opts.acr_values) ? opts.acr_values.join(" ") : opts.acr_values,
|
|
1839
|
-
max_age: opts.max_age,
|
|
1840
|
-
prompt: opts.prompt,
|
|
1841
|
-
locale,
|
|
1842
|
-
display
|
|
1843
|
-
});
|
|
1844
1994
|
let code;
|
|
1845
1995
|
let selectBy = "device";
|
|
1846
|
-
if (
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1996
|
+
if (useAppLink) {
|
|
1997
|
+
const requestId = generateRequestId();
|
|
1998
|
+
const startUrl = sameDeviceStartUrl(issuer, {
|
|
1999
|
+
client_id: clientId,
|
|
2000
|
+
scope: opts.scope ?? "openid",
|
|
2001
|
+
state,
|
|
2002
|
+
nonce,
|
|
2003
|
+
code_challenge: challengeS256Sync(verifier),
|
|
2004
|
+
redirect_uri: flow === "auth-code" ? opts.redirect_uri : void 0,
|
|
2005
|
+
acr_values: Array.isArray(opts.acr_values) ? opts.acr_values.join(" ") : opts.acr_values,
|
|
2006
|
+
max_age: opts.max_age,
|
|
2007
|
+
prompt: opts.prompt,
|
|
2008
|
+
locale,
|
|
2009
|
+
request_id: requestId,
|
|
2010
|
+
origin: window.location.origin
|
|
2011
|
+
});
|
|
2012
|
+
selectBy = "app_link";
|
|
1852
2013
|
const cancel = () => {
|
|
1853
2014
|
controller.abort();
|
|
1854
2015
|
setPairing(null);
|
|
1855
|
-
publishRef.current?.(null);
|
|
1856
2016
|
};
|
|
1857
|
-
const surface = {
|
|
1858
|
-
pairUrl: started.pair_url,
|
|
1859
|
-
appLink: useAppLink,
|
|
1860
|
-
intent,
|
|
1861
|
-
cancel,
|
|
1862
|
-
// The app link has no QR and therefore no cadence to report.
|
|
1863
|
-
...useAppLink ? null : { qrRefreshSeconds }
|
|
1864
|
-
};
|
|
1865
|
-
let qrUrl = `${issuer}/pair/${encodeURIComponent(started.request_id)}/qr.svg`;
|
|
2017
|
+
const surface = { pairUrl: startUrl, appLink: true, intent, cancel };
|
|
1866
2018
|
const active = {
|
|
1867
|
-
requestId
|
|
1868
|
-
pairUrl:
|
|
1869
|
-
qrUrl,
|
|
1870
|
-
state: { status: "pending",
|
|
1871
|
-
appLink:
|
|
2019
|
+
requestId,
|
|
2020
|
+
pairUrl: startUrl,
|
|
2021
|
+
qrUrl: "",
|
|
2022
|
+
state: { status: "pending", ...surface },
|
|
2023
|
+
appLink: true,
|
|
1872
2024
|
cancel
|
|
1873
2025
|
};
|
|
1874
2026
|
setPairing(active);
|
|
1875
|
-
if (!useAppLink) {
|
|
1876
|
-
publishRef.current?.({ state: active.state, qrUrl, intent, cancel });
|
|
1877
|
-
}
|
|
1878
2027
|
opts.onPairingStateChange?.(active.state);
|
|
1879
|
-
|
|
1880
|
-
window.location.assign(started.pair_url);
|
|
1881
|
-
}
|
|
2028
|
+
window.location.assign(startUrl);
|
|
1882
2029
|
code = await pollUntilApproved(
|
|
1883
2030
|
issuer,
|
|
1884
|
-
|
|
2031
|
+
requestId,
|
|
1885
2032
|
(s) => {
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
setPairing(
|
|
1889
|
-
(p) => p && p.requestId === started.request_id ? { ...p, qrUrl, state: enriched } : p
|
|
1890
|
-
);
|
|
1891
|
-
if (!useAppLink) {
|
|
1892
|
-
publishRef.current?.({ state: enriched, qrUrl, intent, cancel });
|
|
1893
|
-
}
|
|
2033
|
+
const enriched = { ...s, ...surface };
|
|
2034
|
+
setPairing((p) => p && p.requestId === requestId ? { ...p, state: enriched } : p);
|
|
1894
2035
|
opts.onPairingStateChange?.(enriched);
|
|
1895
2036
|
},
|
|
1896
2037
|
controller.signal,
|
|
1897
|
-
|
|
1898
|
-
// for one, so the frames are asked for only on the QR surface.
|
|
1899
|
-
{ qrRefreshSeconds: useAppLink ? void 0 : qrRefreshSeconds }
|
|
2038
|
+
{ tolerateUnknownUntil: Date.now() + 15e3 }
|
|
1900
2039
|
);
|
|
2040
|
+
} else {
|
|
2041
|
+
const started = await startPairing(issuer, {
|
|
2042
|
+
client_id: clientId,
|
|
2043
|
+
scope: opts.scope ?? "openid",
|
|
2044
|
+
state,
|
|
2045
|
+
nonce,
|
|
2046
|
+
code_challenge: await challengeS256(verifier),
|
|
2047
|
+
redirect_uri: flow === "auth-code" ? opts.redirect_uri : void 0,
|
|
2048
|
+
acr_values: Array.isArray(opts.acr_values) ? opts.acr_values.join(" ") : opts.acr_values,
|
|
2049
|
+
max_age: opts.max_age,
|
|
2050
|
+
prompt: opts.prompt,
|
|
2051
|
+
locale,
|
|
2052
|
+
display: "qr"
|
|
2053
|
+
});
|
|
2054
|
+
if ("code" in started) {
|
|
2055
|
+
code = started.code;
|
|
2056
|
+
selectBy = "session";
|
|
2057
|
+
} else {
|
|
2058
|
+
selectBy = "qr";
|
|
2059
|
+
const qrRefreshSeconds = qrRefreshSecondsOf(started);
|
|
2060
|
+
const cancel = () => {
|
|
2061
|
+
controller.abort();
|
|
2062
|
+
setPairing(null);
|
|
2063
|
+
publishRef.current?.(null);
|
|
2064
|
+
};
|
|
2065
|
+
const surface = {
|
|
2066
|
+
pairUrl: started.pair_url,
|
|
2067
|
+
appLink: false,
|
|
2068
|
+
intent,
|
|
2069
|
+
cancel,
|
|
2070
|
+
qrRefreshSeconds
|
|
2071
|
+
};
|
|
2072
|
+
let qrUrl = `${issuer}/pair/${encodeURIComponent(started.request_id)}/qr.svg`;
|
|
2073
|
+
const active = {
|
|
2074
|
+
requestId: started.request_id,
|
|
2075
|
+
pairUrl: surface.pairUrl,
|
|
2076
|
+
qrUrl,
|
|
2077
|
+
state: { status: "pending", expiresIn: started.expires_in, qrUrl, ...surface },
|
|
2078
|
+
appLink: false,
|
|
2079
|
+
cancel
|
|
2080
|
+
};
|
|
2081
|
+
setPairing(active);
|
|
2082
|
+
if (!useAppLink) {
|
|
2083
|
+
publishRef.current?.({ state: active.state, qrUrl, intent, cancel });
|
|
2084
|
+
}
|
|
2085
|
+
opts.onPairingStateChange?.(active.state);
|
|
2086
|
+
if (useAppLink) {
|
|
2087
|
+
window.location.assign(started.pair_url);
|
|
2088
|
+
}
|
|
2089
|
+
code = await pollUntilApproved(
|
|
2090
|
+
issuer,
|
|
2091
|
+
started.request_id,
|
|
2092
|
+
(s) => {
|
|
2093
|
+
if (s.qrUrl) qrUrl = s.qrUrl;
|
|
2094
|
+
const enriched = { ...s, ...surface, qrUrl };
|
|
2095
|
+
setPairing(
|
|
2096
|
+
(p) => p && p.requestId === started.request_id ? { ...p, qrUrl, state: enriched } : p
|
|
2097
|
+
);
|
|
2098
|
+
if (!useAppLink) {
|
|
2099
|
+
publishRef.current?.({ state: enriched, qrUrl, intent, cancel });
|
|
2100
|
+
}
|
|
2101
|
+
opts.onPairingStateChange?.(enriched);
|
|
2102
|
+
},
|
|
2103
|
+
controller.signal,
|
|
2104
|
+
{ qrRefreshSeconds }
|
|
2105
|
+
);
|
|
2106
|
+
}
|
|
1901
2107
|
}
|
|
1902
2108
|
setPairing(null);
|
|
1903
2109
|
publishRef.current?.(null);
|
|
@@ -1962,8 +2168,11 @@ function useZorealLogin(options) {
|
|
|
1962
2168
|
}
|
|
1963
2169
|
|
|
1964
2170
|
// src/ring.tsx
|
|
1965
|
-
import { useEffect as useEffect3 } from "react";
|
|
2171
|
+
import { useEffect as useEffect3, useLayoutEffect, useRef as useRef3 } from "react";
|
|
1966
2172
|
import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
2173
|
+
var TAIL = 0.3;
|
|
2174
|
+
var STACK = Array.from({ length: 12 }, (_, i) => 12 - i);
|
|
2175
|
+
var HALO = [3, 2, 1];
|
|
1967
2176
|
function ZorealBusyRing({
|
|
1968
2177
|
busy,
|
|
1969
2178
|
radius = 8,
|
|
@@ -1976,11 +2185,42 @@ function ZorealBusyRing({
|
|
|
1976
2185
|
useEffect3(() => {
|
|
1977
2186
|
ensureStyles();
|
|
1978
2187
|
}, []);
|
|
2188
|
+
const wrapRef = useRef3(null);
|
|
2189
|
+
const measureRef = useRef3(null);
|
|
2190
|
+
useLayoutEffect(() => {
|
|
2191
|
+
const wrap = wrapRef.current;
|
|
2192
|
+
const rect = measureRef.current;
|
|
2193
|
+
if (!wrap || !rect) return;
|
|
2194
|
+
const measure = () => {
|
|
2195
|
+
const length = rect.getTotalLength();
|
|
2196
|
+
if (length > 0) wrap.style.setProperty("--zrl-ring-len", `${length}px`);
|
|
2197
|
+
};
|
|
2198
|
+
measure();
|
|
2199
|
+
if (typeof ResizeObserver === "undefined") return;
|
|
2200
|
+
const observer = new ResizeObserver(measure);
|
|
2201
|
+
observer.observe(wrap);
|
|
2202
|
+
return () => observer.disconnect();
|
|
2203
|
+
}, [radius]);
|
|
1979
2204
|
const rx = radius + 2;
|
|
1980
|
-
const layer = (name) => /* @__PURE__ */ jsx5(
|
|
2205
|
+
const layer = (key, name, len, alpha, ref) => /* @__PURE__ */ jsx5(
|
|
2206
|
+
"rect",
|
|
2207
|
+
{
|
|
2208
|
+
ref,
|
|
2209
|
+
className: cx(name),
|
|
2210
|
+
rx,
|
|
2211
|
+
ry: rx,
|
|
2212
|
+
style: {
|
|
2213
|
+
strokeDasharray: `calc(var(--zrl-l) * ${len}) calc(var(--zrl-l) * ${1 - len})`,
|
|
2214
|
+
"--zrl-s": `calc(var(--zrl-l) * ${-(TAIL - len)})`,
|
|
2215
|
+
opacity: alpha
|
|
2216
|
+
}
|
|
2217
|
+
},
|
|
2218
|
+
key
|
|
2219
|
+
);
|
|
1981
2220
|
return /* @__PURE__ */ jsxs5(
|
|
1982
2221
|
"span",
|
|
1983
2222
|
{
|
|
2223
|
+
ref: wrapRef,
|
|
1984
2224
|
className: className ? `${cx("root")} ${cx("ring")} ${className}` : `${cx("root")} ${cx("ring")}`,
|
|
1985
2225
|
"data-theme": theme,
|
|
1986
2226
|
"data-busy": busy,
|
|
@@ -1988,10 +2228,10 @@ function ZorealBusyRing({
|
|
|
1988
2228
|
children: [
|
|
1989
2229
|
children,
|
|
1990
2230
|
/* @__PURE__ */ jsxs5("svg", { className: cx("ring-svg"), "aria-hidden": "true", children: [
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
layer("ring-head")
|
|
2231
|
+
HALO.map(
|
|
2232
|
+
(n, i) => layer(`h${n}`, "ring-halo", TAIL * n / HALO.length, `calc(var(--zrl-glow-opacity) * 0.6 / ${n})`, i === 0 ? measureRef : void 0)
|
|
2233
|
+
),
|
|
2234
|
+
STACK.map((n) => layer(`t${n}`, n <= 2 ? "ring-head" : "ring-tail", TAIL * n / STACK.length, String(1 / n)))
|
|
1995
2235
|
] })
|
|
1996
2236
|
]
|
|
1997
2237
|
}
|
|
@@ -2094,7 +2334,7 @@ function ZorealLogin(props) {
|
|
|
2094
2334
|
}
|
|
2095
2335
|
|
|
2096
2336
|
// src/useZorealAutoLogin.ts
|
|
2097
|
-
import { useEffect as useEffect4, useRef as
|
|
2337
|
+
import { useEffect as useEffect4, useRef as useRef4 } from "react";
|
|
2098
2338
|
function useZorealAutoLogin(options) {
|
|
2099
2339
|
const { login } = useZorealFlow({
|
|
2100
2340
|
flow: "browser-direct",
|
|
@@ -2111,7 +2351,7 @@ function useZorealAutoLogin(options) {
|
|
|
2111
2351
|
},
|
|
2112
2352
|
onNonOAuthError: (e) => options.onError?.(e)
|
|
2113
2353
|
});
|
|
2114
|
-
const fired =
|
|
2354
|
+
const fired = useRef4(false);
|
|
2115
2355
|
useEffect4(() => {
|
|
2116
2356
|
if (options.disabled || fired.current) return;
|
|
2117
2357
|
fired.current = true;
|