@qrauth/web-components 0.4.1 → 0.4.3

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/CHANGELOG.md CHANGED
@@ -1,5 +1,26 @@
1
1
  # @qrauth/web-components
2
2
 
3
+ ## [0.4.3] — 2026-10-22
4
+
5
+ ### Changed
6
+ - Rebuilt with esbuild 0.28.2 (was 0.28.0). The bundler runtime helper differs; no attribute, event or behaviour change. Bundle size unchanged within 0.1 KB.
7
+
8
+ ## [0.4.2] — 2026-10-13
9
+
10
+ ### Security
11
+ - **Server error text is rendered as text.** All three components put the API's error message into the page with `textContent`. Before, it was interpolated into a template assigned to `innerHTML`, so markup in a message (the API used to echo the `redirect-uri` value back) was parsed in the embedding site's origin.
12
+ - **`<qrauth-login>` and `<qrauth-2fa>` send the PKCE verifier in the `X-QRAuth-Code-Verifier` header** when polling, not in the URL. A query string is written to proxy and server access logs. Requires QRAuth API ≥ 2026-10-13. Against an older API the header is ignored and the approved result comes back without the user; the components now report that as `qrauth:error` instead of a sign-in with no identity.
13
+ - **`<qrauth-login redirect-url>` only follows http(s) and relative URLs.** Any other scheme (`javascript:`, `data:`, ...) is ignored with a console warning. This includes custom app schemes (`myapp://`), which were never documented but did work.
14
+ - **A throwing `on-auth` callback no longer stops the redirect or the modal from closing**, and only an own property of `window` is called (not an inherited one such as `constructor`).
15
+
16
+ ### Docs
17
+ - `on-auth` is documented: it names a function on `window`, which is looked up and called with the session result. Set it only from your own markup. The `qrauth:authenticated` event carries the same data and needs no global.
18
+
19
+ ### Notes
20
+ - No attribute or event changed. Sites that embed the unpinned `components.js` pick this up automatically; sites pinned to a versioned file with an SRI hash need the new file name and hash from `https://cdn.qrauth.io/v1/latest.json`.
21
+ - If your site sets a Content-Security-Policy, `style-src` must allow `'unsafe-inline'` for the components' shadow-DOM styles.
22
+ - Bundle size: 20.8 KB gzipped IIFE / 20.8 KB gzipped ESM (0.4.1: 20.3 KB).
23
+
3
24
  ## [0.4.1] — 2026-04-29
4
25
 
5
26
  ### Fixed
package/README.md CHANGED
@@ -1,10 +1,14 @@
1
1
  # @qrauth/web-components
2
2
 
