@auth-link/web 1.1.0 → 2.0.0

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,12 @@
1
1
  # Changelog
2
2
 
3
+ ## 2.0.0 — 2026-11-13
4
+
5
+ - Use the responsive minimal layout for every JavaScript, React and Vue binding/verification dialog.
6
+ - **Breaking:** remove the variant option/prop, modal.setVariant() and DialogVariant type. Remove these from integrations when upgrading from 1.x.
7
+ - Remove split/compact layouts and their CSS; keep the existing minimal layout, language switching and verification behavior.
8
+
9
+
3
10
  ## 1.1.0 — 2026-11-13
4
11
 
5
12
  - Add openPasscode, createPasscodeClient and React/Vue AuthLinkPasscode components for direct six-digit rotating-code verification.
package/README.md CHANGED
@@ -4,6 +4,10 @@
4
4
 
5
5
  ## English
6
6
 
7
+ ### Upgrading from 1.x
8
+
9
+ All dialogs now use the minimal layout. Remove the variant option/prop, modal.setVariant() calls and DialogVariant type imports. Language switching and verification callbacks are unchanged.
10
+
7
11
  ### Direct rotating-code verification
8
12
 
9
13
  The app directly displays a six-digit rotating code. No per-action approval, authorization requestId, businessId or polling is required. First-time enrollment still scans a QR and verifies the phone; entering its current code on the website completes binding.
@@ -11,7 +15,7 @@ The app directly displays a six-digit rotating code. No per-action approval, aut
11
15
  The CMS backend must reserve one immutable pending action per session before opening the prompt. Another tab cannot replace it. Obtain the user from the server session and check CSRF and business permissions. Verify the passcode on the server and atomically commit verificationId with the saved operation before returning success. onVerified only updates the UI.
12
16
 
13
17
  ```sh
14
- npm install @auth-link/web@^1.1.0
18
+ npm install @auth-link/web@^2.0.0
15
19
  ```
16
20
 
17
21
  ```ts
@@ -19,31 +23,31 @@ import { openPasscode, createPasscodeClient } from '@auth-link/web';
19
23
  import '@auth-link/web/styles.css';
20
24
  const client = createPasscodeClient({ baseUrl: '/api', csrfToken: () => csrfToken });
21
25
  const modal = openPasscode({
22
- client, expiresAt: pending.expiresAt, locale: 'zh-CN', variant: 'B',
26
+ client, expiresAt: pending.expiresAt, locale: 'zh-CN',
23
27
  // First-time enrollment only:
24
28
  // bindingQr: pending.bindingQr, authLinkOrigin: 'https://api.getauthlink.com',
25
29
  onVerified: result => updateUI(result.verificationId),
26
30
  });
27
- // modal.setLocale('en'); modal.setVariant('C'); modal.destroy();
31
+ // modal.setLocale('en'); modal.destroy();
28
32
  ```
29
33
 
30
34
  The default client posts only {passcode} to POST /api/auth with current CMS credentials and CSRF. To submit a form at the same time, supply your own client.verify callback and send {form,passcode} to your CMS. The CMS checks the form against its saved snapshot; never send tenant keys or passcode seeds to the browser.
31
35
 
32
36
  ### Options and callbacks
33
37
 
34
- Required: client.verify(passcode, signal) and expiresAt (the pending CMS action deadline). Optional: locale, variant A/B/C, bindingQr and authLinkOrigin for first enrollment only, onVerified, onError, onClose, onExpired. Success contains status, verificationId, authenticationPolicy and expiresAt; it has no requestId or businessId.
38
+ Required: client.verify(passcode, signal) and expiresAt (the pending CMS action deadline). Optional: locale, bindingQr and authLinkOrigin for first enrollment only, onVerified, onError, onClose, onExpired. Success contains status, verificationId, authenticationPolicy and expiresAt; it has no requestId or businessId.
35
39
 
