@auth-link/web 1.1.0 → 3.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/docs/README.en.md CHANGED
@@ -1,62 +1,63 @@
1
- # AuthLink Web 1.1
1
+ # AuthLink Web 3.0
2
2
 
3
3
  [English](README.en.md) · [简体中文](README.zh-CN.md) · [日本語](README.ja.md)
4
4
 
5
- ## Direct rotating-code verification
6
-
7
- 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.
8
-
9
- 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.
5
+ ## Install and use
10
6
 
11
7
  ```sh
12
- npm install @auth-link/web@^1.1.0
8
+ npm install @auth-link/web@^3.0.0
13
9
  ```
14
10
 
11
+ Same minimal responsive layout and 13 languages, Simplified Chinese by default. First enrollment shows a QR and a six-digit input; daily verification shows only the input. The SDK makes no HTTP requests, creates no authorization requests and never polls. There is no baseUrl, csrfToken, accessToken or fetch configuration.
12
+
15
13
  ```ts
16
- import { openPasscode, createPasscodeClient } from '@auth-link/web';
14
+ import { openVerify, openBind } from '@auth-link/web';
17
15
  import '@auth-link/web/styles.css';
18
- const client = createPasscodeClient({ baseUrl: '/api', csrfToken: () => csrfToken });
19
- const modal = openPasscode({
20
- client, expiresAt: pending.expiresAt, locale: 'zh-CN', variant: 'B',
21
- // First-time enrollment only:
22
- // bindingQr: pending.bindingQr, authLinkOrigin: 'https://api.getauthlink.com',
23
- onVerified: result => updateUI(result.verificationId),
16
+
17
+ openVerify({
18
+ submit: async (passcode, { signal }) => {
19
+ const result = await cmsApi.verifyAuthLink({ passcode }, { signal });
20
+ if (!result.success) throw new Error(result.message || 'VERIFICATION_FAILED');
21
+ },
22
+ onSuccess: () => refreshPage(),
23
+ });
24
+
25
+ // First enrollment only; the CMS creates the binding ticket first.
26
+ openBind({
27
+ qr: ticket.qr,
28
+ expiresAt: ticket.expiresAt,
29
+ authLinkOrigin: 'https://api.getauthlink.com',
30
+ submit: async (passcode, { signal }) => {
31
+ await cmsApi.completeBinding({ passcode }, { signal });
32
+ // This wrapper must throw on HTTP AND business failure.
33
+ },
34
+ onSuccess: () => refreshAccounts(),
35
+ onRefresh: ({ signal }) => cmsApi.createBinding({ signal }),
24
36
  });
25
- // modal.setLocale('en'); modal.setVariant('C'); modal.destroy();
26
37
  ```
27
38
 
28
- 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.
39
+ The cmsApi functions above belong to your application, not this package. Reuse the CMS request wrapper for authentication, CSRF, CORS and business errors. Cookie authentication still requires your existing CSRF protection. Tenant API keys remain on the CMS server.
29
40
 
30
- ## Options and callbacks
41
+ ## Submit contract
31
42
 
32
- 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.
43
+ `submit(passcode, { signal }): Promise<void>` receives the six digits as a string, preserving leading zeros. Resolve only after CMS verification AND the business operation succeed; throw on HTTP or business failure, including HTTP 200 with success=false. A returned JSON object is not inspected. Do not return {success:false}; throw instead. No verificationId or AuthLink receipt must be exposed to the SDK. CMS validates the receipt and stores verificationId with its business transaction.
33
44
 
34
- 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.
45
+ The SDK blocks duplicate submissions. Failed codes remain editable for explicit retry. Closing/unmounting or action expiry aborts signal; forward it to your request layer. Cancellation cannot undo server work. Keep the original pending action and idempotency key for ambiguous outcomes. Never interpret a timeout as a failed business transaction.
35
46
 
36
- ## React and Vue
47
+ Success closes the prompt and calls onSuccess without arguments. onSuccess only updates UI, never authorizes server work. An onSuccess failure does not reopen or resubmit the operation. onError receives the original Error. close() calls onClose; destroy() cleans up silently. retry() clears errors without network activity. setLocale(locale) changes language while retaining input.
37
48
 
