@zoreal/oauth2-js 0.1.17 → 0.1.19
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 +305 -88
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +15 -7
- package/dist/index.d.ts +15 -7
- package/dist/index.js +305 -88
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -278,6 +278,11 @@ declare function mountPairingModal(state: PairingState, options: PairingModalOpt
|
|
|
278
278
|
* the provider never renders a QR for it, so
|
|
279
279
|
* nobody can turn a same-device link into a
|
|
280
280
|
* static code to relay.
|
|
281
|
+
* GET /pair/start the same-device sign-in as a NAVIGATION:
|
|
282
|
+
* the /pair parameters as a query, plus
|
|
283
|
+
* request_id (the page's own token) and
|
|
284
|
+
* origin; answered with a redirect to the
|
|
285
|
+
* pairing's universal link, inside the tap
|
|
281
286
|
* GET /pair/:id/status poll: pending | claimed |
|
|
282
287
|
* approved (with code) | denied | expired |
|
|
283
288
|
* enrolling. Over-polling cancels the request
|
|
@@ -305,7 +310,7 @@ declare function mountPairingModal(state: PairingState, options: PairingModalOpt
|
|
|
305
310
|
* auth-code mode leaves it to the RP backend.
|
|
306
311
|
*/
|
|
307
312
|
declare const WIRE_VERSION = 1;
|
|
308
|
-
declare const SDK_VERSION = "0.1.
|
|
313
|
+
declare const SDK_VERSION = "0.1.19";
|
|
309
314
|
declare const SDK_NAME = "@zoreal/oauth2-js";
|
|
310
315
|
declare const DEFAULT_ISSUER = "https://id.zoreal.com";
|
|
311
316
|
/** Pending TTL is short. Poll gently; over-polling cancels the request. */
|
|
@@ -408,12 +413,15 @@ interface StartPairingParams {
|
|
|
408
413
|
display?: PairDisplay;
|
|
409
414
|
}
|
|
410
415
|
declare function startPairing(issuer: string, params: StartPairingParams, signal?: AbortSignal): Promise<PairStartResponse>;
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
416
|
+
declare function pollUntilApproved(issuer: string, requestId: string, onState?: (state: PairingState) => void, signal?: AbortSignal, options?: {
|
|
417
|
+
/**
|
|
418
|
+
* Same-device navigation only. The page starts polling while the
|
|
419
|
+
* provider is still answering the navigation that creates the pairing,
|
|
420
|
+
* so a "no such pairing" answer before this instant (epoch ms) is the
|
|
421
|
+
* pairing not existing YET, and is read as pending.
|
|
422
|
+
*/
|
|
423
|
+
tolerateUnknownUntil?: number;
|
|
424
|
+
}): Promise<string>;
|
|
417
425
|
/**
|
|
418
426
|
* The code exchange, browser-direct mode only: a public client, PKCE and no
|
|
419
427
|
* secret. What comes back can only ever be the pseudonymous tier, by
|
package/dist/index.d.ts
CHANGED
|
@@ -278,6 +278,11 @@ declare function mountPairingModal(state: PairingState, options: PairingModalOpt
|
|
|
278
278
|
* the provider never renders a QR for it, so
|
|
279
279
|
* nobody can turn a same-device link into a
|
|
280
280
|
* static code to relay.
|
|
281
|
+
* GET /pair/start the same-device sign-in as a NAVIGATION:
|
|
282
|
+
* the /pair parameters as a query, plus
|
|
283
|
+
* request_id (the page's own token) and
|
|
284
|
+
* origin; answered with a redirect to the
|
|
285
|
+
* pairing's universal link, inside the tap
|
|
281
286
|
* GET /pair/:id/status poll: pending | claimed |
|
|
282
287
|
* approved (with code) | denied | expired |
|
|
283
288
|
* enrolling. Over-polling cancels the request
|
|
@@ -305,7 +310,7 @@ declare function mountPairingModal(state: PairingState, options: PairingModalOpt
|
|
|
305
310
|
* auth-code mode leaves it to the RP backend.
|
|
306
311
|
*/
|
|
307
312
|
declare const WIRE_VERSION = 1;
|
|
308
|
-
declare const SDK_VERSION = "0.1.
|
|
313
|
+
declare const SDK_VERSION = "0.1.19";
|
|
309
314
|
declare const SDK_NAME = "@zoreal/oauth2-js";
|
|
310
315
|
declare const DEFAULT_ISSUER = "https://id.zoreal.com";
|
|
311
316
|
/** Pending TTL is short. Poll gently; over-polling cancels the request. */
|
|
@@ -408,12 +413,15 @@ interface StartPairingParams {
|
|
|
408
413
|
display?: PairDisplay;
|
|
409
414
|
}
|
|
410
415
|
declare function startPairing(issuer: string, params: StartPairingParams, signal?: AbortSignal): Promise<PairStartResponse>;
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
416
|
+
declare function pollUntilApproved(issuer: string, requestId: string, onState?: (state: PairingState) => void, signal?: AbortSignal, options?: {
|
|
417
|
+
/**
|
|
418
|
+
* Same-device navigation only. The page starts polling while the
|
|
419
|
+
* provider is still answering the navigation that creates the pairing,
|
|
420
|
+
* so a "no such pairing" answer before this instant (epoch ms) is the
|
|
421
|
+
* pairing not existing YET, and is read as pending.
|
|
422
|
+
*/
|
|
423
|
+
tolerateUnknownUntil?: number;
|
|
424
|
+
}): Promise<string>;
|
|
417
425
|
/**
|
|
418
426
|
* The code exchange, browser-direct mode only: a public client, PKCE and no
|
|
419
427
|
* secret. What comes back can only ever be the pseudonymous tier, by
|
package/dist/index.js
CHANGED
|
@@ -14,7 +14,7 @@ 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.19";
|
|
18
18
|
var SDK_NAME = "@zoreal/oauth2-js";
|
|
19
19
|
var DEFAULT_ISSUER = "https://id.zoreal.com";
|
|
20
20
|
var POLL_INTERVAL_MS = 2e3;
|
|
@@ -63,6 +63,20 @@ async function startPairing(issuer, params, signal) {
|
|
|
63
63
|
}
|
|
64
64
|
return body;
|
|
65
65
|
}
|
|
66
|
+
function sameDeviceStartUrl(issuer, params) {
|
|
67
|
+
const query = new URLSearchParams();
|
|
68
|
+
const all = {
|
|
69
|
+
...params,
|
|
70
|
+
code_challenge_method: "S256",
|
|
71
|
+
wire_version: WIRE_VERSION,
|
|
72
|
+
sdk: `${SDK_NAME}/${SDK_VERSION}`
|
|
73
|
+
};
|
|
74
|
+
for (const [key, value] of Object.entries(all)) {
|
|
75
|
+
if (value === void 0 || value === null || value === "") continue;
|
|
76
|
+
query.set(key, String(value));
|
|
77
|
+
}
|
|
78
|
+
return `${issuer}/pair/start?${query.toString()}`;
|
|
79
|
+
}
|
|
66
80
|
var sleep = (ms, signal) => new Promise((resolve, reject) => {
|
|
67
81
|
if (signal?.aborted) {
|
|
68
82
|
reject(new DOMException("aborted", "AbortError"));
|
|
@@ -78,23 +92,48 @@ var sleep = (ms, signal) => new Promise((resolve, reject) => {
|
|
|
78
92
|
}, ms);
|
|
79
93
|
signal?.addEventListener("abort", onAbort, { once: true });
|
|
80
94
|
});
|
|
81
|
-
|
|
95
|
+
var SETTLE_MS = 1500;
|
|
96
|
+
var NETWORK_FAILURES_TOLERATED = 4;
|
|
97
|
+
async function pollUntilApproved(issuer, requestId, onState, signal, options = {}) {
|
|
98
|
+
const settlingUntil = options.tolerateUnknownUntil ?? 0;
|
|
99
|
+
let networkFailures = 0;
|
|
100
|
+
let last = { status: "pending" };
|
|
101
|
+
if (settlingUntil > Date.now()) await sleep(SETTLE_MS, signal);
|
|
82
102
|
for (; ; ) {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
103
|
+
let response;
|
|
104
|
+
try {
|
|
105
|
+
response = await fetch(`${issuer}/pair/${encodeURIComponent(requestId)}/status`, {
|
|
106
|
+
signal
|
|
107
|
+
});
|
|
108
|
+
networkFailures = 0;
|
|
109
|
+
} catch (e) {
|
|
110
|
+
if (e instanceof DOMException && e.name === "AbortError") throw e;
|
|
111
|
+
networkFailures += 1;
|
|
112
|
+
if (settlingUntil > Date.now() || networkFailures <= NETWORK_FAILURES_TOLERATED) {
|
|
113
|
+
onState?.(last);
|
|
114
|
+
await sleep(POLL_INTERVAL_MS, signal);
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
throw e;
|
|
118
|
+
}
|
|
86
119
|
const body = await parseJson(response);
|
|
120
|
+
if (response.status === 404 && (options.tolerateUnknownUntil ?? 0) > Date.now()) {
|
|
121
|
+
onState?.({ status: "pending" });
|
|
122
|
+
await sleep(POLL_INTERVAL_MS, signal);
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
87
125
|
if (!response.ok) {
|
|
88
126
|
throw new OAuthFlowError(
|
|
89
127
|
body.error ?? "server_error",
|
|
90
128
|
body.error_description ?? `Pairing status failed (${response.status})`
|
|
91
129
|
);
|
|
92
130
|
}
|
|
93
|
-
|
|
131
|
+
last = {
|
|
94
132
|
status: body.status,
|
|
95
133
|
expiresIn: body.expires_in,
|
|
96
134
|
enrolmentDeadline: body.enrolment_deadline
|
|
97
|
-
}
|
|
135
|
+
};
|
|
136
|
+
onState?.(last);
|
|
98
137
|
switch (body.status) {
|
|
99
138
|
case "approved":
|
|
100
139
|
if (!body.code) {
|
|
@@ -1688,11 +1727,154 @@ async function challengeS256(verifier) {
|
|
|
1688
1727
|
const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(verifier));
|
|
1689
1728
|
return base64url(new Uint8Array(digest));
|
|
1690
1729
|
}
|
|
1730
|
+
function challengeS256Sync(verifier) {
|
|
1731
|
+
return base64url(sha256(new TextEncoder().encode(verifier)));
|
|
1732
|
+
}
|
|
1733
|
+
var K = new Uint32Array([
|
|
1734
|
+
1116352408,
|
|
1735
|
+
1899447441,
|
|
1736
|
+
3049323471,
|
|
1737
|
+
3921009573,
|
|
1738
|
+
961987163,
|
|
1739
|
+
1508970993,
|
|
1740
|
+
2453635748,
|
|
1741
|
+
2870763221,
|
|
1742
|
+
3624381080,
|
|
1743
|
+
310598401,
|
|
1744
|
+
607225278,
|
|
1745
|
+
1426881987,
|
|
1746
|
+
1925078388,
|
|
1747
|
+
2162078206,
|
|
1748
|
+
2614888103,
|
|
1749
|
+
3248222580,
|
|
1750
|
+
3835390401,
|
|
1751
|
+
4022224774,
|
|
1752
|
+
264347078,
|
|
1753
|
+
604807628,
|
|
1754
|
+
770255983,
|
|
1755
|
+
1249150122,
|
|
1756
|
+
1555081692,
|
|
1757
|
+
1996064986,
|
|
1758
|
+
2554220882,
|
|
1759
|
+
2821834349,
|
|
1760
|
+
2952996808,
|
|
1761
|
+
3210313671,
|
|
1762
|
+
3336571891,
|
|
1763
|
+
3584528711,
|
|
1764
|
+
113926993,
|
|
1765
|
+
338241895,
|
|
1766
|
+
666307205,
|
|
1767
|
+
773529912,
|
|
1768
|
+
1294757372,
|
|
1769
|
+
1396182291,
|
|
1770
|
+
1695183700,
|
|
1771
|
+
1986661051,
|
|
1772
|
+
2177026350,
|
|
1773
|
+
2456956037,
|
|
1774
|
+
2730485921,
|
|
1775
|
+
2820302411,
|
|
1776
|
+
3259730800,
|
|
1777
|
+
3345764771,
|
|
1778
|
+
3516065817,
|
|
1779
|
+
3600352804,
|
|
1780
|
+
4094571909,
|
|
1781
|
+
275423344,
|
|
1782
|
+
430227734,
|
|
1783
|
+
506948616,
|
|
1784
|
+
659060556,
|
|
1785
|
+
883997877,
|
|
1786
|
+
958139571,
|
|
1787
|
+
1322822218,
|
|
1788
|
+
1537002063,
|
|
1789
|
+
1747873779,
|
|
1790
|
+
1955562222,
|
|
1791
|
+
2024104815,
|
|
1792
|
+
2227730452,
|
|
1793
|
+
2361852424,
|
|
1794
|
+
2428436474,
|
|
1795
|
+
2756734187,
|
|
1796
|
+
3204031479,
|
|
1797
|
+
3329325298
|
|
1798
|
+
]);
|
|
1799
|
+
var rotr = (x, n) => x >>> n | x << 32 - n;
|
|
1800
|
+
function sha256(message) {
|
|
1801
|
+
const H = new Uint32Array([
|
|
1802
|
+
1779033703,
|
|
1803
|
+
3144134277,
|
|
1804
|
+
1013904242,
|
|
1805
|
+
2773480762,
|
|
1806
|
+
1359893119,
|
|
1807
|
+
2600822924,
|
|
1808
|
+
528734635,
|
|
1809
|
+
1541459225
|
|
1810
|
+
]);
|
|
1811
|
+
const length = message.length;
|
|
1812
|
+
const padded = new Uint8Array(length + 9 + 63 >> 6 << 6);
|
|
1813
|
+
padded.set(message);
|
|
1814
|
+
padded[length] = 128;
|
|
1815
|
+
const view = new DataView(padded.buffer);
|
|
1816
|
+
const bits = length * 8;
|
|
1817
|
+
view.setUint32(padded.length - 8, Math.floor(bits / 4294967296));
|
|
1818
|
+
view.setUint32(padded.length - 4, bits >>> 0);
|
|
1819
|
+
const W = new Uint32Array(64);
|
|
1820
|
+
for (let offset = 0; offset < padded.length; offset += 64) {
|
|
1821
|
+
for (let i = 0; i < 16; i++) W[i] = view.getUint32(offset + i * 4);
|
|
1822
|
+
for (let i = 16; i < 64; i++) {
|
|
1823
|
+
const w15 = W[i - 15];
|
|
1824
|
+
const w2 = W[i - 2];
|
|
1825
|
+
const s0 = rotr(w15, 7) ^ rotr(w15, 18) ^ w15 >>> 3;
|
|
1826
|
+
const s1 = rotr(w2, 17) ^ rotr(w2, 19) ^ w2 >>> 10;
|
|
1827
|
+
W[i] = W[i - 16] + s0 + W[i - 7] + s1 >>> 0;
|
|
1828
|
+
}
|
|
1829
|
+
let [a, b, c, d, e, f, g, h] = H;
|
|
1830
|
+
for (let i = 0; i < 64; i++) {
|
|
1831
|
+
const S1 = rotr(e, 6) ^ rotr(e, 11) ^ rotr(e, 25);
|
|
1832
|
+
const ch = e & f ^ ~e & g;
|
|
1833
|
+
const t1 = h + S1 + ch + K[i] + W[i] >>> 0;
|
|
1834
|
+
const S0 = rotr(a, 2) ^ rotr(a, 13) ^ rotr(a, 22);
|
|
1835
|
+
const maj = a & b ^ a & c ^ b & c;
|
|
1836
|
+
const t2 = S0 + maj >>> 0;
|
|
1837
|
+
h = g;
|
|
1838
|
+
g = f;
|
|
1839
|
+
f = e;
|
|
1840
|
+
e = d + t1 >>> 0;
|
|
1841
|
+
d = c;
|
|
1842
|
+
c = b;
|
|
1843
|
+
b = a;
|
|
1844
|
+
a = t1 + t2 >>> 0;
|
|
1845
|
+
}
|
|
1846
|
+
H[0] = H[0] + a >>> 0;
|
|
1847
|
+
H[1] = H[1] + b >>> 0;
|
|
1848
|
+
H[2] = H[2] + c >>> 0;
|
|
1849
|
+
H[3] = H[3] + d >>> 0;
|
|
1850
|
+
H[4] = H[4] + e >>> 0;
|
|
1851
|
+
H[5] = H[5] + f >>> 0;
|
|
1852
|
+
H[6] = H[6] + g >>> 0;
|
|
1853
|
+
H[7] = H[7] + h >>> 0;
|
|
1854
|
+
}
|
|
1855
|
+
const out = new Uint8Array(32);
|
|
1856
|
+
const outView = new DataView(out.buffer);
|
|
1857
|
+
for (let i = 0; i < 8; i++) outView.setUint32(i * 4, H[i]);
|
|
1858
|
+
return out;
|
|
1859
|
+
}
|
|
1691
1860
|
function generateState() {
|
|
1692
1861
|
const bytes = new Uint8Array(16);
|
|
1693
1862
|
crypto.getRandomValues(bytes);
|
|
1694
1863
|
return base64url(bytes);
|
|
1695
1864
|
}
|
|
1865
|
+
var TOKEN_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
|
1866
|
+
function generateRequestId() {
|
|
1867
|
+
let out = "";
|
|
1868
|
+
const bytes = new Uint8Array(64);
|
|
1869
|
+
while (out.length < 32) {
|
|
1870
|
+
crypto.getRandomValues(bytes);
|
|
1871
|
+
for (const byte of bytes) {
|
|
1872
|
+
if (byte >= 248 || out.length === 32) continue;
|
|
1873
|
+
out += TOKEN_ALPHABET[byte % 62];
|
|
1874
|
+
}
|
|
1875
|
+
}
|
|
1876
|
+
return out;
|
|
1877
|
+
}
|
|
1696
1878
|
|
|
1697
1879
|
// src/login.ts
|
|
1698
1880
|
function startLogin(options) {
|
|
@@ -1722,109 +1904,144 @@ function startLogin(options) {
|
|
|
1722
1904
|
const state = generateState();
|
|
1723
1905
|
const nonce = generateState();
|
|
1724
1906
|
try {
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1907
|
+
let code;
|
|
1908
|
+
let selectBy = "device";
|
|
1909
|
+
if (useAppLink && typeof window !== "undefined") {
|
|
1910
|
+
const requestId = generateRequestId();
|
|
1911
|
+
const startUrl = sameDeviceStartUrl(issuer, {
|
|
1728
1912
|
client_id: options.clientId,
|
|
1729
1913
|
scope: options.scope ?? "openid",
|
|
1730
1914
|
state,
|
|
1731
1915
|
nonce,
|
|
1732
|
-
code_challenge:
|
|
1916
|
+
code_challenge: challengeS256Sync(verifier),
|
|
1733
1917
|
redirect_uri: flow === "auth-code" ? options.redirect_uri : void 0,
|
|
1734
1918
|
acr_values: Array.isArray(options.acr_values) ? options.acr_values.join(" ") : options.acr_values,
|
|
1735
1919
|
max_age: options.max_age,
|
|
1736
1920
|
prompt: options.prompt,
|
|
1737
1921
|
locale: options.locale,
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
let code;
|
|
1743
|
-
let selectBy = "device";
|
|
1744
|
-
if ("code" in started) {
|
|
1745
|
-
code = started.code;
|
|
1746
|
-
selectBy = "session";
|
|
1747
|
-
} else {
|
|
1748
|
-
selectBy = useAppLink ? "app_link" : "qr";
|
|
1749
|
-
const requestId = started.request_id;
|
|
1750
|
-
const qrBase = `${issuer}/pair/${encodeURIComponent(requestId)}/qr.svg`;
|
|
1751
|
-
const animated = !useAppLink && started.display !== "legacy";
|
|
1752
|
-
const qrRefreshSeconds = !animated ? void 0 : typeof started.qr_refresh_seconds === "number" && started.qr_refresh_seconds > 0 ? started.qr_refresh_seconds : DEFAULT_QR_REFRESH_SECONDS;
|
|
1922
|
+
request_id: requestId,
|
|
1923
|
+
origin: window.location.origin
|
|
1924
|
+
});
|
|
1925
|
+
selectBy = "app_link";
|
|
1753
1926
|
surface.requestId = requestId;
|
|
1754
|
-
surface.pairUrl =
|
|
1755
|
-
surface.
|
|
1756
|
-
surface.appLink = useAppLink;
|
|
1927
|
+
surface.pairUrl = startUrl;
|
|
1928
|
+
surface.appLink = true;
|
|
1757
1929
|
const withSurface = (s) => ({
|
|
1758
1930
|
...s,
|
|
1759
|
-
pairUrl:
|
|
1760
|
-
|
|
1761
|
-
qrRefreshSeconds,
|
|
1762
|
-
appLink: useAppLink,
|
|
1931
|
+
pairUrl: startUrl,
|
|
1932
|
+
appLink: true,
|
|
1763
1933
|
intent,
|
|
1764
1934
|
cancel
|
|
1765
1935
|
});
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
options.onState?.(initial);
|
|
1769
|
-
if ((options.pairingUI ?? "modal") === "modal" && !useAppLink) {
|
|
1770
|
-
modal = mountPairingModal(initial, {
|
|
1771
|
-
onCancel: cancel,
|
|
1772
|
-
intent,
|
|
1773
|
-
locale: options.locale,
|
|
1774
|
-
theme: options.theme,
|
|
1775
|
-
timeoutMs: options.pairingTimeoutMs
|
|
1776
|
-
});
|
|
1777
|
-
}
|
|
1778
|
-
if (useAppLink && typeof window !== "undefined") {
|
|
1779
|
-
window.location.assign(started.pair_url);
|
|
1780
|
-
}
|
|
1781
|
-
if (qrRefreshSeconds !== void 0) {
|
|
1782
|
-
const periodMs = qrRefreshSeconds * 1e3;
|
|
1783
|
-
let due = Date.now() + periodMs;
|
|
1784
|
-
let timer;
|
|
1785
|
-
let stopped = false;
|
|
1786
|
-
const emit = () => {
|
|
1787
|
-
timer = void 0;
|
|
1788
|
-
surface.qrUrl = `${qrBase}?t=${Date.now()}`;
|
|
1789
|
-
const next = withSurface(lastPolled);
|
|
1790
|
-
modal?.update(next);
|
|
1791
|
-
options.onState?.(next);
|
|
1792
|
-
if (stopped) return;
|
|
1793
|
-
due = Date.now() + periodMs;
|
|
1794
|
-
timer = setTimeout(emit, periodMs);
|
|
1795
|
-
};
|
|
1796
|
-
const onVisible = () => {
|
|
1797
|
-
if (document.visibilityState === "visible" && timer !== void 0 && Date.now() >= due) {
|
|
1798
|
-
clearTimeout(timer);
|
|
1799
|
-
emit();
|
|
1800
|
-
}
|
|
1801
|
-
};
|
|
1802
|
-
const hasDocument = typeof document !== "undefined";
|
|
1803
|
-
if (hasDocument) document.addEventListener("visibilitychange", onVisible);
|
|
1804
|
-
stopRefresh = () => {
|
|
1805
|
-
stopped = true;
|
|
1806
|
-
if (timer !== void 0) clearTimeout(timer);
|
|
1807
|
-
timer = void 0;
|
|
1808
|
-
if (hasDocument) document.removeEventListener("visibilitychange", onVisible);
|
|
1809
|
-
stopRefresh = () => {
|
|
1810
|
-
};
|
|
1811
|
-
};
|
|
1812
|
-
timer = setTimeout(emit, Math.max(0, due - Date.now()));
|
|
1813
|
-
}
|
|
1936
|
+
options.onState?.(withSurface({ status: "pending" }));
|
|
1937
|
+
window.location.assign(startUrl);
|
|
1814
1938
|
code = await pollUntilApproved(
|
|
1815
1939
|
issuer,
|
|
1816
1940
|
requestId,
|
|
1817
|
-
(s) =>
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1941
|
+
(s) => options.onState?.(withSurface(s)),
|
|
1942
|
+
controller.signal,
|
|
1943
|
+
{ tolerateUnknownUntil: Date.now() + 15e3 }
|
|
1944
|
+
);
|
|
1945
|
+
} else {
|
|
1946
|
+
const started = await startPairing(
|
|
1947
|
+
issuer,
|
|
1948
|
+
{
|
|
1949
|
+
client_id: options.clientId,
|
|
1950
|
+
scope: options.scope ?? "openid",
|
|
1951
|
+
state,
|
|
1952
|
+
nonce,
|
|
1953
|
+
code_challenge: await challengeS256(verifier),
|
|
1954
|
+
redirect_uri: flow === "auth-code" ? options.redirect_uri : void 0,
|
|
1955
|
+
acr_values: Array.isArray(options.acr_values) ? options.acr_values.join(" ") : options.acr_values,
|
|
1956
|
+
max_age: options.max_age,
|
|
1957
|
+
prompt: options.prompt,
|
|
1958
|
+
locale: options.locale,
|
|
1959
|
+
display: "qr"
|
|
1823
1960
|
},
|
|
1824
1961
|
controller.signal
|
|
1825
1962
|
);
|
|
1963
|
+
if ("code" in started) {
|
|
1964
|
+
code = started.code;
|
|
1965
|
+
selectBy = "session";
|
|
1966
|
+
} else {
|
|
1967
|
+
selectBy = "qr";
|
|
1968
|
+
const requestId = started.request_id;
|
|
1969
|
+
const qrBase = `${issuer}/pair/${encodeURIComponent(requestId)}/qr.svg`;
|
|
1970
|
+
const animated = started.display !== "legacy";
|
|
1971
|
+
const qrRefreshSeconds = !animated ? void 0 : typeof started.qr_refresh_seconds === "number" && started.qr_refresh_seconds > 0 ? started.qr_refresh_seconds : DEFAULT_QR_REFRESH_SECONDS;
|
|
1972
|
+
surface.requestId = requestId;
|
|
1973
|
+
surface.pairUrl = started.pair_url;
|
|
1974
|
+
surface.qrUrl = qrBase;
|
|
1975
|
+
surface.appLink = false;
|
|
1976
|
+
const withSurface = (s) => ({
|
|
1977
|
+
...s,
|
|
1978
|
+
pairUrl: surface.pairUrl,
|
|
1979
|
+
qrUrl: surface.qrUrl,
|
|
1980
|
+
qrRefreshSeconds,
|
|
1981
|
+
appLink: false,
|
|
1982
|
+
intent,
|
|
1983
|
+
cancel
|
|
1984
|
+
});
|
|
1985
|
+
let lastPolled = { status: "pending", expiresIn: started.expires_in };
|
|
1986
|
+
const initial = withSurface(lastPolled);
|
|
1987
|
+
options.onState?.(initial);
|
|
1988
|
+
if ((options.pairingUI ?? "modal") === "modal") {
|
|
1989
|
+
modal = mountPairingModal(initial, {
|
|
1990
|
+
onCancel: cancel,
|
|
1991
|
+
intent,
|
|
1992
|
+
locale: options.locale,
|
|
1993
|
+
theme: options.theme,
|
|
1994
|
+
timeoutMs: options.pairingTimeoutMs
|
|
1995
|
+
});
|
|
1996
|
+
}
|
|
1997
|
+
if (qrRefreshSeconds !== void 0) {
|
|
1998
|
+
const periodMs = qrRefreshSeconds * 1e3;
|
|
1999
|
+
let due = Date.now() + periodMs;
|
|
2000
|
+
let timer;
|
|
2001
|
+
let stopped = false;
|
|
2002
|
+
const emit = () => {
|
|
2003
|
+
timer = void 0;
|
|
2004
|
+
surface.qrUrl = `${qrBase}?t=${Date.now()}`;
|
|
2005
|
+
const next = withSurface(lastPolled);
|
|
2006
|
+
modal?.update(next);
|
|
2007
|
+
options.onState?.(next);
|
|
2008
|
+
if (stopped) return;
|
|
2009
|
+
due = Date.now() + periodMs;
|
|
2010
|
+
timer = setTimeout(emit, periodMs);
|
|
2011
|
+
};
|
|
2012
|
+
const onVisible = () => {
|
|
2013
|
+
if (document.visibilityState === "visible" && timer !== void 0 && Date.now() >= due) {
|
|
2014
|
+
clearTimeout(timer);
|
|
2015
|
+
emit();
|
|
2016
|
+
}
|
|
2017
|
+
};
|
|
2018
|
+
const hasDocument = typeof document !== "undefined";
|
|
2019
|
+
if (hasDocument) document.addEventListener("visibilitychange", onVisible);
|
|
2020
|
+
stopRefresh = () => {
|
|
2021
|
+
stopped = true;
|
|
2022
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
2023
|
+
timer = void 0;
|
|
2024
|
+
if (hasDocument) document.removeEventListener("visibilitychange", onVisible);
|
|
2025
|
+
stopRefresh = () => {
|
|
2026
|
+
};
|
|
2027
|
+
};
|
|
2028
|
+
timer = setTimeout(emit, Math.max(0, due - Date.now()));
|
|
2029
|
+
}
|
|
2030
|
+
code = await pollUntilApproved(
|
|
2031
|
+
issuer,
|
|
2032
|
+
requestId,
|
|
2033
|
+
(s) => {
|
|
2034
|
+
lastPolled = s;
|
|
2035
|
+
if (s.status !== "pending") stopRefresh();
|
|
2036
|
+
const next = withSurface(s);
|
|
2037
|
+
modal?.update(next);
|
|
2038
|
+
options.onState?.(next);
|
|
2039
|
+
},
|
|
2040
|
+
controller.signal
|
|
2041
|
+
);
|
|
2042
|
+
}
|
|
2043
|
+
teardown();
|
|
1826
2044
|
}
|
|
1827
|
-
teardown();
|
|
1828
2045
|
if (flow === "auth-code") {
|
|
1829
2046
|
const response2 = {
|
|
1830
2047
|
code,
|