36
40
  Cookie authentication requires CSRF. Bearer authentication uses accessToken with credentials=omit. For a cross-origin CMS explicitly set allowedBackendOrigins. Bare HTTPS origins are required outside loopback development.
37
41
 
38
42
  ### React and Vue
39
43
 
40
- The React component takes the same options. Vue emits verified, error, close and expired. Keep client references stable. Thirteen languages remain supported; Simplified Chinese is the default. A is minimal, B is split, C is compact.
44
+ The React component takes the same options. Vue emits verified, error, close and expired. Keep client references stable. Thirteen languages remain supported; Simplified Chinese is the default. All dialogs use one responsive minimal layout.
41
45
 
42
46
  ```tsx
43
47
  import { AuthLinkPasscode } from '@auth-link/web/react';
44
48
  // Keep client stable (for example with useMemo).
45
49
  <AuthLinkPasscode client={client} expiresAt={pending.expiresAt}
46
- locale="zh-CN" variant="B" onVerified={updateUI} />
50
+ locale="zh-CN" onVerified={updateUI} />
47
51
  ```
48
52
  ```vue
49
53
  <script setup>
@@ -51,7 +55,7 @@ import { AuthLinkPasscode } from '@auth-link/web/vue';
51
55
  </script>
52
56
  <template>
53
57
  <AuthLinkPasscode :client="client" :expires-at="pending.expiresAt"
54
- locale="zh-CN" variant="B" @verified="updateUI" />
58
+ locale="zh-CN" @verified="updateUI" />
55
59
  </template>
56
60
  ```
57
61
 
@@ -67,6 +71,10 @@ The older openAuthLink / Ticket / Consume workflow remains available for the ear
67
71
 
68
72
  ## 简体中文
69
73
 
74
+ ### 从 1.x 升级
75
+
76
+ 所有弹窗统一使用极简布局。请删除 variant 参数或组件属性、modal.setVariant() 调用和 DialogVariant 类型导入。语言切换与验证回调保持不变。
77
+
70
78
  ### 直接验证动态码
71
79
 
72
80
  App 直接显示六位动态码。日常验证不需要逐次批准、授权 requestId、businessId 或轮询。首次绑定仍需扫码验证设备,在网站输入 App 当前动态码后才完成绑定。
@@ -74,7 +82,7 @@ App 直接显示六位动态码。日常验证不需要逐次批准、授权 req
74
82
  打开弹窗前,CMS 后台必须为会话原子保留唯一且不可变的待办。其他标签页不能覆盖它。用户来自服务端会话,后台检查 CSRF、业务权限和原表单,验证动态码后将 verificationId 与原操作原子提交,再返回成功。onVerified 只更新界面。
75
83
 
76
84
  ```sh
77
- npm install @auth-link/web@^1.1.0
85
+ npm install @auth-link/web@^2.0.0
78
86
  ```
79
87
 
80
88
  ```ts
@@ -82,31 +90,31 @@ import { openPasscode, createPasscodeClient } from '@auth-link/web';
82
90
  import '@auth-link/web/styles.css';
83
91
  const client = createPasscodeClient({ baseUrl: '/api', csrfToken: () => csrfToken });
84
92
  const modal = openPasscode({
85
- client, expiresAt: pending.expiresAt, locale: 'zh-CN', variant: 'B',
93
+ client, expiresAt: pending.expiresAt, locale: 'zh-CN',
86
94
  // First-time enrollment only:
87
95
  // bindingQr: pending.bindingQr, authLinkOrigin: 'https://api.getauthlink.com',
88
96
  onVerified: result => updateUI(result.verificationId),
89
97
  });
90
- // modal.setLocale('en'); modal.setVariant('C'); modal.destroy();
98
+ // modal.setLocale('en'); modal.destroy();
91
99
  ```
92
100
 