38
- 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.
49
+ ## Time and binding refresh
39
50
 
40
- ```tsx
41
- import { AuthLinkPasscode } from '@auth-link/web/react';
42
- // Keep client stable (for example with useMemo).
43
- <AuthLinkPasscode client={client} expiresAt={pending.expiresAt}
44
- locale="zh-CN" variant="B" onVerified={updateUI} />
45
- ```
46
- ```vue
47
- <script setup>
48
- import { AuthLinkPasscode } from '@auth-link/web/vue';
49
- </script>
50
- <template>
51
- <AuthLinkPasscode :client="client" :expires-at="pending.expiresAt"
52
- locale="zh-CN" variant="B" @verified="updateUI" />
53
- </template>
54
- ```
51
+ openVerify requires only submit; expiresAt is an optional CMS action deadline. There is no artificial five-minute SDK limit. It is NOT a countdown for the App's current code, whose generation time the browser does not know. The App code and receipt remain valid for at most one minute.
52
+
53
+ openBind additionally requires qr (JSON payload, not SVG/URL) and expiresAt returned by CMS. The binding QR lasts up to 60 minutes from creation; scanning and retrying do not extend it. authLinkOrigin optionally checks the expected AuthLink API origin. QR generation is local. onRefresh({signal}) is optional, returns Promise<{qr,expiresAt}>, and runs only when the user clicks refresh after expiry. The CMS owns replacement requests. A failed refresh stays expired and can be retried.
54
+
55
+ ## React and Vue
55
56
 
56
- ## Errors and cleanup
57
+ Import AuthLinkVerify / AuthLinkBind from @auth-link/web/react or @auth-link/web/vue. Options match the JavaScript API; Vue emits success, close, error, expired. onRefresh remains a function prop that returns a ticket. Components render no dialog during SSR and abort browser work on unmount. Unmount the component on success/close/expiry before preparing another operation. React supports updated callback references without resetting the form.
57
58
 
58
- A used code cannot authorize a second action, even in another session. After sign-in or first enrollment, wait for the next code. PASSCODE_INVALID/PASSCODE_USED/PASSCODE_LOCKED are validation errors, not success. Retain the same backend idempotency key and original code when a result is unknown; retries must not repeat the business action.
59
+ See [React example](../examples/react-client.tsx) and [Vue example](../examples/vue-client.vue). Explicitly import @auth-link/web/styles.css. React 18.2+/19, Vue 3.3+, ESM and TypeScript are supported; the development toolchain requires Node 22.13+.
59
60
 
60
- destroy() aborts browser work and clears timers without a close callback. close() also calls onClose. Closing a dialog does not roll back server work: your CMS must manage cancellation and its pending slot. Waiting and local expiry never poll.
61
+ ## Migrate from 2.x
61
62
 
62
- The older openAuthLink / Ticket / Consume workflow remains available for the earlier protocol; new apps use openPasscode / AuthLinkPasscode and the backend Authenticate/authenticate method. No old-binding migration is provided.
63
+ Remove createAuthLinkClient, createPasscodeClient, openPasscode, openAuthLink*, observeRequest, Ticket/Consume, AuthLinkPasscode, AuthLinkConfirm and useAuthLink. Replace client.verify with submit(code,{signal}); onVerified/onConfirmed with onSuccess(). Use openBind({qr,expiresAt,submit}) for initial enrollment and openVerify({submit}) for daily checks. Remove HTTP/token settings and legacy result/polling code. Move all network/authentication handling to the CMS wrapper. Go SDK and AuthLink server APIs do not change.
package/docs/README.ja.md CHANGED
@@ -1,62 +1,63 @@
1
- # AuthLink Web 1.1
1
+ # AuthLink Web 3.0
2
2
 
3
3
  [English](README.en.md) · [简体中文](README.zh-CN.md) · [日本語](README.ja.md)
4
4
 
