@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/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.16";
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 ("code" in started) {
1848
- code = started.code;
1849
- selectBy = "session";
1850
- } else {
1851
- selectBy = useAppLink ? "app_link" : "qr";
1852
- const qrRefreshSeconds = qrRefreshSecondsOf(started);
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
- let qrUrl = `${issuer}/pair/${encodeURIComponent(started.request_id)}/qr.svg`;
2017
+ const surface = { pairUrl: startUrl, appLink: true, intent, cancel };
1867
2018
  const active = {
1868
- requestId: started.request_id,
1869
- pairUrl: surface.pairUrl,
1870
- qrUrl,
1871
- state: { status: "pending", expiresIn: started.expires_in, qrUrl, ...surface },
1872
- appLink: useAppLink,
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
- if (useAppLink) {
1881
- window.location.assign(started.pair_url);
1882
- }
2028
+ window.location.assign(startUrl);
1883
2029
  code = await pollUntilApproved(
1884
2030
  issuer,
1885
- started.request_id,
2031
+ requestId,
1886
2032
  (s) => {
1887
- if (s.qrUrl) qrUrl = s.qrUrl;
1888
- const enriched = { ...s, ...surface, qrUrl };
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
- // The app link has no QR to animate, and the provider answers 404
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);