93
101
  默认客户端向 CMS 的 POST /api/auth 提交 {passcode},携带当前会话与 CSRF。要随表单提交,可自行实现 client.verify,向 CMS 发送 {form,passcode};后台核对原保存快照。租户密钥与动态码密钥不能进入浏览器。
94
102
 
95
103
  ### 选项与回调
96
104
 
97
- 必填:client.verify(passcode, signal)、expiresAt(CMS 待办截止时间)。可选:locale、variant A/B/C、仅首次绑定使用的 bindingQr 与 authLinkOrigin、onVerified、onError、onClose、onExpired。成功结果含 status、verificationId、authenticationPolicy、expiresAt,不含 requestId 或 businessId。
105
+ 必填:client.verify(passcode, signal)、expiresAt(CMS 待办截止时间)。可选:locale、仅首次绑定使用的 bindingQr 与 authLinkOrigin、onVerified、onError、onClose、onExpired。成功结果含 status、verificationId、authenticationPolicy、expiresAt,不含 requestId 或 businessId。
98
106
 
99
107
  Cookie 模式必须配置 CSRF。Bearer 模式配置 accessToken 且 credentials=omit。跨域 CMS 必须显式配置 allowedBackendOrigins;回环开发之外使用 HTTPS。
100
108
 
101
109
  ### React 与 Vue
102
110
 
103
- React 组件使用相同选项,Vue 发出 verified、error、close、expired 事件。client 引用需保持稳定。支持 13 种语言,默认简体中文;A 极简、B 分栏、C 紧凑。
111
+ React 组件使用相同选项,Vue 发出 verified、error、close、expired 事件。client 引用需保持稳定。支持 13 种语言,默认简体中文;统一使用响应式极简布局。
104
112
 
105
113
  ```tsx
106
114
  import { AuthLinkPasscode } from '@auth-link/web/react';
107
115
  // Keep client stable (for example with useMemo).
108
116
  <AuthLinkPasscode client={client} expiresAt={pending.expiresAt}
109
- locale="zh-CN" variant="B" onVerified={updateUI} />
117
+ locale="zh-CN" onVerified={updateUI} />
110
118
  ```
111
119
  ```vue
112
120
  <script setup>
@@ -114,7 +122,7 @@ import { AuthLinkPasscode } from '@auth-link/web/vue';
114
122
  </script>
115
123
  <template>
116
124
  <AuthLinkPasscode :client="client" :expires-at="pending.expiresAt"
117
- locale="zh-CN" variant="B" @verified="updateUI" />
125
+ locale="zh-CN" @verified="updateUI" />
118
126
  </template>
119
127
  ```
120
128
 
@@ -130,6 +138,10 @@ destroy() 中止浏览器请求并清理计时器,不触发关闭回调;clos
130
138
 
131
139
  ## 日本語
132
140
 
141
+ ### 1.x からの更新
142
+
143
+ すべてのダイアログはシンプルレイアウトに統一されました。variant オプション・プロパティ、modal.setVariant() の呼び出し、DialogVariant 型のインポートを削除してください。言語切り替えと認証コールバックは変更ありません。
144
+
133
145
  ### 認証コードを直接検証
134
146
 
135
147
  アプリに6桁の認証コードを直接表示します。通常の検証では、毎回の承認、認証用requestId、businessId、ポーリングは不要です。初回はQRで端末を確認し、サイトで現在のコードを検証して連携を完了します。
@@ -137,7 +149,7 @@ destroy() 中止浏览器请求并清理计时器,不触发关闭回调;clos
137
149
  画面を開く前にCMSサーバーでセッションの唯一の保留中操作を原子的に確保します。別タブから上書きさせません。ユーザーはサーバーのセッションから取得し、CSRF、業務権限、元のフォームを確認します。コードを検証し、verificationIdと元の操作を原子的に確定してから成功を返します。onVerifiedは表示更新専用です。
138
150
 