5
- ## 認証コードを直接検証
6
-
7
- アプリに6桁の認証コードを直接表示します。通常の検証では、毎回の承認、認証用requestId、businessId、ポーリングは不要です。初回はQRで端末を確認し、サイトで現在のコードを検証して連携を完了します。
8
-
9
- 画面を開く前にCMSサーバーでセッションの唯一の保留中操作を原子的に確保します。別タブから上書きさせません。ユーザーはサーバーのセッションから取得し、CSRF、業務権限、元のフォームを確認します。コードを検証し、verificationIdと元の操作を原子的に確定してから成功を返します。onVerifiedは表示更新専用です。
5
+ ## インストールと使い方
10
6
 
11
7
  ```sh
12
- npm install @auth-link/web@^1.1.0
8
+ npm install @auth-link/web@^3.0.0
13
9
  ```
14
10
 
11
+ 従来のシンプルなレスポンシブ画面と13言語を維持します。既定は簡体字中国語です。初回連携はQRコードと6桁入力、通常認証は入力のみです。SDKはHTTP通信、操作ごとの認証リクエスト作成、ポーリングを行いません。baseUrl、csrfToken、accessToken、fetchの設定は不要です。
12
+
15
13
  ```ts
16
- import { openPasscode, createPasscodeClient } from '@auth-link/web';
14
+ import { openVerify, openBind } from '@auth-link/web';
17
15
  import '@auth-link/web/styles.css';
18
- const client = createPasscodeClient({ baseUrl: '/api', csrfToken: () => csrfToken });
19
- const modal = openPasscode({
20
- client, expiresAt: pending.expiresAt, locale: 'zh-CN', variant: 'B',
21
- // First-time enrollment only:
22
- // bindingQr: pending.bindingQr, authLinkOrigin: 'https://api.getauthlink.com',
23
- onVerified: result => updateUI(result.verificationId),
16
+
17
+ openVerify({
18
+ submit: async (passcode, { signal }) => {
19
+ const result = await cmsApi.verifyAuthLink({ passcode }, { signal });
20
+ if (!result.success) throw new Error(result.message || 'VERIFICATION_FAILED');
21
+ },
22
+ onSuccess: () => refreshPage(),
23
+ });
24
+
25
+ // First enrollment only; the CMS creates the binding ticket first.
26
+ openBind({
27
+ qr: ticket.qr,
28
+ expiresAt: ticket.expiresAt,
29
+ authLinkOrigin: 'https://api.getauthlink.com',
30
+ submit: async (passcode, { signal }) => {
31
+ await cmsApi.completeBinding({ passcode }, { signal });
32
+ // This wrapper must throw on HTTP AND business failure.
33
+ },
34
+ onSuccess: () => refreshAccounts(),
35
+ onRefresh: ({ signal }) => cmsApi.createBinding({ signal }),
24
36
  });
25
- // modal.setLocale('en'); modal.setVariant('C'); modal.destroy();
26
37
  ```
27
38
 
28
- 標準クライアントは現在の認証情報とCSRFを付け、CMSのPOST /api/authへ{passcode}を送信します。フォームも送る場合はclient.verifyで{form,passcode}をCMSへ送信し、サーバーで保存済み内容と照合します。テナントキーやコード生成用秘密はブラウザーへ渡しません。
39
+ cmsApi はサイト側の既存リクエスト処理です。認証、CSRF、CORS、業務エラーはCMS側で処理してください。Cookie認証では既存のCSRF対策を維持し、テナントAPIキーはサーバーにのみ保存します。
29
40
 
30
- ## オプションとコールバック
41
+ ## submit の契約
31
42
 
32
- 必須はclient.verify(passcode, signal)とexpiresAt(CMS操作の期限)です。任意でlocale、variant A/B/C、初回用bindingQrとauthLinkOrigin、onVerified、onError、onClose、onExpiredを指定します。成功結果はstatus、verificationId、authenticationPolicy、expiresAtを含み、requestIdやbusinessIdは含みません。
43
+ `submit(passcode, { signal }): Promise<void>` は先頭ゼロを含む6桁の文字列を受け取ります。CMSがコード検証と業務処理を完了してから正常終了し、HTTPまたは業務エラーでは例外を投げます。HTTP 200でもsuccess=falseなら例外が必要です。SDKは戻り値JSONを判定しません。return {success:false} は失敗通知になりません。AuthLinkの検証結果やverificationIdをブラウザーへ返す必要はなく、CMSが検証して業務トランザクションに保存します。
33
44
 
