@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 +21 -0
- package/README.md +20 -6
- package/dist/base.d.ts +35 -0
- package/dist/login.d.ts +10 -2
- package/dist/qrauth-components.esm.js +45 -45
- package/dist/qrauth-components.js +45 -45
- package/package.json +6 -6
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,
|
|
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,
|
|
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
|
|
22
|
-
integrity="sha384
|
|
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
|
|
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
|
-
*
|
|
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;
|