@zoreal/oauth2-js 0.1.22 → 0.1.24

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 CHANGED
@@ -157,7 +157,7 @@ step with the pairing, and takes it down when the flow settles. You render
157
157
  nothing.
158
158
 
159
159
  The code on screen changes every few seconds. Each image is one frame of the
160
- pairing, and the provider refuses a frame more than 30 seconds old, so a
160
+ pairing, and the provider refuses a frame the sequence has moved past, so a
161
161
  screenshot of the code passed to someone else is already dead when it arrives:
162
162
  signing in needs the screen as it is right now. The provider renders the
163
163
  frames and this package re-fetches the image on the interval the provider
@@ -170,7 +170,7 @@ is nothing to configure and nothing to draw.
170
170
  | **Live status** | Copy and title follow the pairing: waiting for a scan, then waiting for approval once the code is claimed (the spent QR blurs out behind a phone glyph). |
171
171
  | **Title** | Says what the scan is for, inferred from the request: "Scan to sign in" for `openid`, `email` and `profile.name`; "Scan to verify your identity" once a document attribute such as `zoreal.age` or `profile.birthdate` is requested; "Scan to prove you are a real human" for `openid` alone with `acr_values: 'zoreal.live'`. Override with `intent`, one of `'sign-in'`, `'identify'`, `'presence'`, when the scope does not say. |
172
172
  | **Countdown** | Counts down to expiry, turning amber under 20s. Reads the clock each tick rather than decrementing, so a backgrounded tab comes back honest. |
173
- | **Timeout** | Closes and cancels at zero. Defaults to 120s; override with `pairingTimeoutMs`. The provider's own expiry wins when it is shorter. |
173
+ | **Timeout** | Closes and cancels at zero. Follows the provider's expiry (five minutes); `pairingTimeoutMs` can shorten it, never extend it. |
174
174
  | **Cancel** | The X, the Cancel button, `Escape`, clicking outside and the timeout are one behaviour: abort the poll, close, reject the promise with `AbortError`. |
175
175
  | **No ZOREAL ID yet** | A footer says the same code also installs the app. Without it the panel reads as "scan this with something I do not have", and the flow dead-ends at the one moment it can still be recovered. |
176
176
  | **Themes** | `theme: 'auto'` (default) follows `prefers-color-scheme`; `'light'` and `'dark'` force it. In dark mode the code is drawn light on the dark surface, the mark included, and the light around it runs brighter and wider. |
@@ -182,7 +182,7 @@ startLogin({
182
182
  clientId: 'ast_your_asset_id',
183
183
  locale: 'sv', // omit to follow the browser
184
184
  theme: 'auto', // 'light' | 'dark'
185
- pairingTimeoutMs: 120_000,
185
+ pairingTimeoutMs: 120_000, // shorten the provider's five minutes; omit to keep them
186
186
  });