34
- Cookie認証ではCSRFが必須です。Bearer認証はaccessTokenとcredentials=omitを使います。別オリジンのCMSはallowedBackendOriginsを明示し、ループバック開発以外ではHTTPSを使います。
45
+ 二重送信を防ぎ、エラー後は明示的に再送できます。閉じる、アンマウント、CMS操作の期限切れでsignalがキャンセルされるため、リクエスト層へ渡してください。ブラウザーの中止はサーバー処理の取り消しではありません。結果不明の場合は元の操作、コード、冪等キーを維持し、別の業務操作を作成しないでください。
35
46
 
36
- ## ReactとVue
47
+ 成功時は画面を閉じて引数なしのonSuccessを呼びます。onSuccessは表示更新専用です。その失敗で業務を再送しません。onErrorは元のErrorを受け取り、close()はonCloseを通知、destroy()は通知せず終了します。retry()はエラーを消すだけです。setLocale(locale)は入力を維持して言語を切り替えます。
37
48
 
38
- Reactは同じオプションを使い、Vueはverified、error、close、expiredイベントを発行します。clientの参照を安定させてください。13言語に対応し、既定は簡体字中国語です。Aはシンプル、Bは分割、Cはコンパクト表示です。
49
+ ## 期限とQR更新
39
50
 
40
- ```tsx
41
- import { AuthLinkPasscode } from '@auth-link/web/react';
42
- // Keep client stable (for example with useMemo).
43
- <AuthLinkPasscode client={client} expiresAt={pending.expiresAt}
44
- locale="zh-CN" variant="B" onVerified={updateUI} />
45
- ```
46
- ```vue
47
- <script setup>
48
- import { AuthLinkPasscode } from '@auth-link/web/vue';
49
- </script>
50
- <template>
51
- <AuthLinkPasscode :client="client" :expires-at="pending.expiresAt"
52
- locale="zh-CN" variant="B" @verified="updateUI" />
53
- </template>
54
- ```
51
+ openVerifyはsubmitのみ必須で、expiresAtはCMS操作の任意の期限です。SDK側の5分制限はありません。Appの現在のコード生成時刻はブラウザーから分からないため、1分のカウントダウンを仮定しません。Appコードと検証検証結果は引き続き最長1分です。
52
+
53
+ openBindはCMSが返したqr(画像URLではなくJSON)とexpiresAtも必要です。QRは作成から最長60分で、スキャンや再試行では延長しません。authLinkOriginは任意のAuthLink APIオリジン照合です。QR描画はローカルのみです。onRefresh({signal})はPromise<{qr,expiresAt}>を返し、期限切れ後に利用者が更新を選んだ場合のみ実行します。新規リクエストはCMSが管理し、更新失敗時は期限切れのまま再試行できます。
54
+
55
+ ## React / Vue
55
56
 
56
- ## 失敗と終了処理
57
+ @auth-link/web/react または @auth-link/web/vue からAuthLinkVerify / AuthLinkBindをインポートします。JavaScriptと同じオプションで、Vueはsuccess、close、error、expiredを通知します。onRefreshはチケットを返す関数propです。SSRでは画面を開かず、アンマウント時に中止します。成功・終了後は親側でアンマウントし、次の操作を準備してください。Reactはコールバック更新で入力をリセットしません。
57
58
 
58
- 使用済みコードは別操作や別セッションに使えません。ログインや初回連携直後は次のコードを待ちます。PASSCODE_INVALID、PASSCODE_USED、PASSCODE_LOCKEDを成功として扱わないでください。通信結果不明時は元の冪等キーとコードを保持して再試行し、業務を二重実行しません。
59
+ [React例](../examples/react-client.tsx)、[Vue例](../examples/vue-client.vue)。@auth-link/web/styles.cssを明示的に読み込みます。React 18.2+/19、Vue 3.3+、ESM、TypeScriptに対応し、開発にはNode 22.13+が必要です。
59
60
 
