@zoreal/oauth2-react 0.2.16 → 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 +258 -53
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +258 -53
- 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.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;
|
|
@@ -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;
|
|
@@ -1711,6 +1725,11 @@ async function pollUntilApproved(issuer, requestId, onState, signal, options = {
|
|
|
1711
1725
|
signal
|
|
1712
1726
|
});
|
|
1713
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
|
+
}
|
|
1714
1733
|
if (!response.ok) {
|
|
1715
1734
|
throw new OAuthFlowError(
|
|
1716
1735
|
body.error ?? "server_error",
|
|
@@ -1792,11 +1811,154 @@ async function challengeS256(verifier) {
|
|
|
1792
1811
|
const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(verifier));
|
|
1793
1812
|
return base64url(new Uint8Array(digest));
|
|
1794
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
|
+
}
|
|
1795
1944
|
function generateState() {
|
|
1796
1945
|
const bytes = new Uint8Array(16);
|
|
1797
1946
|
crypto.getRandomValues(bytes);
|
|
1798
1947
|
return base64url(bytes);
|
|
1799
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
|
+
}
|
|
1800
1962
|
|
|
1801
1963
|
// src/useZorealLogin.ts
|
|
1802
1964
|
function useZorealFlow(options) {
|
|
@@ -1829,76 +1991,119 @@ function useZorealFlow(options) {
|
|
|
1829
1991
|
const useAppLink = display === "link";
|
|
1830
1992
|
const intent = resolveIntent(opts.intent, opts.scope, opts.acr_values);
|
|
1831
1993
|
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
1994
|
let code;
|
|
1846
1995
|
let selectBy = "device";
|
|
1847
|
-
if (
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
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";
|
|
1853
2013
|
const cancel = () => {
|
|
1854
2014
|
controller.abort();
|
|
1855
2015
|
setPairing(null);
|
|
1856
|
-
publishRef.current?.(null);
|
|
1857
|
-
};
|
|
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
2016
|
};
|
|
1866
|
-
|
|
2017
|
+
const surface = { pairUrl: startUrl, appLink: true, intent, cancel };
|
|
1867
2018
|
const active = {
|
|
1868
|
-
requestId
|
|
1869
|
-
pairUrl:
|
|
1870
|
-
qrUrl,
|
|
1871
|
-
state: { status: "pending",
|
|
1872
|
-
appLink:
|
|
2019
|
+
requestId,
|
|
2020
|
+
pairUrl: startUrl,
|
|
2021
|
+
qrUrl: "",
|
|
2022
|
+
state: { status: "pending", ...surface },
|
|
2023
|
+
appLink: true,
|
|
1873
2024
|
cancel
|
|
1874
2025
|
};
|
|
1875
2026
|
setPairing(active);
|
|
1876
|
-
if (!useAppLink) {
|
|
1877
|
-
publishRef.current?.({ state: active.state, qrUrl, intent, cancel });
|
|
1878
|
-
}
|
|
1879
2027
|
opts.onPairingStateChange?.(active.state);
|
|
1880
|
-
|
|
1881
|
-
window.location.assign(started.pair_url);
|
|
1882
|
-
}
|
|
2028
|
+
window.location.assign(startUrl);
|
|
1883
2029
|
code = await pollUntilApproved(
|
|
1884
2030
|
issuer,
|
|
1885
|
-
|
|
2031
|
+
requestId,
|
|
1886
2032
|
(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
|
-
}
|
|
2033
|
+
const enriched = { ...s, ...surface };
|
|
2034
|
+
setPairing((p) => p && p.requestId === requestId ? { ...p, state: enriched } : p);
|
|
1895
2035
|
opts.onPairingStateChange?.(enriched);
|
|
1896
2036
|
},
|
|
1897
2037
|
controller.signal,
|
|
1898
|
-
|
|
1899
|
-
// for one, so the frames are asked for only on the QR surface.
|
|
1900
|
-
{ qrRefreshSeconds: useAppLink ? void 0 : qrRefreshSeconds }
|
|
2038
|
+
{ tolerateUnknownUntil: Date.now() + 15e3 }
|
|
1901
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
|
+
}
|
|
1902
2107
|
}
|
|
1903
2108
|
setPairing(null);
|
|
1904
2109
|
publishRef.current?.(null);
|