@zoreal/oauth2-react 0.2.16 → 0.2.18
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 +279 -56
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +279 -56
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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.18";
|
|
9
9
|
var DEFAULT_ISSUER = "https://id.zoreal.com";
|
|
10
10
|
var POLL_INTERVAL_MS = 2e3;
|
|
11
11
|
var POLL_INTERVAL_ENROLLING_MS = 5e3;
|
|
@@ -1655,6 +1655,20 @@ async function startPairing(issuer, params) {
|
|
|
1655
1655
|
}
|
|
1656
1656
|
return body;
|
|
1657
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
|
+
}
|
|
1658
1672
|
function qrRefreshSecondsOf(started) {
|
|
1659
1673
|
const seconds = started.qr_refresh_seconds;
|
|
1660
1674
|
return typeof seconds === "number" && seconds > 0 ? seconds : DEFAULT_QR_REFRESH_SECONDS;
|
|
@@ -1677,6 +1691,8 @@ var sleep = (ms, signal) => new Promise((resolve, reject) => {
|
|
|
1677
1691
|
}, ms);
|
|
1678
1692
|
signal?.addEventListener("abort", onAbort, { once: true });
|
|
1679
1693
|
});
|
|
1694
|
+
var SETTLE_MS = 1500;
|
|
1695
|
+
var NETWORK_FAILURES_TOLERATED = 4;
|
|
1680
1696
|
async function pollUntilApproved(issuer, requestId, onState, signal, options = {}) {
|
|
1681
1697
|
let last = { status: "pending" };
|
|
1682
1698
|
const emit = (state) => {
|
|
@@ -1705,12 +1721,33 @@ async function pollUntilApproved(issuer, requestId, onState, signal, options = {
|
|
|
1705
1721
|
})().catch(() => {
|
|
1706
1722
|
});
|
|
1707
1723
|
};
|
|
1724
|
+
const settlingUntil = options.tolerateUnknownUntil ?? 0;
|
|
1725
|
+
let networkFailures = 0;
|
|
1708
1726
|
try {
|
|
1727
|
+
if (settlingUntil > Date.now()) await sleep(SETTLE_MS, signal);
|
|
1709
1728
|
for (; ; ) {
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1729
|
+
let response;
|
|
1730
|
+
try {
|
|
1731
|
+
response = await fetch(`${issuer}/pair/${encodeURIComponent(requestId)}/status`, {
|
|
1732
|
+
signal
|
|
1733
|
+
});
|
|
1734
|
+
networkFailures = 0;
|
|
1735
|
+
} catch (e) {
|
|
1736
|
+
if (e instanceof DOMException && e.name === "AbortError") throw e;
|
|
1737
|
+
networkFailures += 1;
|
|
1738
|
+
if (settlingUntil > Date.now() || networkFailures <= NETWORK_FAILURES_TOLERATED) {
|
|
1739
|
+
emit({ ...last, status: last.status });
|
|
1740
|
+
await sleep(POLL_INTERVAL_MS, signal);
|
|
1741
|
+
continue;
|
|
1742
|
+
}
|
|
1743
|
+
throw e;
|
|
1744
|
+
}
|
|
1713
1745
|
const body = await parseJson(response);
|
|
1746
|
+
if (response.status === 404 && (options.tolerateUnknownUntil ?? 0) > Date.now()) {
|
|
1747
|
+
emit({ status: "pending" });
|
|
1748
|
+
await sleep(POLL_INTERVAL_MS, signal);
|
|
1749
|
+
continue;
|
|
1750
|
+
}
|
|
1714
1751
|
if (!response.ok) {
|
|
1715
1752
|
throw new OAuthFlowError(
|
|
1716
1753
|
body.error ?? "server_error",
|
|
@@ -1792,11 +1829,154 @@ async function challengeS256(verifier) {
|
|
|
1792
1829
|
const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(verifier));
|
|
1793
1830
|
return base64url(new Uint8Array(digest));
|
|
1794
1831
|
}
|
|
1832
|
+
function challengeS256Sync(verifier) {
|
|
1833
|
+
return base64url(sha256(new TextEncoder().encode(verifier)));
|
|
1834
|
+
}
|
|
1835
|
+
var K = new Uint32Array([
|
|
1836
|
+
1116352408,
|
|
1837
|
+
1899447441,
|
|
1838
|
+
3049323471,
|
|
1839
|
+
3921009573,
|
|
1840
|
+
961987163,
|
|
1841
|
+
1508970993,
|
|
1842
|
+
2453635748,
|
|
1843
|
+
2870763221,
|
|
1844
|
+
3624381080,
|
|
1845
|
+
310598401,
|
|
1846
|
+
607225278,
|
|
1847
|
+
1426881987,
|
|
1848
|
+
1925078388,
|
|
1849
|
+
2162078206,
|
|
1850
|
+
2614888103,
|
|
1851
|
+
3248222580,
|
|
1852
|
+
3835390401,
|
|
1853
|
+
4022224774,
|
|
1854
|
+
264347078,
|
|
1855
|
+
604807628,
|
|
1856
|
+
770255983,
|
|
1857
|
+
1249150122,
|
|
1858
|
+
1555081692,
|
|
1859
|
+
1996064986,
|
|
1860
|
+
2554220882,
|
|
1861
|
+
2821834349,
|
|
1862
|
+
2952996808,
|
|
1863
|
+
3210313671,
|
|
1864
|
+
3336571891,
|
|
1865
|
+
3584528711,
|
|
1866
|
+
113926993,
|
|
1867
|
+
338241895,
|
|
1868
|
+
666307205,
|
|
1869
|
+
773529912,
|
|
1870
|
+
1294757372,
|
|
1871
|
+
1396182291,
|
|
1872
|
+
1695183700,
|
|
1873
|
+
1986661051,
|
|
1874
|
+
2177026350,
|
|
1875
|
+
2456956037,
|
|
1876
|
+
2730485921,
|
|
1877
|
+
2820302411,
|
|
1878
|
+
3259730800,
|
|
1879
|
+
3345764771,
|
|
1880
|
+
3516065817,
|
|
1881
|
+
3600352804,
|
|
1882
|
+
4094571909,
|
|
1883
|
+
275423344,
|
|
1884
|
+
430227734,
|
|
1885
|
+
506948616,
|
|
1886
|
+
659060556,
|
|
1887
|
+
883997877,
|
|
1888
|
+
958139571,
|
|
1889
|
+
1322822218,
|
|
1890
|
+
1537002063,
|
|
1891
|
+
1747873779,
|
|
1892
|
+
1955562222,
|
|
1893
|
+
2024104815,
|
|
1894
|
+
2227730452,
|
|
1895
|
+
2361852424,
|
|
1896
|
+
2428436474,
|
|
1897
|
+
2756734187,
|
|
1898
|
+
3204031479,
|
|
1899
|
+
3329325298
|
|
1900
|
+
]);
|
|
1901
|
+
var rotr = (x, n) => x >>> n | x << 32 - n;
|
|
1902
|
+
function sha256(message) {
|
|
1903
|
+
const H = new Uint32Array([
|
|
1904
|
+
1779033703,
|
|
1905
|
+
3144134277,
|
|
1906
|
+
1013904242,
|
|
1907
|
+
2773480762,
|
|
1908
|
+
1359893119,
|
|
1909
|
+
2600822924,
|
|
1910
|
+
528734635,
|
|
1911
|
+
1541459225
|
|
1912
|
+
]);
|
|
1913
|
+
const length = message.length;
|
|
1914
|
+
const padded = new Uint8Array(length + 9 + 63 >> 6 << 6);
|
|
1915
|
+
padded.set(message);
|
|
1916
|
+
padded[length] = 128;
|
|
1917
|
+
const view = new DataView(padded.buffer);
|
|
1918
|
+
const bits = length * 8;
|
|
1919
|
+
view.setUint32(padded.length - 8, Math.floor(bits / 4294967296));
|
|
1920
|
+
view.setUint32(padded.length - 4, bits >>> 0);
|
|
1921
|
+
const W = new Uint32Array(64);
|
|
1922
|
+
for (let offset = 0; offset < padded.length; offset += 64) {
|
|
1923
|
+
for (let i = 0; i < 16; i++) W[i] = view.getUint32(offset + i * 4);
|
|
1924
|
+
for (let i = 16; i < 64; i++) {
|
|
1925
|
+
const w15 = W[i - 15];
|
|
1926
|
+
const w2 = W[i - 2];
|
|
1927
|
+
const s0 = rotr(w15, 7) ^ rotr(w15, 18) ^ w15 >>> 3;
|
|
1928
|
+
const s1 = rotr(w2, 17) ^ rotr(w2, 19) ^ w2 >>> 10;
|
|
1929
|
+
W[i] = W[i - 16] + s0 + W[i - 7] + s1 >>> 0;
|
|
1930
|
+
}
|
|
1931
|
+
let [a, b, c, d, e, f, g, h] = H;
|
|
1932
|
+
for (let i = 0; i < 64; i++) {
|
|
1933
|
+
const S1 = rotr(e, 6) ^ rotr(e, 11) ^ rotr(e, 25);
|
|
1934
|
+
const ch = e & f ^ ~e & g;
|
|
1935
|
+
const t1 = h + S1 + ch + K[i] + W[i] >>> 0;
|
|
1936
|
+
const S0 = rotr(a, 2) ^ rotr(a, 13) ^ rotr(a, 22);
|
|
1937
|
+
const maj = a & b ^ a & c ^ b & c;
|
|
1938
|
+
const t2 = S0 + maj >>> 0;
|
|
1939
|
+
h = g;
|
|
1940
|
+
g = f;
|
|
1941
|
+
f = e;
|
|
1942
|
+
e = d + t1 >>> 0;
|
|
1943
|
+
d = c;
|
|
1944
|
+
c = b;
|
|
1945
|
+
b = a;
|
|
1946
|
+
a = t1 + t2 >>> 0;
|
|
1947
|
+
}
|
|
1948
|
+
H[0] = H[0] + a >>> 0;
|
|
1949
|
+
H[1] = H[1] + b >>> 0;
|
|
1950
|
+
H[2] = H[2] + c >>> 0;
|
|
1951
|
+
H[3] = H[3] + d >>> 0;
|
|
1952
|
+
H[4] = H[4] + e >>> 0;
|
|
1953
|
+
H[5] = H[5] + f >>> 0;
|
|
1954
|
+
H[6] = H[6] + g >>> 0;
|
|
1955
|
+
H[7] = H[7] + h >>> 0;
|
|
1956
|
+
}
|
|
1957
|
+
const out = new Uint8Array(32);
|
|
1958
|
+
const outView = new DataView(out.buffer);
|
|
1959
|
+
for (let i = 0; i < 8; i++) outView.setUint32(i * 4, H[i]);
|
|
1960
|
+
return out;
|
|
1961
|
+
}
|
|
1795
1962
|
function generateState() {
|
|
1796
1963
|
const bytes = new Uint8Array(16);
|
|
1797
1964
|
crypto.getRandomValues(bytes);
|
|
1798
1965
|
return base64url(bytes);
|
|
1799
1966
|
}
|
|
1967
|
+
var TOKEN_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
|
1968
|
+
function generateRequestId() {
|
|
1969
|
+
let out = "";
|
|
1970
|
+
const bytes = new Uint8Array(64);
|
|
1971
|
+
while (out.length < 32) {
|
|
1972
|
+
crypto.getRandomValues(bytes);
|
|
1973
|
+
for (const byte of bytes) {
|
|
1974
|
+
if (byte >= 248 || out.length === 32) continue;
|
|
1975
|
+
out += TOKEN_ALPHABET[byte % 62];
|
|
1976
|
+
}
|
|
1977
|
+
}
|
|
1978
|
+
return out;
|
|
1979
|
+
}
|
|
1800
1980
|
|
|
1801
1981
|
// src/useZorealLogin.ts
|
|
1802
1982
|
function useZorealFlow(options) {
|
|
@@ -1829,76 +2009,119 @@ function useZorealFlow(options) {
|
|
|
1829
2009
|
const useAppLink = display === "link";
|
|
1830
2010
|
const intent = resolveIntent(opts.intent, opts.scope, opts.acr_values);
|
|
1831
2011
|
try {
|
|
1832
|
-
const started = await startPairing(issuer, {
|
|
1833
|
-
client_id: clientId,
|
|
1834
|
-
scope: opts.scope ?? "openid",
|
|
1835
|
-
state,
|
|
1836
|
-
nonce,
|
|
1837
|
-
code_challenge: await challengeS256(verifier),
|
|
1838
|
-
redirect_uri: flow === "auth-code" ? opts.redirect_uri : void 0,
|
|
1839
|
-
acr_values: Array.isArray(opts.acr_values) ? opts.acr_values.join(" ") : opts.acr_values,
|
|
1840
|
-
max_age: opts.max_age,
|
|
1841
|
-
prompt: opts.prompt,
|
|
1842
|
-
locale,
|
|
1843
|
-
display
|
|
1844
|
-
});
|
|
1845
2012
|
let code;
|
|
1846
2013
|
let selectBy = "device";
|
|
1847
|
-
if (
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
2014
|
+
if (useAppLink) {
|
|
2015
|
+
const requestId = generateRequestId();
|
|
2016
|
+
const startUrl = sameDeviceStartUrl(issuer, {
|
|
2017
|
+
client_id: clientId,
|
|
2018
|
+
scope: opts.scope ?? "openid",
|
|
2019
|
+
state,
|
|
2020
|
+
nonce,
|
|
2021
|
+
code_challenge: challengeS256Sync(verifier),
|
|
2022
|
+
redirect_uri: flow === "auth-code" ? opts.redirect_uri : void 0,
|
|
2023
|
+
acr_values: Array.isArray(opts.acr_values) ? opts.acr_values.join(" ") : opts.acr_values,
|
|
2024
|
+
max_age: opts.max_age,
|
|
2025
|
+
prompt: opts.prompt,
|
|
2026
|
+
locale,
|
|
2027
|
+
request_id: requestId,
|
|
2028
|
+
origin: window.location.origin
|
|
2029
|
+
});
|
|
2030
|
+
selectBy = "app_link";
|
|
1853
2031
|
const cancel = () => {
|
|
1854
2032
|
controller.abort();
|
|
1855
2033
|
setPairing(null);
|
|
1856
|
-
publishRef.current?.(null);
|
|
1857
2034
|
};
|
|
1858
|
-
const surface = {
|
|
1859
|
-
pairUrl: started.pair_url,
|
|
1860
|
-
appLink: useAppLink,
|
|
1861
|
-
intent,
|
|
1862
|
-
cancel,
|
|
1863
|
-
// The app link has no QR and therefore no cadence to report.
|
|
1864
|
-
...useAppLink ? null : { qrRefreshSeconds }
|
|
1865
|
-
};
|
|
1866
|
-
let qrUrl = `${issuer}/pair/${encodeURIComponent(started.request_id)}/qr.svg`;
|
|
2035
|
+
const surface = { pairUrl: startUrl, appLink: true, intent, cancel };
|
|
1867
2036
|
const active = {
|
|
1868
|
-
requestId
|
|
1869
|
-
pairUrl:
|
|
1870
|
-
qrUrl,
|
|
1871
|
-
state: { status: "pending",
|
|
1872
|
-
appLink:
|
|
2037
|
+
requestId,
|
|
2038
|
+
pairUrl: startUrl,
|
|
2039
|
+
qrUrl: "",
|
|
2040
|
+
state: { status: "pending", ...surface },
|
|
2041
|
+
appLink: true,
|
|
1873
2042
|
cancel
|
|
1874
2043
|
};
|
|
1875
2044
|
setPairing(active);
|
|
1876
|
-
if (!useAppLink) {
|
|
1877
|
-
publishRef.current?.({ state: active.state, qrUrl, intent, cancel });
|
|
1878
|
-
}
|
|
1879
2045
|
opts.onPairingStateChange?.(active.state);
|
|
1880
|
-
|
|
1881
|
-
window.location.assign(started.pair_url);
|
|
1882
|
-
}
|
|
2046
|
+
window.location.assign(startUrl);
|
|
1883
2047
|
code = await pollUntilApproved(
|
|
1884
2048
|
issuer,
|
|
1885
|
-
|
|
2049
|
+
requestId,
|
|
1886
2050
|
(s) => {
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
setPairing(
|
|
1890
|
-
(p) => p && p.requestId === started.request_id ? { ...p, qrUrl, state: enriched } : p
|
|
1891
|
-
);
|
|
1892
|
-
if (!useAppLink) {
|
|
1893
|
-
publishRef.current?.({ state: enriched, qrUrl, intent, cancel });
|
|
1894
|
-
}
|
|
2051
|
+
const enriched = { ...s, ...surface };
|
|
2052
|
+
setPairing((p) => p && p.requestId === requestId ? { ...p, state: enriched } : p);
|
|
1895
2053
|
opts.onPairingStateChange?.(enriched);
|
|
1896
2054
|
},
|
|
1897
2055
|
controller.signal,
|
|
1898
|
-
|
|
1899
|
-
// for one, so the frames are asked for only on the QR surface.
|
|
1900
|
-
{ qrRefreshSeconds: useAppLink ? void 0 : qrRefreshSeconds }
|
|
2056
|
+
{ tolerateUnknownUntil: Date.now() + 15e3 }
|
|
1901
2057
|
);
|
|
2058
|
+
} else {
|
|
2059
|
+
const started = await startPairing(issuer, {
|
|
2060
|
+
client_id: clientId,
|
|
2061
|
+
scope: opts.scope ?? "openid",
|
|
2062
|
+
state,
|
|
2063
|
+
nonce,
|
|
2064
|
+
code_challenge: await challengeS256(verifier),
|
|
2065
|
+
redirect_uri: flow === "auth-code" ? opts.redirect_uri : void 0,
|
|
2066
|
+
acr_values: Array.isArray(opts.acr_values) ? opts.acr_values.join(" ") : opts.acr_values,
|
|
2067
|
+
max_age: opts.max_age,
|
|
2068
|
+
prompt: opts.prompt,
|
|
2069
|
+
locale,
|
|
2070
|
+
display: "qr"
|
|
2071
|
+
});
|
|
2072
|
+
if ("code" in started) {
|
|
2073
|
+
code = started.code;
|
|
2074
|
+
selectBy = "session";
|
|
2075
|
+
} else {
|
|
2076
|
+
selectBy = "qr";
|
|
2077
|
+
const qrRefreshSeconds = qrRefreshSecondsOf(started);
|
|
2078
|
+
const cancel = () => {
|
|
2079
|
+
controller.abort();
|
|
2080
|
+
setPairing(null);
|
|
2081
|
+
publishRef.current?.(null);
|
|
2082
|
+
};
|
|
2083
|
+
const surface = {
|
|
2084
|
+
pairUrl: started.pair_url,
|
|
2085
|
+
appLink: false,
|
|
2086
|
+
intent,
|
|
2087
|
+
cancel,
|
|
2088
|
+
qrRefreshSeconds
|
|
2089
|
+
};
|
|
2090
|
+
let qrUrl = `${issuer}/pair/${encodeURIComponent(started.request_id)}/qr.svg`;
|
|
2091
|
+
const active = {
|
|
2092
|
+
requestId: started.request_id,
|
|
2093
|
+
pairUrl: surface.pairUrl,
|
|
2094
|
+
qrUrl,
|
|
2095
|
+
state: { status: "pending", expiresIn: started.expires_in, qrUrl, ...surface },
|
|
2096
|
+
appLink: false,
|
|
2097
|
+
cancel
|
|
2098
|
+
};
|
|
2099
|
+
setPairing(active);
|
|
2100
|
+
if (!useAppLink) {
|
|
2101
|
+
publishRef.current?.({ state: active.state, qrUrl, intent, cancel });
|
|
2102
|
+
}
|
|
2103
|
+
opts.onPairingStateChange?.(active.state);
|
|
2104
|
+
if (useAppLink) {
|
|
2105
|
+
window.location.assign(started.pair_url);
|
|
2106
|
+
}
|
|
2107
|
+
code = await pollUntilApproved(
|
|
2108
|
+
issuer,
|
|
2109
|
+
started.request_id,
|
|
2110
|
+
(s) => {
|
|
2111
|
+
if (s.qrUrl) qrUrl = s.qrUrl;
|
|
2112
|
+
const enriched = { ...s, ...surface, qrUrl };
|
|
2113
|
+
setPairing(
|
|
2114
|
+
(p) => p && p.requestId === started.request_id ? { ...p, qrUrl, state: enriched } : p
|
|
2115
|
+
);
|
|
2116
|
+
if (!useAppLink) {
|
|
2117
|
+
publishRef.current?.({ state: enriched, qrUrl, intent, cancel });
|
|
2118
|
+
}
|
|
2119
|
+
opts.onPairingStateChange?.(enriched);
|
|
2120
|
+
},
|
|
2121
|
+
controller.signal,
|
|
2122
|
+
{ qrRefreshSeconds }
|
|
2123
|
+
);
|
|
2124
|
+
}
|
|
1902
2125
|
}
|
|
1903
2126
|
setPairing(null);
|
|
1904
2127
|
publishRef.current?.(null);
|