60
- destroy()はブラウザー要求を中止してタイマーを解除し、終了コールバックを呼びません。close()はonCloseも呼びます。画面を閉じてもサーバー処理は元に戻らず、キャンセルと保留中操作はCMS側で管理します。待機やローカル期限判定はポーリングしません。
61
+ ## 2.xからの移行
61
62
 
62
- 旧openAuthLink / Ticket / Consumeは以前のプロトコル向けに残ります。新アプリはopenPasscode / AuthLinkPasscodeとサーバーのAuthenticate / authenticateを使います。旧連携の移行機能は提供しません。
63
+ createAuthLinkClient、createPasscodeClient、openPasscode、openAuthLink*、observeRequest、Ticket/Consume、AuthLinkPasscode、AuthLinkConfirm、useAuthLinkは削除されました。client.verifyをsubmit(code,{signal})、onVerified/onConfirmedをonSuccess()へ変更します。初回はopenBind({qr,expiresAt,submit})、通常はopenVerify({submit})です。HTTPやToken設定をCMSの通信層に移します。Go SDKとAuthLinkサーバーAPIの変更は不要です。
@@ -1,62 +1,63 @@
1
- # AuthLink Web 1.1
1
+ # AuthLink Web 3.0
2
2
 
3
3
  [English](README.en.md) · [简体中文](README.zh-CN.md) · [日本語](README.ja.md)
4
4
 
5
- ## 直接验证动态码
6
-
7
- App 直接显示六位动态码。日常验证不需要逐次批准、授权 requestId、businessId 或轮询。首次绑定仍需扫码验证设备,在网站输入 App 当前动态码后才完成绑定。
8
-
9
- 打开弹窗前,CMS 后台必须为会话原子保留唯一且不可变的待办。其他标签页不能覆盖它。用户来自服务端会话,后台检查 CSRF、业务权限和原表单,验证动态码后将 verificationId 与原操作原子提交,再返回成功。onVerified 只更新界面。
5
+ ## 安装与使用
10
6
 
11
7
  ```sh
12
- npm install @auth-link/web@^1.1.0
8
+ npm install @auth-link/web@^3.0.0
13
9
  ```
14
10
 
11
+ 保留原有极简响应式布局和 13 种语言,默认简体中文。首次绑定显示二维码和六位码输入框,日常验证仅显示输入框。SDK 不发送 HTTP 请求、不创建逐次授权请求、不轮询,无需配置 baseUrl、csrfToken、accessToken 或 fetch。
12
+
15
13
  ```ts
16
- import { openPasscode, createPasscodeClient } from '@auth-link/web';
14
+ import { openVerify, openBind } from '@auth-link/web';
17
15
  import '@auth-link/web/styles.css';
18
- const client = createPasscodeClient({ baseUrl: '/api', csrfToken: () => csrfToken });
19
- const modal = openPasscode({
20
- client, expiresAt: pending.expiresAt, locale: 'zh-CN', variant: 'B',
21
- // First-time enrollment only:
22
- // bindingQr: pending.bindingQr, authLinkOrigin: 'https://api.getauthlink.com',
23
- onVerified: result => updateUI(result.verificationId),
16
+
17
+ openVerify({
18
+ submit: async (passcode, { signal }) => {
19
+ const result = await cmsApi.verifyAuthLink({ passcode }, { signal });
20
+ if (!result.success) throw new Error(result.message || 'VERIFICATION_FAILED');
21
+ },
22
+ onSuccess: () => refreshPage(),
23
+ });
24
+
25
+ // First enrollment only; the CMS creates the binding ticket first.
26
+ openBind({
27
+ qr: ticket.qr,
28
+ expiresAt: ticket.expiresAt,
29
+ authLinkOrigin: 'https://api.getauthlink.com',
30
+ submit: async (passcode, { signal }) => {
31
+ await cmsApi.completeBinding({ passcode }, { signal });
32
+ // This wrapper must throw on HTTP AND business failure.
33
+ },
34
+ onSuccess: () => refreshAccounts(),
35
+ onRefresh: ({ signal }) => cmsApi.createBinding({ signal }),
24
36
  });
25
- // modal.setLocale('en'); modal.setVariant('C'); modal.destroy();
26
37
  ```
27
38
 