139
151
  ```sh
140
- npm install @auth-link/web@^1.1.0
152
+ npm install @auth-link/web@^2.0.0
141
153
  ```
142
154
 
143
155
  ```ts
@@ -145,31 +157,31 @@ import { openPasscode, createPasscodeClient } from '@auth-link/web';
145
157
  import '@auth-link/web/styles.css';
146
158
  const client = createPasscodeClient({ baseUrl: '/api', csrfToken: () => csrfToken });
147
159
  const modal = openPasscode({
148
- client, expiresAt: pending.expiresAt, locale: 'zh-CN', variant: 'B',
160
+ client, expiresAt: pending.expiresAt, locale: 'zh-CN',
149
161
  // First-time enrollment only:
150
162
  // bindingQr: pending.bindingQr, authLinkOrigin: 'https://api.getauthlink.com',
151
163
  onVerified: result => updateUI(result.verificationId),
152
164
  });
153
- // modal.setLocale('en'); modal.setVariant('C'); modal.destroy();
165
+ // modal.setLocale('en'); modal.destroy();
154
166
  ```
155
167
 
156
168
  標準クライアントは現在の認証情報とCSRFを付け、CMSのPOST /api/authへ{passcode}を送信します。フォームも送る場合はclient.verifyで{form,passcode}をCMSへ送信し、サーバーで保存済み内容と照合します。テナントキーやコード生成用秘密はブラウザーへ渡しません。
157
169
 
158
170
  ### オプションとコールバック
159
171
 
160
- 必須はclient.verify(passcode, signal)とexpiresAt(CMS操作の期限)です。任意でlocale、variant A/B/C、初回用bindingQrとauthLinkOrigin、onVerified、onError、onClose、onExpiredを指定します。成功結果はstatus、verificationId、authenticationPolicy、expiresAtを含み、requestIdやbusinessIdは含みません。
172
+ 必須はclient.verify(passcode, signal)とexpiresAt(CMS操作の期限)です。任意でlocale、初回用bindingQrとauthLinkOrigin、onVerified、onError、onClose、onExpiredを指定します。成功結果はstatus、verificationId、authenticationPolicy、expiresAtを含み、requestIdやbusinessIdは含みません。
161
173
 
162
174
  Cookie認証ではCSRFが必須です。Bearer認証はaccessTokenとcredentials=omitを使います。別オリジンのCMSはallowedBackendOriginsを明示し、ループバック開発以外ではHTTPSを使います。
163
175
 
164
176
  ### ReactとVue
165
177
 
166
- Reactは同じオプションを使い、Vueはverified、error、close、expiredイベントを発行します。clientの参照を安定させてください。13言語に対応し、既定は簡体字中国語です。Aはシンプル、Bは分割、Cはコンパクト表示です。
178
+ Reactは同じオプションを使い、Vueはverified、error、close、expiredイベントを発行します。clientの参照を安定させてください。13言語に対応し、既定は簡体字中国語です。すべてのダイアログはレスポンシブなシンプルレイアウトを使用します。
167
179
 
168
180
  ```tsx
169
181
  import { AuthLinkPasscode } from '@auth-link/web/react';
170
182
  // Keep client stable (for example with useMemo).
171
183
  <AuthLinkPasscode client={client} expiresAt={pending.expiresAt}
172
- locale="zh-CN" variant="B" onVerified={updateUI} />
184
+ locale="zh-CN" onVerified={updateUI} />
173
185
  ```
174
186
  ```vue
175
187
  <script setup>
@@ -177,7 +189,7 @@ import { AuthLinkPasscode } from '@auth-link/web/vue';
177
189
  </script>
178
190
  <template>
179
191
  <AuthLinkPasscode :client="client" :expires-at="pending.expiresAt"
180
- locale="zh-CN" variant="B" @verified="updateUI" />
192
+ locale="zh-CN" @verified="updateUI" />
181
193
  </template>
182
194
  ```
183
195
 