3
- Drop-in Web Components for [QRAuth](https://qrauth.io) — passwordless QR authentication, 2FA, and ephemeral delegated access. Shadow DOM, framework-free, ~10 KB gzipped.
3
+ Drop-in Web Components for [QRAuth](https://qrauth.io) — passwordless QR authentication, 2FA, and ephemeral delegated access. Shadow DOM, framework-free, 20.8 KB gzipped (0.4.3).
4
+
5
+ <a href="https://openid.net/certification/certified-openid-providers-profiles/"><img src="https://qrauth.io/assets/certifications/openid-certified.png" alt="OpenID Certified" width="180" /></a>
6
+
7
+ QRAuth is an **OpenID Certified™** OpenID Provider (Basic OP + Config OP profiles).
4
8
 
5
9
  - `<qrauth-login>` — passwordless sign-in, button or inline. Mobile-aware pending state (tap-to-continue CTA with QR demoted under a "Use another device" expander).
6
10
  - `<qrauth-2fa>` — drop-in second-factor step with session-token binding.
7
- - `<qrauth-ephemeral>` — time-limited, device-bound access QRs.
11
+ - `<qrauth-ephemeral>` — time-limited access QRs, optionally bound to a device fingerprint.
8
12
 
9
13
  All three components bubble `qrauth:*` events across the Shadow DOM boundary (`composed: true`) so any framework can listen with plain `addEventListener`.
10
14
 
@@ -18,8 +22,8 @@ Or load the CDN IIFE directly in any HTML page (latest hashes at [`https://cdn.q
18
22
 
19
23
  ```html
20
24
  <script
21
- src="https://cdn.qrauth.io/v1/components-0.4.0.js"
22
- integrity="sha384-ZsvnpXBK9tghmz/PCtZUtR+7qTF7XhR35/SGNfJuJgLOBxnIRi3JYhRt1oFxNtU6"
25
+ src="https://cdn.qrauth.io/v1/components-<version>.js"
26
+ integrity="<sha384 for that file, from latest.json>"
23
27
  crossorigin="anonymous"
24
28
  ></script>
25
29
 
@@ -51,8 +55,8 @@ Or load the CDN IIFE directly in any HTML page (latest hashes at [`https://cdn.q
51
55
  | `theme` | `light` \| `dark` | `light` | Colour scheme. |
52
56
  | `base-url` | URL | `https://qrauth.io` | API host. Override for staging / local dev. |
53
57
  | `scopes` | space-separated | `identity` | OAuth-style scopes requested. |
54
- | `redirect-url` | URL | — | Auto-redirect after success. |
55
- | `on-auth` | string | — | Name of a global function to invoke on success. |
58
+ | `redirect-url` | URL | — | Auto-redirect after success. Only an http(s) URL or a relative URL is followed; any other scheme is ignored with a console warning. |
59
+ | `on-auth` | string | — | Name of a function on `window`, called with the session result on success. It is looked up by name, so set it only from your own markup, never from user input. The `qrauth:authenticated` event carries the same data and needs no global. |
56
60
  | `display` | `button` \| `inline` | `button` | Modal trigger vs. embedded widget. |
57
61
  | `animated` | boolean | off | Enables the pulse animation on the QR. |
58
62
  | `force-mode` | `mobile` \| `desktop` \| `auto` | `auto` | Override automatic mobile detection. |
@@ -69,6 +73,9 @@ All events bubble across the Shadow DOM (`composed: true`):
69
73
  - `qrauth:denied` — user denied approval on their device
70
74
  - `qrauth:error` — `detail: { message }`
71
75
 
76
+ `qrauth:error` carries `detail.message`, which can be text from the API. Show
77
+ it with `textContent`, never `innerHTML`.
78
+
72
79
  ## Theming
73
80
 
74
81
  Override any of the following CSS custom properties on the host page or on the element itself:
@@ -90,6 +97,13 @@ qrauth-login {
90
97
 
91
98
  Or flip the bundled dark palette via `theme="dark"`.
92
99
 
100
+ ## Content Security Policy
101
+
102
+ The components style themselves inside their shadow root with inline
103
+ `<style>`. If the embedding page sends a Content-Security-Policy, `style-src`
104
+ must allow `'unsafe-inline'`. `connect-src` must allow the QRAuth API origin
105
+ (`https://qrauth.io` unless you set `base-url`).
106
+
93
107
  ## Framework integration
94
108
 
95
109
  The components are framework-free native Custom Elements. They work in React, Vue, Svelte, Angular, plain HTML — wherever the browser exposes `customElements`. For React, wrap event listeners in `useEffect` because React's synthetic event system doesn't observe custom events directly.
package/dist/base.d.ts CHANGED
@@ -8,6 +8,16 @@
8
8
  * Exported for documentation and test mocks.
9
9
  */
10
10
  export declare const MOBILE_MEDIA_QUERY = "(pointer: coarse) and (hover: none)";
11
+ /** Header the API reads the PKCE verifier from when an auth session is polled. */
12
+ export declare const CODE_VERIFIER_HEADER = "X-QRAuth-Code-Verifier";
13
+ /**
14
+ * Where `redirect-url` may send the browser: an http(s) URL, or a relative
15
+ * one resolved against the current page. Anything that does not resolve to
16
+ * http(s), `javascript:` and `data:` in particular, is refused. Returns the
17
+ * absolute URL to navigate to, or null. This is a scheme check, not an
18
+ * origin check: an absolute URL on another site is allowed, as documented.
19
+ */
20
+ export declare function safeRedirectTarget(raw: string | null | undefined, pageUrl: string): string | null;
11
21
  export declare abstract class QRAuthElement extends HTMLElement {
12
22
  protected shadow: ShadowRoot;
13
23
  private _sseConnection;
@@ -43,6 +53,31 @@ export declare abstract class QRAuthElement extends HTMLElement {
43
53
  protected startPolling(url: string, intervalMs: number, callback: (data: unknown) => void, headers?: Record<string, string>): void;
44
54
  protected stopPolling(): void;
45
55
  protected cleanup(): void;
56
+ /**
57
+ * Put a message into the rendered error state as text. The message can come
58
+ * from the API, so it is never parsed as HTML; the template only supplies
59
+ * the element and its default wording.
60
+ */
61
+ protected setErrorText(message: string | null | undefined): void;
62
+ /**
63
+ * True when a PKCE-bound session came back APPROVED without the user: the
64
+ * API did not accept the verifier (an API older than the verifier header
65
+ * ignores it). There is no identity to hand to the page, so this must not
66
+ * be reported as a successful sign-in.
67
+ */
68
+ protected isApprovalWithoutIdentity(sentVerifier: boolean, data: {
69
+ user?: unknown;
70
+ } | undefined): boolean;
71
+ /**
72
+ * Call the function named by an `on-*` attribute. Only an own property of
73
+ * `window` is called, not an inherited one (`constructor`, `toString`), and
74
+ * a throwing callback does not stop the component. Built-ins such as
75
+ * `alert` are own properties of `window` in browsers, so the attribute must
76
+ * still come from the embedding page's own markup, as documented.
77
+ */
78
+ protected callGlobal(name: string | null, payload: unknown): void;
79
+ /** Leave the page. Separate so it can be observed in tests. */
80
+ protected navigate(url: string): void;
46
81
  protected emit(name: string, detail?: unknown): void;
47
82
  protected generateCodeVerifier(): Promise<string>;
48
83
  protected computeCodeChallenge(verifier: string): Promise<string>;
package/dist/login.d.ts CHANGED
@@ -7,8 +7,14 @@ import { QRAuthElement } from './base.js';
7
7
  * theme — "light" (default) | "dark"
8
8
  * base-url — API base URL (default: https://qrauth.io)
9
9
  * scopes — Space-separated scopes (default: "identity")
10
- * redirect-url — Optional redirect after successful auth
11
- * on-auth — Name of a global callback function to call on success
10
+ * redirect-url — Optional redirect after successful auth. Only an
11
+ * http(s) URL or a relative URL is followed; any
12
+ * other scheme is ignored with a console warning.
13
+ * on-auth — Name of a function on `window` to call on success
14
+ * with the session result. It is looked up by name
15
+ * and called, so set it only from your own markup,
16
+ * never from user input. The `qrauth:authenticated`
17
+ * event carries the same data and needs no global.
12
18
  * display — "button" (default) | "inline"
13
19
  * animated — If present, enables subtle QR pulse animation
14
20
  * force-mode — "mobile" | "desktop" | "auto" (default "auto").
@@ -108,6 +114,8 @@ export declare class QRAuthLogin extends QRAuthElement {
108
114
  private _openModal;
109
115
  private _closeModal;
110
116
  private _startAuth;
117
+ /** The scan was approved but the API returned no identity for it. */
118
+ private _failWithoutIdentity;
111
119
  private _retry;
112
120
  private _beginPolling;
113
121
  private _handleStatusChange;