28
- 默认客户端向 CMS 的 POST /api/auth 提交 {passcode},携带当前会话与 CSRF。要随表单提交,可自行实现 client.verify,向 CMS 发送 {form,passcode};后台核对原保存快照。租户密钥与动态码密钥不能进入浏览器。
39
+ 示例中的 cmsApi 是 CMS 自己的请求封装,不是本 SDK 提供的方法。复用已有登录态、CSRF、跨域和错误处理;Cookie 模式仍需 CMS 原有的 CSRF 保护。租户密钥只保存在 CMS 服务端。
29
40
 
30
- ## 选项与回调
41
+ ## submit 返回什么
31
42
 
32
- 必填:client.verify(passcode, signal)、expiresAt(CMS 待办截止时间)。可选:locale、variant A/B/C、仅首次绑定使用的 bindingQr 与 authLinkOrigin、onVerified、onError、onClose、onExpired。成功结果含 status、verificationId、authenticationPolicy、expiresAt,不含 requestId 或 businessId。
43
+ `submit(passcode, { signal }): Promise<void>` 接收六位数字字符串,保留前导零。只有 CMS 校验验证码并完成业务后才能正常结束;HTTP 或业务失败必须抛错,包括 HTTP 200 但 success=false。SDK 不检查返回 JSON,不能用 return {success:false} 代替抛错。前端不必返回 AuthLink 回执或 verificationId;由 CMS 后台核验并与业务事务一起保存。
33
44
 
34
- Cookie 模式必须配置 CSRF。Bearer 模式配置 accessToken 且 credentials=omit。跨域 CMS 必须显式配置 allowedBackendOrigins;回环开发之外使用 HTTPS。
45
+ SDK 防止重复提交,失败保留输入供用户主动重试。关闭、卸载或业务待办过期会取消 signal,请传给请求层。取消前端等待不能撤销后台已完成的操作;网络结果不明确时保留原待办、原验证码及原幂等键重试,不要另建业务操作。
35
46
 
36
- ## React 与 Vue
47
+ 成功时关闭弹窗并调用无参数 onSuccess,回调只更新页面,不负责后台放行。页面更新失败不会重新打开或重复提交。onError 接收原 Error;close() 触发 onClose,destroy() 静默清理。retry() 只清除错误,setLocale(locale) 保留输入切换语言,二者都不会自动发送请求。
37
48
 
38
- React 组件使用相同选项,Vue 发出 verified、error、close、expired 事件。client 引用需保持稳定。支持 13 种语言,默认简体中文;A 极简、B 分栏、C 紧凑。
49
+ ## 有效期与二维码刷新
39
50
 
40
- ```tsx
41
- import { AuthLinkPasscode } from '@auth-link/web/react';
42
- // Keep client stable (for example with useMemo).
43
- <AuthLinkPasscode client={client} expiresAt={pending.expiresAt}
44
- locale="zh-CN" variant="B" onVerified={updateUI} />
45
- ```
46
- ```vue
47
- <script setup>
48
- import { AuthLinkPasscode } from '@auth-link/web/vue';
49
- </script>
50
- <template>
51
- <AuthLinkPasscode :client="client" :expires-at="pending.expiresAt"
52
- locale="zh-CN" variant="B" @verified="updateUI" />
53
- </template>
54
- ```
51
+ openVerify 只要求 submit。expiresAt 可选,表示 CMS 当前业务待办的截止时间,SDK 不再强制五分钟上限。网页不知道 App 当前码何时生成,因此不伪造一分钟验证码倒计时;App 的数字码和服务端回执仍最多有效一分钟。
52
+
53
+ openBind 还要求 CMS 返回的 qr(二维码 JSON 内容,不是图片或 URL)和 expiresAt。二维码从创建起最多有效 60 分钟,扫码和重试不延长。authLinkOrigin 可选,仅核对 AuthLink API 来源。二维码在本地生成。onRefresh({signal}) 可选,返回 Promise<{qr,expiresAt}>;仅过期后用户主动点击重新获取时执行,由 CMS 创建替换请求。刷新失败保持过期状态,可再次重试。
54
+
55
+ ## React 与 Vue
55
56
 