package/dist/index.js CHANGED
@@ -2392,12 +2392,9 @@ function getMessages(locale) {
2392
2392
 
2393
2393
  // src/view.ts
2394
2394
  var node = (tag, props, ...children) => ({ tag, props, children });
2395
- function resolveVariant(variant) {
2396
- return variant === "B" || variant === "C" ? variant : "A";
2397
- }
2398
2395
  function widgetView(options) {
2399
2396
  const { snapshot: s, mode, retry, refresh } = options;
2400
- const locale = resolveLocale(options.locale), variant = resolveVariant(options.variant);
2397
+ const locale = resolveLocale(options.locale);
2401
2398
  const l = getMessages(locale);
2402
2399
  const pending = s.status === "pending", hasQr = pending && (mode === "bind" || Boolean(options.showQr)) && Boolean(options.qr);
2403
2400
  const status = { idle: l.loading, pending: options.passcodeMode ? l.otpWaiting : l.waiting, confirmed: l.confirmed, rejected: l.rejected, expired: l.expired, error: l.error }[s.status];
@@ -2414,12 +2411,6 @@ function widgetView(options) {
2414
2411
  expiring ? l.expiring : status,
2415
2412
  ...expiring ? [node("span", { class: "authlink-expiry", "aria-hidden": "true" }, ` \xB7 ${expiryTime}`)] : []
2416
2413
  );
2417
- const cancel = () => options.close ? [node("button", {
2418
- type: "button",
2419
- class: "authlink-cancel",
2420
- "data-authlink-focus": "cancel",
2421
- onClick: options.close
2422
- }, l.cancel)] : [];
2423
2414
  const children = [node("h3", { class: "authlink-title" }, l[mode])];
2424
2415
  if (hasQr) children.push(node("p", { class: "authlink-subtitle" }, l.scan));
2425
2416
  if (pending) {
@@ -2462,9 +2453,7 @@ function widgetView(options) {
2462
2453
  ...s.error ? [node("p", { class: "authlink-input-error", role: "alert" }, options.passcodeMode ? s.error.message === "OTP_NOT_CONFIGURED" ? l.otpSetup : l.otpError : l.codeError)] : []
2463
2454
  ];
2464
2455
  children.push(node("div", { class: "authlink-body" }, ...qr, node("div", { class: "authlink-details" }, ...detail)));
2465
- if (variant === "A") children.push(node("div", { class: "authlink-footer" }, statusNode()));
2466
- if (variant === "B") children.push(node("div", { class: "authlink-footer" }, statusNode(), ...cancel()));
2467
- if (variant === "C") children.push(node("div", { class: "authlink-footer" }, statusNode(), ...cancel()));
2456
+ children.push(node("div", { class: "authlink-footer" }, statusNode()));
2468
2457
  } else {
2469
2458
  children.push(statusNode());
2470
2459
  if (s.error?.message === "CONFIRMATION_CODE_LOCKED") children.push(node("p", { class: "authlink-input-error", role: "alert" }, l.codeLocked));
@@ -2485,7 +2474,6 @@ function widgetView(options) {
2485
2474
  lang: locale,
2486
2475
  class: `authlink-widget ${options.className ?? ""}`.trim(),
2487
2476
  "data-status": s.status,
2488
- "data-variant": variant,
2489
2477
  "data-has-qr": String(hasQr),
2490
2478
  "aria-label": l[mode]
2491
2479
  }, ...children);
@@ -2529,7 +2517,7 @@ function openAuthLink(options) {
2529
2517
  content.className = "authlink-modal-content";
2530
2518
  header.append(brand, closeButton);
2531
2519
  dialog.append(header, content);
2532
- let locale = options.locale, variant = resolveVariant(options.variant), snapshot = initialSnapshot(), closed = false;
2520
+ let locale = options.locale, snapshot = initialSnapshot(), closed = false;
2533
2521
  let abort;
2534
2522
  let inputCode = "";
2535
2523
  let submitCode;
@@ -2559,7 +2547,6 @@ function openAuthLink(options) {
2559
2547
  const language = resolveLocale(locale), messages2 = getMessages(language);
2560
2548
  dialog.lang = language;
2561
2549
  dialog.setAttribute("aria-label", messages2[mode]);
2562
- dialog.dataset.variant = variant;
2563
2550
  dialog.dataset.mode = mode;
2564
2551
  dialog.dataset.hasQr = String(snapshot.status === "pending" && (mode === "bind" || Boolean(options.showQr)));
2565
2552
  closeButton.setAttribute("aria-label", messages2.close);
@@ -2568,7 +2555,6 @@ function openAuthLink(options) {
2568
2555
  const next = element(widgetView({
2569
2556
  mode,
2570
2557
  locale,
2571
- variant,
2572
2558
  snapshot,
2573
2559
  qr: image,
2574
2560
  showQr: options.showQr,
@@ -2578,8 +2564,7 @@ function openAuthLink(options) {
2578
2564
  },
2579
2565
  submitCode: (code) => submitCode?.(code) ?? Promise.resolve(),
2580
2566
  retry,
2581
- refresh: options.onRefresh,
2582
- close: () => dispose(true)
2567
+ refresh: options.onRefresh
2583
2568
  }));
2584
2569
  const oldCode = content.querySelector(".authlink-code");
2585
2570
  const nextCode = next.querySelector(".authlink-code");
@@ -2692,10 +2677,6 @@ function openAuthLink(options) {
2692
2677
  locale = value;
2693
2678
  render();
2694
2679
  },
2695
- setVariant: (value) => {
2696
- variant = resolveVariant(value);
2697
- render();
2698
- },
2699
2680
  retry
2700
2681
  };
2701
2682
  }
@@ -2744,7 +2725,7 @@ function openPasscode(options) {
2744
2725
  content.className = "authlink-modal-content";
2745
2726
  header.append(brand, close);
2746
2727
  dialog.append(header, content);
2747
- let locale = options.locale, variant = resolveVariant(options.variant), closed = false, busy = false, code = "", error;
2728
+ let locale = options.locale, closed = false, busy = false, code = "", error;
2748
2729
  const mode = options.bindingQr ? "bind" : "confirm";
2749
2730
  let image;
2750
2731
  if (options.bindingQr) {
@@ -2771,7 +2752,6 @@ function openPasscode(options) {
2771
2752
  if (closed) return;
2772
2753
  const l = getMessages(locale);
2773
2754
  dialog.lang = resolveLocale(locale);
2774
- dialog.dataset.variant = variant;
2775
2755
  dialog.dataset.mode = mode;
2776
2756
  dialog.dataset.hasQr = String(Boolean(image));
2777
2757
  dialog.setAttribute("aria-label", l[mode]);
@@ -2782,7 +2762,6 @@ function openPasscode(options) {
2782
2762
  content.replaceChildren(element(widgetView({
2783
2763
  mode,
2784
2764
  locale,
2785
- variant,
2786
2765
  passcodeMode: true,
2787
2766
  snapshot,
2788
2767
  qr: image,
@@ -2795,8 +2774,7 @@ function openPasscode(options) {
2795
2774
  retry: () => {
2796
2775
  error = void 0;
2797
2776
  render();
2798
- },
2799
- close: () => dispose(true)
2777
+ }
2800
2778
  })));
2801
2779
  if (focused) content.querySelector("input")?.focus();
2802
2780
  };
@@ -2858,10 +2836,6 @@ function openPasscode(options) {
2858
2836
  locale = value;
2859
2837
  render();
2860
2838
  },
2861
- setVariant: (value) => {
2862
- variant = resolveVariant(value);
2863
- render();
2864
- },
2865
2839
  retry: () => {
2866
2840
  error = void 0;
2867
2841
  render();