187
187
  ```
188
188
 
@@ -406,7 +406,7 @@ enforced where enforcement counts: on your backend, against the verified token.
406
406
  | `unsafeClaims(idToken)` | reads claims without verifying. Convenience only; verification happens server-side |
407
407
  | `isMobileUserAgent()` | whether this user agent gets the app link rather than a QR |
408
408
  | `mountPairingModal(state, { onCancel, locale?, theme?, timeoutMs? })` | mounts the dialog yourself, for `pairingUI: 'none'` callers who still want the real one. Returns `{ update, close }`, or `null` outside a browser |
409
- | `DEFAULT_PAIRING_TIMEOUT_MS` | `120000`, the modal's default cap |
409
+ | `DEFAULT_PAIRING_TIMEOUT_MS` | `300000`, the modal's cap when the provider states no expiry |
410
410
  | `DEFAULT_QR_REFRESH_SECONDS` | `3`, how often the QR frame is re-fetched when the provider does not say |
411
411
 
412
412
  Errors: `OAuthFlowError` (the provider refused; `error` is the OAuth code,
@@ -417,7 +417,7 @@ provider). `cancel()` rejects with a `DOMException` named `AbortError`.
417
417
 
418
418
  `startLogin` options controlling the built-in modal: `pairingUI`
419
419
  (`'modal'` default, `'none'` to render your own), `theme` (`'auto'` default,
420
- `'light'`, `'dark'`), `pairingTimeoutMs` (120000 default), and `locale`, which
420
+ `'light'`, `'dark'`), `pairingTimeoutMs` (the provider's expiry by default), and `locale`, which
421
421
  is sent to the provider AND picks the modal's own language. See
422
422
  [The pairing modal](#the-pairing-modal).
423
423
 
package/dist/index.cjs CHANGED
@@ -61,7 +61,7 @@ function unsafeClaims(idToken) {
61
61
 
62
62
  // src/wire.ts
63
63
  var WIRE_VERSION = 1;
64
- var SDK_VERSION = "0.1.22";
64
+ var SDK_VERSION = "0.1.24";
65
65
  var SDK_NAME = "@zoreal/oauth2-js";
66
66
  var DEFAULT_ISSUER = "https://id.zoreal.com";
67
67
  var POLL_INTERVAL_MS = 2e3;
@@ -717,9 +717,16 @@ var SVG_NS = "http://www.w3.org/2000/svg";
717
717
  var TAIL = 0.3;
718
718
  var STACK = Array.from({ length: 12 }, (_, i) => 12 - i);
719
719
  var HALO = [3, 2, 1];
720
- function radiusOf(control) {
721
- const value = parseFloat(getComputedStyle(control).borderTopLeftRadius);
722
- return Number.isFinite(value) ? value : 8;
720
+ function cornerOf(radius, width, height) {
721
+ const raw = radius.trim();
722
+ const value = parseFloat(raw);
723
+ const offset = 2;
724
+ if (raw.endsWith("%") && Number.isFinite(value)) {
725
+ return [width * value / 100 + offset, height * value / 100 + offset];
726
+ }
727
+ const px = Number.isFinite(value) ? value : /infinity/i.test(raw) ? Infinity : 8;
728
+ const r = Math.min(px, width / 2, height / 2) + offset;
729
+ return [r, r];
723
730
  }
724
731
  function holdBusy(control) {
725
732
  if (typeof document === "undefined") return () => {
@@ -740,12 +747,11 @@ function holdBusy(control) {
740
747
  overlay.setAttribute("aria-hidden", "true");
741
748
  const svg2 = document.createElementNS(SVG_NS, "svg");
742
749
  svg2.setAttribute("class", cx("ring-svg"));
743
- const rx = radiusOf(control) + 2;
750
+ const rects = [];
744
751
  const layer = (name, len, alpha) => {
745
752
  const rect = document.createElementNS(SVG_NS, "rect");
746
753
  rect.setAttribute("class", cx(name));
747
- rect.setAttribute("rx", String(rx));
748
- rect.setAttribute("ry", String(rx));
754
+ rects.push(rect);
749
755
  rect.style.strokeDasharray = `calc(var(--zrl-l) * ${len}) calc(var(--zrl-l) * ${1 - len})`;
750
756
  rect.style.setProperty("--zrl-s", `calc(var(--zrl-l) * ${-(TAIL - len)})`);
751
757
  rect.style.opacity = alpha;
@@ -768,6 +774,11 @@ function holdBusy(control) {
768
774
  overlay.style.top = `${box.top}px`;
769
775
  overlay.style.width = `${box.width}px`;
770
776
  overlay.style.height = `${box.height}px`;
777
+ const [rx, ry] = cornerOf(getComputedStyle(control).borderTopLeftRadius, box.width, box.height);
778
+ for (const rect of rects) {
779
+ rect.setAttribute("rx", String(rx));
780
+ rect.setAttribute("ry", String(ry));
781
+ }
771
782
  const length = typeof measured?.getTotalLength === "function" ? measured.getTotalLength() : 0;
772
783
  if (length > 0) overlay.style.setProperty("--zrl-ring-len", `${length}px`);
773
784
  };
@@ -1751,7 +1762,7 @@ function interpolate(template, time) {
1751
1762
  }
1752
1763
 
1753
1764
  // src/modal.ts
1754
- var DEFAULT_PAIRING_TIMEOUT_MS = 12e4;
1765
+ var DEFAULT_PAIRING_TIMEOUT_MS = 3e5;
1755
1766
  var URGENT_SECONDS = 20;
1756
1767
  function mmss(totalSeconds) {
1757
1768
  const m = Math.floor(totalSeconds / 60);
@@ -1823,7 +1834,7 @@ function mountPairingModal(state, options) {
1823
1834
  if (typeof document === "undefined") return null;
1824
1835
  ensureStyles();
1825
1836
  const t = strings(options.locale);
1826
- const timeoutMs = options.timeoutMs ?? DEFAULT_PAIRING_TIMEOUT_MS;
1837
+ const timeoutMs = options.timeoutMs;
1827
1838
  let closed = false;
1828
1839
  const scrim = el("div", `${cx("root")} ${cx("scrim")}`);
1829
1840
  scrim.dataset.theme = options.theme ?? "auto";
@@ -1881,7 +1892,8 @@ function mountPairingModal(state, options) {
1881
1892
  card.append(closeBtn, body, help, footer);
1882
1893
  scrim.appendChild(card);
1883
1894
  const serverMs = typeof state.expiresIn === "number" ? state.expiresIn * 1e3 : Infinity;
1884
- const deadline = Date.now() + Math.min(timeoutMs, serverMs);
1895
+ const capMs = timeoutMs ?? (Number.isFinite(serverMs) ? serverMs : DEFAULT_PAIRING_TIMEOUT_MS);
1896
+ const deadline = Date.now() + Math.min(capMs, serverMs);
1885
1897
  let shown = state.qrUrl;
1886
1898
  let loading = null;
1887
1899
  const spent = () => qr.dataset.spent === "true";