56
- ## 失败与清理
57
+ 从 @auth-link/web/react 或 @auth-link/web/vue 导入 AuthLinkVerify / AuthLinkBind,参数与 JavaScript 一致。Vue 发出 success、close、error、expired 事件;onRefresh 是返回新二维码的函数属性。SSR 不打开弹窗,卸载时自动取消等待。成功、关闭或过期后由父组件卸载,再准备新操作;React 更新回调引用不会清空输入。
57
58
 
58
- 已使用的码不能用于另一个操作或会话;刚登录或首次绑定后需等待下一组数字。PASSCODE_INVALID / PASSCODE_USED / PASSCODE_LOCKED 是验证失败,不能当成成功。网络结果不明时,后台保留原幂等键和原码重试,业务不能重复执行。
59
+ 参见 [React 示例](../examples/react-client.tsx)、[Vue 示例](../examples/vue-client.vue)。显式导入 @auth-link/web/styles.css。支持 React 18.2+/19、Vue 3.3+、ESM 和 TypeScript,开发工具链要求 Node 22.13+。
59
60
 
60
- destroy() 中止浏览器请求并清理计时器,不触发关闭回调;close() 还调用 onClose。关闭弹窗不会回滚服务端操作,取消与会话待办由 CMS 管理。等待和本地过期均不轮询。
61
+ ## 从 2.x 升级
61
62
 
62
- 旧 openAuthLink / Ticket / Consume 流程仍供此前协议使用;新 App 使用 openPasscode / AuthLinkPasscode 以及后台 Authenticate / authenticate。此次不提供旧绑定升级。
63
+ 移除 createAuthLinkClient、createPasscodeClient、openPasscode、openAuthLink*、observeRequest、Ticket/Consume、AuthLinkPasscode、AuthLinkConfirm 和 useAuthLink。client.verify 改为 submit(code,{signal});onVerified/onConfirmed 改为无参数 onSuccess。首次绑定使用 openBind({qr,expiresAt,submit}),日常验证使用 openVerify({submit})。删除 SDK 的 HTTP/Token 配置及旧审批流程,网络鉴权交给 CMS 请求层。Go SDK 和 AuthLink 服务端接口无需调整。
@@ -1,24 +1,12 @@
1
1
  "use client";
2
- import { useState } from 'react';
3
- import type { OTPVerification, PasscodeClient } from '@auth-link/web';
4
- import { AuthLinkPasscode } from '@auth-link/web/react';
5
- // Import '@auth-link/web/styles.css' once in your application entry.
6
-
7
- // The parent reserves one pending CMS action first. Its close handler cancels
8
- // that backend slot before allowing another operation to start.
9
- export function AuthLinkPanel({ client, pending, onVerified, onClose }: {
10
- client: PasscodeClient;
11
- pending: { expiresAt: string; bindingQr?: string };
12
- onVerified: (result: OTPVerification) => void;
13
- onClose: () => void;
2
+ import { AuthLinkVerify, AuthLinkBind } from '@auth-link/web/react';
3
+ import type { Submit, BindingTicket } from '@auth-link/web';
4
+ // Import '@auth-link/web/styles.css' once in the app entry.
5
+ // The CMS prepares its pending action before rendering this component.
6
+ export function AuthLinkPanel({ submit, binding, onSuccess, onClose }: {
7
+ submit: Submit; binding?: BindingTicket; onSuccess: () => void; onClose: () => void;
14
8
  }) {
15
- const [message, setMessage] = useState('');
16
- return <>
17
- <AuthLinkPasscode client={client} expiresAt={pending.expiresAt}
18
- bindingQr={pending.bindingQr} authLinkOrigin="https://api.getauthlink.com"
19
- locale="en" variant="B" onVerified={onVerified} onClose={onClose}
20
- onExpired={() => setMessage('Expired; start a new action')}
21
- onError={error => setMessage(error.message)} />
22
- <p role="status">{message}</p>
23
- </>;
9
+ return binding
10
+ ? <AuthLinkBind {...binding} submit={submit} onSuccess={onSuccess} onClose={onClose} />
11
+ : <AuthLinkVerify submit={submit} onSuccess={onSuccess} onClose={onClose} />;
24
12
  }
@@ -1,31 +1,14 @@
1
1
  <script setup lang="ts">
2
- import { markRaw, ref } from 'vue';
3
- import { createPasscodeClient } from '@auth-link/web';
4
- import type { OTPVerification } from '@auth-link/web';
5
- import { AuthLinkPasscode } from '@auth-link/web/vue';
2
+ import { AuthLinkVerify, AuthLinkBind } from '@auth-link/web/vue';
3
+ import type { Submit, BindingTicket } from '@auth-link/web';
6
4
  import '@auth-link/web/styles.css';
7
-
8
- // The CMS has already reserved this session's sole pending action. First
9
- // enrollment includes bindingQr; ordinary verification needs no QR.
10
- const props = defineProps<{
11
- csrf: string;
12
- pending: { expiresAt: string; bindingQr?: string };
13
- }>();
14
- const emit = defineEmits<{
15
- verified: [result: OTPVerification];
16
- close: [];
17
- }>();
18
- const message = ref('');
19
- const client = markRaw(createPasscodeClient({ baseUrl: '/api', csrfToken: () => props.csrf }));
20
- function verified(result: OTPVerification) { emit('verified', result); }
21
- function showError(error: Error) { message.value = error.message; }
22
- // The parent cancels its backend slot before preparing another action.
5
+ // The CMS request wrapper owns authentication, CSRF and business errors.
6
+ defineProps<{ submit: Submit; binding?: BindingTicket }>();
7
+ const emit = defineEmits<{ success: []; close: [] }>();
23
8
  </script>
24
-
25
9
  <template>
26
- <AuthLinkPasscode :client="client" :expires-at="pending.expiresAt"
27
- :binding-qr="pending.bindingQr" auth-link-origin="https://api.getauthlink.com"
28
- locale="en" variant="B" @verified="verified" @close="emit('close')"
29
- @expired="message = 'Expired; start a new action'" @error="showError" />
30
- <p role="status">{{ message }}</p>
10
+ <AuthLinkBind v-if="binding" v-bind="binding" :submit="submit"
11
+ @success="emit('success')" @close="emit('close')" />
12
+ <AuthLinkVerify v-else :submit="submit"
13
+ @success="emit('success')" @close="emit('close')" />
31
14
  </template>
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@auth-link/web",
3
- "version": "1.1.0",
3
+ "version": "3.0.0",
4
4
  "private": false,
5
- "description": "Three responsive AuthLink dialog layouts for phone binding and mobile confirmation. Framework-neutral API, React and Vue, 13 languages, Simplified Chinese by default.",
5
+ "description": "Callback-driven AuthLink binding and six-digit verification dialogs. No HTTP client; JavaScript, React, Vue and 13 languages.",
6
6
  "type": "module",
7
7
  "license": "MIT",
8
8
  "files": [
@@ -1,29 +0,0 @@
1
- import type { ClientOptions } from './core.js';
2
- import type { AuthLinkModal } from './modal.js';
3
- import type { DialogVariant, Locale } from './types.js';
4
- export interface OTPVerification {
5
- status: 'confirmed';
6
- verificationId: string;
7
- authenticationPolicy: 'device-totp.v1';
8
- expiresAt: string;
9
- }
10
- export interface PasscodeClient {
11
- verify(passcode: string, signal: AbortSignal): Promise<OTPVerification>;
12
- }
13
- export interface PasscodeOptions {
14
- client: PasscodeClient;
15
- /** The CMS's single pending action deadline, not a TOTP time step. */
16
- expiresAt: string;
17
- locale?: Locale;
18
- variant?: DialogVariant;
19
- /** First-time enrollment only. Ordinary verification has no QR or request ID. */
20
- bindingQr?: string;
21
- authLinkOrigin?: string;
22
- onVerified?: (result: OTPVerification) => void | Promise<void>;
23
- onClose?: () => void;
24
- onExpired?: () => void | Promise<void>;
25
- onError?: (error: Error) => void;
26
- }
27
- export declare function createPasscodeClient(options: ClientOptions): PasscodeClient;
28
- /** A session-scoped passcode prompt. No request creation, request ID or polling. */
29
- export declare function openPasscode(options: PasscodeOptions): AuthLinkModal;