@auth-link/web 0.0.0-stage → 0.0.1
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 +47 -0
- package/README.md +785 -2
- package/THIRD_PARTY_NOTICES.md +29 -0
- package/dist/index.js +2533 -0
- package/dist/index.js.map +7 -0
- package/dist/react.js +2519 -0
- package/dist/react.js.map +7 -0
- package/dist/styles.css +126 -0
- package/dist/types/core.d.ts +29 -0
- package/dist/types/i18n.d.ts +7 -0
- package/dist/types/index.d.ts +5 -0
- package/dist/types/modal.d.ts +19 -0
- package/dist/types/react.d.ts +11 -0
- package/dist/types/types.d.ts +71 -0
- package/dist/types/view.d.ts +19 -0
- package/dist/types/vue.d.ts +12 -0
- package/dist/vue.js +2541 -0
- package/dist/vue.js.map +7 -0
- package/docs/README.en.md +250 -0
- package/docs/README.ja.md +250 -0
- package/docs/README.zh-CN.md +282 -0
- package/package.json +88 -4
package/README.md
CHANGED
|
@@ -1,3 +1,786 @@
|
|
|
1
|
-
#
|
|
1
|
+
# AuthLink Web
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[English](#english) | [简体中文](#简体中文) | [日本語](#日本語)
|
|
4
|
+
|
|
5
|
+
## English
|
|
6
|
+
|
|
7
|
+
Responsive dialogs for **AuthLink phone binding and mobile confirmation**, with direct function calls and React / Vue components. Display binding QR codes, synchronized six-digit matching codes, countdowns and confirmation results in any web application. No particular site platform is required.
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
- Phone-binding QR codes generated locally in the browser.
|
|
12
|
+
- Matching numbers that update every 30 seconds, synchronized with the AuthLink app.
|
|
13
|
+
- Confirmation, rejection, expiry and error callbacks.
|
|
14
|
+
- React components and hook, Vue components and composable, and framework-independent tools.
|
|
15
|
+
- 13 interface languages, **Simplified Chinese by default**, with per-component language selection.
|
|
16
|
+
- TypeScript declarations, separate CSS and optional React/Vue peer dependencies.
|
|
17
|
+
|
|
18
|
+
This is a browser integration library. It works with your application backend, the AuthLink service and the mobile app. The service verifies the original phone’s hardware-backed proof after the user approves in the app. Screen-lock checks and system passkeys are not required. Installing the library alone does not create an authentication service.
|
|
19
|
+
|
|
20
|
+
### Install
|
|
21
|
+
|
|
22
|
+
```sh
|
|
23
|
+
npm install @auth-link/web
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Compatibility: React 18.2+ / 19, Vue 3.3+, TypeScript, ESM. Install only the framework you use. The package declares Node 22.13+ for tooling.
|
|
27
|
+
|
|
28
|
+
### Browser integration
|
|
29
|
+
|
|
30
|
+
1. Configure your backend's AuthLink tenant credentials.
|
|
31
|
+
2. Implement the two same-origin endpoints below, authenticated to the current website user.
|
|
32
|
+
3. Create a request on a user action and pass its ticket to a component.
|
|
33
|
+
4. Your backend verifies and consumes the result before returning a confirmed response.
|
|
34
|
+
|
|
35
|
+
The browser can call this library directly. Tenant credentials and result consumption belong on your backend; never embed a tenant API key in frontend code. The supplied client intentionally uses a same-origin backend. You can supply a custom `AuthLinkClient` to integrate another transport while preserving server-side credential handling and verification.
|
|
36
|
+
|
|
37
|
+
### Create a client
|
|
38
|
+
|
|
39
|
+
```ts
|
|
40
|
+
// authlink-client.ts
|
|
41
|
+
import { createAuthLinkClient, requestKey } from '@auth-link/web';
|
|
42
|
+
import '@auth-link/web/styles.css';
|
|
43
|
+
|
|
44
|
+
export const client = createAuthLinkClient({
|
|
45
|
+
baseUrl: '/api/authlink',
|
|
46
|
+
csrfToken: () => {
|
|
47
|
+
const token = document.querySelector<HTMLMetaElement>('meta[name="csrf-token"]')?.content;
|
|
48
|
+
if (!token) throw new Error('Missing CSRF token');
|
|
49
|
+
return token;
|
|
50
|
+
},
|
|
51
|
+
authLinkOrigin: 'https://auth.example.com',
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
// Call from a user action. Keep this key if you retry the same creation.
|
|
55
|
+
const idempotencyKey = requestKey();
|
|
56
|
+
const ticket = await client.create('bind', { idempotencyKey });
|
|
57
|
+
// For a mobile confirmation, use client.create('confirm', { idempotencyKey }).
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Provide a real CSRF token from your application's session in the meta element, and replace the example AuthLink origin with your configured HTTPS origin. Request creation belongs in a click handler, not a mount effect. Components only observe existing requests.
|
|
61
|
+
|
|
62
|
+
### Open a dialog directly (React, Vue or plain JavaScript)
|
|
63
|
+
|
|
64
|
+
```ts
|
|
65
|
+
import { openAuthLinkBind, openAuthLinkConfirm, requestKey } from '@auth-link/web';
|
|
66
|
+
import '@auth-link/web/styles.css';
|
|
67
|
+
|
|
68
|
+
// Call in your button handler, after your backend creates the request.
|
|
69
|
+
const ticket = await client.create('confirm', { idempotencyKey: requestKey() });
|
|
70
|
+
const modal = openAuthLinkConfirm({
|
|
71
|
+
ticket, client, locale: 'en',
|
|
72
|
+
onConfirmed: result => console.log('Confirmed:', result.requestId),
|
|
73
|
+
onRejected: result => console.log(result.code), // REJECTED; dialog already closed
|
|
74
|
+
onClose: () => console.log('Dialog closed'),
|
|
75
|
+
});
|
|
76
|
+
// Bind instead: openAuthLinkBind({ ticket: bindingTicket, client }).
|
|
77
|
+
// modal.close(); modal.setLocale('ja');
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
The dialog adapts to mobile widths, has a scrollable height, keyboard Escape and an accessible close button. Native `<dialog>` provides modal focus handling; a browser with `showModal()` is required. Closing aborts browser observation and restores focus/scroll. It does not reject, delete or undo the server request. Its original expiry still applies. Confirmation and rejection automatically close the dialog before delivering `onConfirmed` or `onRejected`; it does not emit a user-close callback. Always call `modal.destroy()` when the owning page/component unmounts; this cleans up without emitting `onClose`.
|
|
81
|
+
|
|
82
|
+
### React
|
|
83
|
+
|
|
84
|
+
```tsx
|
|
85
|
+
import { AuthLinkBind, AuthLinkConfirm } from '@auth-link/web/react';
|
|
86
|
+
import type { Ticket, Confirmation } from '@auth-link/web';
|
|
87
|
+
import { client } from './authlink-client';
|
|
88
|
+
|
|
89
|
+
export function VerificationPanel({ ticket }: { ticket: Ticket }) {
|
|
90
|
+
const onConfirmed = (result: Confirmation) => {
|
|
91
|
+
console.log('Confirmed request:', result.requestId);
|
|
92
|
+
};
|
|
93
|
+
return ticket.purpose === 'bind'
|
|
94
|
+
? <AuthLinkBind ticket={ticket} client={client} onConfirmed={onConfirmed} />
|
|
95
|
+
: <AuthLinkConfirm ticket={ticket} client={client} locale="en"
|
|
96
|
+
onConfirmed={onConfirmed} onExpired={() => console.log('Expired')} />;
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Mounting either component opens a dialog; unmount it in `onClose` (for example, clear the selected ticket). SSR renders no dialog and makes no requests. Omit `locale` for Simplified Chinese. Use `locale="zh-CN"` for Simplified Chinese or another code from the language table. Keep `client` and `ticket` stable between renders. The React entry preserves `"use client"`; SSR rendering does not create requests.
|
|
101
|
+
|
|
102
|
+
### Vue
|
|
103
|
+
|
|
104
|
+
```vue
|
|
105
|
+
<script setup lang="ts">
|
|
106
|
+
import { AuthLinkBind, AuthLinkConfirm } from '@auth-link/web/vue';
|
|
107
|
+
import type { Ticket, Confirmation } from '@auth-link/web';
|
|
108
|
+
import { client } from './authlink-client';
|
|
109
|
+
|
|
110
|
+
defineProps<{ ticket: Ticket }>();
|
|
111
|
+
function confirmed(result: Confirmation) {
|
|
112
|
+
console.log('Confirmed request:', result.requestId);
|
|
113
|
+
}
|
|
114
|
+
</script>
|
|
115
|
+
|
|
116
|
+
<template>
|
|
117
|
+
<AuthLinkBind v-if="ticket.purpose === 'bind'" :ticket="ticket" :client="client"
|
|
118
|
+
@confirmed="confirmed" />
|
|
119
|
+
<AuthLinkConfirm v-else :ticket="ticket" :client="client" locale="ja"
|
|
120
|
+
@confirmed="confirmed" />
|
|
121
|
+
</template>
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
Use `:locale="selectedLanguage"` to switch languages without creating a new verification request. Mounting opens the dialog. Use `@close` to clear the selected ticket. Vue exposes `confirmed`, `rejected`, `expired`, `error` and `close` events.
|
|
125
|
+
|
|
126
|
+
### Choose a layout
|
|
127
|
+
|
|
128
|
+
Set `variant` when opening a dialog or mounting a component. Omit it for **A**.
|
|
129
|
+
|
|
130
|
+
| Value | Desktop | Mobile |
|
|
131
|
+
| --- | --- | --- |
|
|
132
|
+
| `A` (default) | Minimal centered card with a thin code-refresh progress bar | Centered card |
|
|
133
|
+
| `B` | QR and matching code in two columns | Vertical layout |
|
|
134
|
+
| `C` | Compact card with footer cancellation | Bottom sheet with safe-area padding |
|
|
135
|
+
|
|
136
|
+
```ts
|
|
137
|
+
openAuthLinkBind({ ticket, client }); // A, Simplified Chinese by default
|
|
138
|
+
openAuthLinkConfirm({ ticket, client, variant: 'B' });
|
|
139
|
+
// modal.setVariant('C'); // Presentation only; preserves the request and polling.
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
```tsx
|
|
143
|
+
<AuthLinkBind ticket={ticket} client={client} variant="C" />
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
```vue
|
|
147
|
+
<AuthLinkConfirm :ticket="ticket" :client="client" variant="B" />
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
All layouts show a thin progress bar and a static “Code updates automatically every 30 seconds” caption. The footer normally says “Waiting for your phone”; only in the last 30 seconds does it switch to an amber “Request expiring soon · 00:29” warning. Code changes fade in briefly, respect reduced-motion preferences, and never extend the request expiry.
|
|
151
|
+
|
|
152
|
+
All variants use the same verification, expiry and callbacks. `variant` changes can be reactive in React and Vue without restarting observation. Invalid runtime values fall back to A. The footer Cancel control in B/C has the same behavior as the close icon: it closes observation, without rejecting the server request. Confirmation still dismisses the dialog automatically. A uses a subtle close icon; all close controls retain a 44px hit target and keyboard focus indication.
|
|
153
|
+
|
|
154
|
+
### Component options
|
|
155
|
+
|
|
156
|
+
| Option | Type | Meaning |
|
|
157
|
+
| --- | --- | --- |
|
|
158
|
+
| `ticket` | `Ticket`, required | Existing request returned by your backend |
|
|
159
|
+
| `client` | `AuthLinkClient`, required | Client that queries your backend |
|
|
160
|
+
| `locale` | `Locale` | Interface language; defaults to `zh-CN` |
|
|
161
|
+
| `variant` | `DialogVariant` | `A` (default), `B` or `C`; changes layout only |
|
|
162
|
+
| `showQr` | `boolean` | Optional QR on confirmation panels; binding panels always show it |
|
|
163
|
+
| `intervalMs` | `number` | Polling interval, 250–10000 ms; default 1500 ms |
|
|
164
|
+
| `onRefresh` | `() => void` | Create a new request from the expiry panel and replace the ticket |
|
|
165
|
+
|
|
166
|
+
| React callback / Vue event | Argument | Meaning |
|
|
167
|
+
| --- | --- | --- |
|
|
168
|
+
| `onConfirmed` / `confirmed` | `Confirmation` | Backend has verified and consumed the result |
|
|
169
|
+
| `onRejected` / `rejected` | `Rejection` | Phone rejected; dialog closes automatically |
|
|
170
|
+
| `onExpired` / `expired` | None | Request expired |
|
|
171
|
+
| `onClose` / `close` | None | Dialog closed; unmount the component / clear its ticket |
|
|
172
|
+
| `onError` / `error` | `Error` | Network, response validation or callback failure |
|
|
173
|
+
|
|
174
|
+
Rejection returns `{ status: 'rejected', code: 'REJECTED', requestId, purpose }`. `REJECTED` is an SDK business status, not an HTTP status code or an authorization receipt. The SDK closes the dialog without showing a rejection/retry panel or firing `onClose`. Handle any message or new request in your website's callback. In React/Vue, clear the selected ticket in `onRejected` / `@rejected` to unmount the component. Headless hooks retain `snapshot.status === 'rejected'` for custom UI.
|
|
175
|
+
|
|
176
|
+
`useAuthLink(options)` for React and `useAuthLink(() => options)` for Vue expose `snapshot` and `retry` for custom interfaces. Vue's snapshot is a shallow ref. Retry resumes the same request; `onRefresh` creates a new one through your code.
|
|
177
|
+
|
|
178
|
+
### Backend contract
|
|
179
|
+
|
|
180
|
+
The default prefix is `/api/authlink`. Both endpoints use your website session and `X-CSRF-Token`. Creation also sends a stable `Idempotency-Key`. Your backend derives the website account from the session rather than a browser-supplied account ID.
|
|
181
|
+
|
|
182
|
+
```ts
|
|
183
|
+
import type { Ticket, Result } from '@auth-link/web';
|
|
184
|
+
|
|
185
|
+
// POST /api/authlink/requests
|
|
186
|
+
// Body: { purpose: 'bind' | 'confirm' }
|
|
187
|
+
// Response: Ticket
|
|
188
|
+
|
|
189
|
+
// POST /api/authlink/requests/{requestId}/result
|
|
190
|
+
// Body: {}
|
|
191
|
+
// Response: Result
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
```json
|
|
195
|
+
{
|
|
196
|
+
"status": "confirmed",
|
|
197
|
+
"requestId": "<request-id>",
|
|
198
|
+
"verificationId": "<verification-id>",
|
|
199
|
+
"purpose": "confirm",
|
|
200
|
+
"authenticationPolicy": "device-presence.v1",
|
|
201
|
+
"expiresAt": "<same-expiry-as-ticket>"
|
|
202
|
+
}
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
Creation returns the public ticket from AuthLink: `requestId`, `purpose`, `qr`, `pairingCode`, `expiresAt` and the current `displayCode`. The display frame contains `value`, `serverTime`, `validUntil`, `periodSeconds: 30`. A confirmation ticket requires a display frame.
|
|
206
|
+
|
|
207
|
+
The result endpoint verifies session/request ownership and forwards pending states and the current display frame. States `new`, `pending`, `approved` and `consumed` alone do not trigger success. Before returning `confirmed`, your backend must consume the AuthLink result with its saved state and stable consume idempotency key, check the complete receipt and persist confirmation for the correct session. Persist idempotence using request/verification IDs.
|
|
208
|
+
|
|
209
|
+
### Languages
|
|
210
|
+
|
|
211
|
+
| Code | Language |
|
|
212
|
+
| --- | --- |
|
|
213
|
+
| `en` | English |
|
|
214
|
+
| `zh-CN` | 简体中文 (default) |
|
|
215
|
+
| `zh-TW` | 繁體中文 |
|
|
216
|
+
| `ja` | 日本語 |
|
|
217
|
+
| `bn` | বাংলা |
|
|
218
|
+
| `es` | Español |
|
|
219
|
+
| `fil` | Filipino |
|
|
220
|
+
| `hi` | हिन्दी |
|
|
221
|
+
| `id` | Bahasa Indonesia |
|
|
222
|
+
| `ms` | Bahasa Melayu |
|
|
223
|
+
| `pt` | Português |
|
|
224
|
+
| `th` | ไทย |
|
|
225
|
+
| `vi` | Tiếng Việt |
|
|
226
|
+
|
|
227
|
+
Missing or unsupported locale codes fall back to Simplified Chinese. The panel sets its root `lang` attribute. Import `supportedLocales` from the root entry to get the supported codes. Language selection changes display text only, never the verification request or matching numbers. Documentation is available in English, Chinese and Japanese through the links above.
|
|
228
|
+
|
|
229
|
+
### Styles and entry points
|
|
230
|
+
|
|
231
|
+
Explicitly import `@auth-link/web/styles.css`. Override `--authlink-foreground`, `--authlink-background`, `--authlink-accent` and `--authlink-muted` on `:root` or `.authlink-dialog`; dialogs are mounted under `document.body`. All component classes use the `authlink-` prefix.
|
|
232
|
+
|
|
233
|
+
| Import | Contents |
|
|
234
|
+
| --- | --- |
|
|
235
|
+
| `@auth-link/web` | Client, request key, supported locales, types and core tools |
|
|
236
|
+
| `@auth-link/web/react` | React components and hook |
|
|
237
|
+
| `@auth-link/web/vue` | Vue components and composable |
|
|
238
|
+
| `@auth-link/web/styles.css` | Component styles |
|
|
239
|
+
|
|
240
|
+
### Security and timing
|
|
241
|
+
|
|
242
|
+
- Matching numbers are for visual comparison with the app, not standalone passwords or authorization proofs.
|
|
243
|
+
- The browser receives only public tickets and current display frames, never generation seeds, device keys or tenant secrets.
|
|
244
|
+
- A request lasts at most 120 seconds; rotating the code does not extend it. Backend and app must support AuthLink mobile protocol 7.
|
|
245
|
+
- Countdown uses server time and a monotonic clock. Expired numbers are hidden even if a network call stalls; a short update gap at rotation is expected.
|
|
246
|
+
- Component unmount/request changes abort polling. Confirmation is deduplicated per mounted instance; your backend still needs persistent idempotence.
|
|
247
|
+
- Frontend callbacks update UI. Protected APIs must rely on trusted backend records, never a JavaScript callback or a browser-supplied success flag.
|
|
248
|
+
- QR generation is local; backend SVG is not injected and no third-party QR service receives the ticket. Allow `img-src 'self' data:` in your CSP.
|
|
249
|
+
|
|
250
|
+
### License
|
|
251
|
+
|
|
252
|
+
UNLICENSED. Public availability of the package does not grant an open-source license. Bundled third-party attribution is in `THIRD_PARTY_NOTICES.md`.
|
|
253
|
+
|
|
254
|
+
---
|
|
255
|
+
|
|
256
|
+
## 简体中文
|
|
257
|
+
|
|
258
|
+
**AuthLink 官方前端组件包**,帮助网站接入手机绑定与 App 确认流程,支持 React 18/19、Vue 3.3+、TypeScript 和 ESM。
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
### 直接调用弹窗(React、Vue 或原生 JavaScript)
|
|
262
|
+
|
|
263
|
+
```ts
|
|
264
|
+
import { openAuthLinkBind, openAuthLinkConfirm, requestKey } from '@auth-link/web';
|
|
265
|
+
import '@auth-link/web/styles.css';
|
|
266
|
+
|
|
267
|
+
// 在按钮事件中创建请求,再打开弹窗;client 为前文的同源客户端。
|
|
268
|
+
const ticket = await client.create('confirm', { idempotencyKey: requestKey() });
|
|
269
|
+
const modal = openAuthLinkConfirm({
|
|
270
|
+
ticket, client, locale: 'zh-CN',
|
|
271
|
+
onConfirmed: result => console.log(result.requestId),
|
|
272
|
+
onRejected: result => console.log(result.code), // REJECTED,此时弹窗已关闭
|
|
273
|
+
onClose: () => console.log('已关闭'),
|
|
274
|
+
});
|
|
275
|
+
// 绑定使用 openAuthLinkBind({ ticket: bindingTicket, client })。
|
|
276
|
+
// modal.close(); modal.setLocale('ja');
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
React / Vue 的 AuthLinkBind、AuthLinkConfirm 挂载后也直接打开弹窗,不再内嵌在页面。React 用 onClose、Vue 用 @close 清空 ticket 并卸载组件。服务端渲染不创建弹窗或请求。支持手机窄屏、内容滚动、关闭按钮及 Escape;要求浏览器支持原生 dialog.showModal()。弹窗挂到 document.body,主题变量请配置在 :root 或 .authlink-dialog。
|
|
280
|
+
|
|
281
|
+
关闭只停止前端查询,不拒绝或撤销服务端请求;请求仍按原有效期结束。确认成功或拒绝后弹窗自动关闭,分别回调 onConfirmed 或 onRejected,不触发用户关闭回调。直接调用时,页面卸载须执行 modal.destroy() 清理,它不触发用户关闭回调。切换语言用 modal.setLocale(),不会重建鉴权请求。
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
### 功能
|
|
285
|
+
|
|
286
|
+
- **扫码绑定**:网站显示二维码,用户使用 AuthLink App 扫码并确认绑定。
|
|
287
|
+
- **手机确认**:电脑与 App 显示同步变化的六位数字,每 30 秒更新,用户核对后在 App 内批准。
|
|
288
|
+
- **结果通知**:提供确认成功、拒绝、过期和错误事件,方便网站更新界面。
|
|
289
|
+
- **框架支持**:React 组件与 hook、Vue 组件与 composable,以及无框架客户端。
|
|
290
|
+
- **多语言与主题**:支持下表中的 13 种语言,默认简体中文;独立 CSS 和可覆盖的主题变量。
|
|
291
|
+
- **生命周期管理**:请求切换或组件卸载时取消轮询,数字过期后自动隐藏。
|
|
292
|
+
|
|
293
|
+
此包是 AuthLink 的网页接入层,需要配套应用后台、AuthLink 服务和手机 App。完整 AuthLink 流程校验原手机硬件密钥证明及 App 内批准,不要求系统通行密钥、指纹或锁屏;安装此包本身不会建立鉴权服务。
|
|
294
|
+
|
|
295
|
+
### 接入步骤
|
|
296
|
+
|
|
297
|
+
1. 在应用后台完成站点租户配置,并实现本文的创建请求、查询结果两个同源接口。
|
|
298
|
+
2. 安装包、导入样式,创建指向应用后台的客户端。
|
|
299
|
+
3. 用户点击绑定或验证时创建请求,将返回的 ticket 交给 React/Vue 组件。
|
|
300
|
+
4. 用户在 App 确认后,应用后台验证并消费结果,组件收到可信确认事件。
|
|
301
|
+
|
|
302
|
+
租户密钥只保存在应用服务端。用户是否能够继续调用受保护接口,也必须由应用后台判断。
|
|
303
|
+
|
|
304
|
+
### 流程
|
|
305
|
+
|
|
306
|
+
应用后台根据当前登录用户创建 `bind` 或 `confirm` 请求,前端拿到 ticket 后交给组件。组件只轮询应用后台。应用后台在验证归属、state、双重认证策略并消费一次性结果后,才返回 `confirmed`。
|
|
307
|
+
|
|
308
|
+
定时数字用于电脑与手机核对;用户仍在 AuthLink App 内完成 原手机硬件密钥校验和 App 内批准。它不是用户输入网站的 TOTP 登录密码,不能单独授权。请求最长 120 秒;数字变化不延长请求有效期。前端不生成数字、不接收生成 seed,不在数字更新时新建请求。
|
|
309
|
+
|
|
310
|
+
需要配套更新后的 Go 服务和客户端协议 7。服务端与 App 必须使用当前单一协议,不能只升级网页。
|
|
311
|
+
|
|
312
|
+
### 安装
|
|
313
|
+
|
|
314
|
+
在应用项目中安装:
|
|
315
|
+
|
|
316
|
+
```sh
|
|
317
|
+
npm install @auth-link/web
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
### 创建同源应用客户端
|
|
321
|
+
|
|
322
|
+
```ts
|
|
323
|
+
import { createAuthLinkClient, requestKey } from '@auth-link/web';
|
|
324
|
+
import '@auth-link/web/styles.css';
|
|
325
|
+
|
|
326
|
+
// 不要传 AuthLink 租户 API key。此客户端只调用当前应用网站后台。
|
|
327
|
+
export const client = createAuthLinkClient({
|
|
328
|
+
baseUrl: '/api/authlink',
|
|
329
|
+
csrfToken: () => readCsrfTokenFromYourCms(),
|
|
330
|
+
authLinkOrigin: 'https://你的-authlink-域名', // 公开来源,用于核对二维码
|
|
331
|
+
});
|
|
332
|
+
// 在用户点击“绑定”或“验证”时调用;不要在 React effect / Vue watch 中创建请求。
|
|
333
|
+
const key = requestKey();
|
|
334
|
+
const ticket = await client.create('bind', { idempotencyKey: key });
|
|
335
|
+
// 创建响应丢失时复用 key 重试,不能每次重试都生成新 key。
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
`create` 只传用途;网站账号由应用后台的登录 session 决定。也可以自己请求后台,把 ticket 和自定义 `AuthLinkClient` 交给组件,适配 Axios、GraphQL 或其他认证方式。
|
|
339
|
+
|
|
340
|
+
### React
|
|
341
|
+
|
|
342
|
+
```tsx
|
|
343
|
+
import { AuthLinkBind, AuthLinkConfirm } from '@auth-link/web/react';
|
|
344
|
+
import type { Ticket } from '@auth-link/web';
|
|
345
|
+
import { client } from './authlink-client'; // 上文创建的客户端
|
|
346
|
+
|
|
347
|
+
export function BindPanel({ ticket }: { ticket: Ticket }) {
|
|
348
|
+
return <AuthLinkBind ticket={ticket} client={client} locale="zh-CN"
|
|
349
|
+
onConfirmed={result => showBound(result.requestId)}
|
|
350
|
+
onRejected={() => showRejected()} onExpired={() => showExpired()}
|
|
351
|
+
onError={error => showNetworkError(error.message)} />;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
export function ConfirmPanel({ ticket }: { ticket: Ticket }) {
|
|
355
|
+
return <AuthLinkConfirm ticket={ticket} client={client} locale="zh-CN"
|
|
356
|
+
onConfirmed={result => showConfirmed(result.requestId)} />;
|
|
357
|
+
}
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
确认组件默认只显示动态数字;`showQr` 可额外显示扫码入口。绑定组件始终显示二维码。`onRefresh` 可提供重新创建请求的函数;仅过期后显示该按钮。组件自身的“重试”只恢复当前请求,不创建第二个请求。
|
|
361
|
+
|
|
362
|
+
未指定 `locale` 时使用简体中文;不支持的语言代码也回退到简体中文。组件根元素设置对应的 `lang`,语言切换不会重新创建鉴权请求。
|
|
363
|
+
|
|
364
|
+
React 入口保留 `"use client"`,可用于 Next.js 客户端组件。导入包和 SSR 渲染不会创建请求。将 client 与 ticket 保持为稳定对象,例如模块级 client 和 React state 中的 ticket。卸载/切换 ticket 会取消旧轮询;StrictMode 不会创建额外请求。
|
|
365
|
+
|
|
366
|
+
### Vue
|
|
367
|
+
|
|
368
|
+
```vue
|
|
369
|
+
<script setup lang="ts">
|
|
370
|
+
import { AuthLinkBind, AuthLinkConfirm } from '@auth-link/web/vue';
|
|
371
|
+
import type { Ticket, Confirmation } from '@auth-link/web';
|
|
372
|
+
import '@auth-link/web/styles.css';
|
|
373
|
+
import { client } from './authlink-client'; // 上文创建的客户端
|
|
374
|
+
defineProps<{ ticket: Ticket; mode: 'bind' | 'confirm' }>();
|
|
375
|
+
function confirmed(result: Confirmation) { showConfirmed(result.requestId); }
|
|
376
|
+
</script>
|
|
377
|
+
|
|
378
|
+
<template>
|
|
379
|
+
<AuthLinkBind v-if="mode === 'bind'" :ticket="ticket" :client="client"
|
|
380
|
+
locale="zh-CN" @confirmed="confirmed" @expired="showExpired" />
|
|
381
|
+
<AuthLinkConfirm v-else :ticket="ticket" :client="client"
|
|
382
|
+
locale="zh-CN" @confirmed="confirmed" @rejected="showRejected" />
|
|
383
|
+
</template>
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
拒绝时返回 `{ status: 'rejected', code: 'REJECTED', requestId, purpose }`。`REJECTED` 是 SDK 业务状态码,不是 HTTP 状态码或授权凭证。组件自动关闭,不再显示“已拒绝 / 重新发起”面板,也不触发 `onClose`;提示和重新发起由接入网站在回调中决定。React / Vue 请在 `onRejected` / `@rejected` 中清空 ticket、卸载组件。无 UI 的 hook 仍保留 `snapshot.status === 'rejected'`,供自定义界面使用。
|
|
387
|
+
|
|
388
|
+
Vue 可通过 `:on-refresh="createNewRequest"` 提供重新创建操作。`useAuthLink(() => options)` composable 与 React 的 `useAuthLink(options)` hook 可用于自定义界面,返回 `snapshot` 和 `retry`;Vue 的 snapshot 是浅 ref。
|
|
389
|
+
|
|
390
|
+
### 选择弹窗样式
|
|
391
|
+
|
|
392
|
+
初始化时通过 `variant` 选择样式,**不传时默认 A**。
|
|
393
|
+
|
|
394
|
+
| 参数值 | 电脑端 | 手机端 |
|
|
395
|
+
| --- | --- | --- |
|
|
396
|
+
| `A`(默认) | 极简居中卡片、细线数字更新进度条 | 居中卡片 |
|
|
397
|
+
| `B` | 二维码和核对码左右分栏 | 自动纵向排列 |
|
|
398
|
+
| `C` | 紧凑卡片、底部取消入口 | 底部弹层,适配安全区域 |
|
|
399
|
+
|
|
400
|
+
```ts
|
|
401
|
+
openAuthLinkBind({ ticket, client }); // 默认 A、简体中文
|
|
402
|
+
openAuthLinkConfirm({ ticket, client, variant: 'B' });
|
|
403
|
+
// modal.setVariant('C'); // 只更新显示,不重建请求、不重启查询
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
```tsx
|
|
407
|
+
<AuthLinkBind ticket={ticket} client={client} variant="C" />
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
```vue
|
|
411
|
+
<AuthLinkConfirm :ticket="ticket" :client="client" variant="B" />
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
三套样式统一使用细进度条和固定文案“核对码每 30 秒自动更新”,不显示数字更新秒数。底部通常显示“等待手机确认”;仅最后 30 秒改为柔和橙色“请求即将过期 · 00:29”。新核对码轻微淡入,遵守系统减少动态效果设置;数字刷新不延长请求有效期。
|
|
415
|
+
|
|
416
|
+
三套样式使用相同的鉴权、过期和回调逻辑。React / Vue 可响应式切换 `variant`,不会重新发起请求;无效的运行时值回退到 A。B / C 的底部“取消”和右上角关闭行为一致,仅停止前端观察,不会替用户拒绝服务端请求。确认成功仍自动关闭。关闭图标保留 44px 点击区域和键盘焦点提示。
|
|
417
|
+
|
|
418
|
+
### 组件参数与事件
|
|
419
|
+
|
|
420
|
+
`AuthLinkBind` 用于绑定二维码,`AuthLinkConfirm` 用于手机确认。两者使用相同的主要参数:
|
|
421
|
+
|
|
422
|
+
| 参数 | 类型 | 说明 |
|
|
423
|
+
| --- | --- | --- |
|
|
424
|
+
| `ticket` | `Ticket`,必填 |应用后台创建的当前请求;组件不会自行创建请求 |
|
|
425
|
+
| `client` | `AuthLinkClient`,必填 | 查询应用后台结果的客户端 |
|
|
426
|
+
| `locale` | `Locale`,见下表 | 界面语言,默认 `zh-CN` |
|
|
427
|
+
| `variant` | `DialogVariant` | `A`(默认)、`B` 或 `C`;只改变布局 |
|
|
428
|
+
| `showQr` | `boolean` | 确认组件是否额外显示二维码;绑定组件始终显示 |
|
|
429
|
+
| `intervalMs` | `number` | 结果轮询间隔,默认 1500 毫秒 |
|
|
430
|
+
| `onRefresh` | `() => void` | 过期后,由网站重新创建请求并更新 ticket |
|
|
431
|
+
|
|
432
|
+
| React 回调 / Vue 事件 | 参数 | 触发时机 |
|
|
433
|
+
| --- | --- | --- |
|
|
434
|
+
| `onConfirmed` / `confirmed` | `Confirmation` |应用后台完成结果验证和消费后 |
|
|
435
|
+
| `onRejected` / `rejected` | `Rejection` | 手机拒绝请求,弹窗自动关闭 |
|
|
436
|
+
| `onExpired` / `expired` | 无 | 请求已过期 |
|
|
437
|
+
| `onError` / `error` | `Error` | 网络错误、响应校验失败或成功回调执行失败等 |
|
|
438
|
+
|
|
439
|
+
业务处理以 `requestId` / `verificationId` 做持久化幂等。前端成功事件用于展示状态,不能作为后台接口放行凭据。
|
|
440
|
+
|
|
441
|
+
### 导入入口
|
|
442
|
+
|
|
443
|
+
| 入口 | 用途 |
|
|
444
|
+
| --- | --- |
|
|
445
|
+
| `@auth-link/web` | `createAuthLinkClient`、`requestKey`、公开类型及无框架工具 |
|
|
446
|
+
| `@auth-link/web/react` | React 组件和 `useAuthLink` |
|
|
447
|
+
| `@auth-link/web/vue` | Vue 组件和 `useAuthLink` |
|
|
448
|
+
| `@auth-link/web/styles.css` | 组件样式,需显式导入 |
|
|
449
|
+
|
|
450
|
+
###应用后台接口约定
|
|
451
|
+
|
|
452
|
+
默认前缀 `/api/authlink`;下面两个接口由接入方实现,**不是直接把浏览器请求转发到租户密钥 API**。
|
|
453
|
+
|
|
454
|
+
#### `POST /requests`
|
|
455
|
+
|
|
456
|
+
浏览器携带自己的应用session、`X-CSRF-Token`、稳定的 `Idempotency-Key`,body:
|
|
457
|
+
|
|
458
|
+
```json
|
|
459
|
+
{"purpose":"bind"}
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
后台从 session 取得用户 ID,创建并持久保存 state、创建/消费幂等键、用户/session 关联,然后调用 AuthLink 服务端创建请求。返回其公开 ticket:
|
|
463
|
+
|
|
464
|
+
```ts
|
|
465
|
+
type Ticket = {
|
|
466
|
+
requestId: string;
|
|
467
|
+
purpose: 'bind' | 'confirm';
|
|
468
|
+
qr: string; // AuthLink 返回的 QR JSON,不是 HTML
|
|
469
|
+
pairingCode: string;
|
|
470
|
+
expiresAt: string;
|
|
471
|
+
displayCode: {
|
|
472
|
+
value: string; // 六位数字,保留前导零
|
|
473
|
+
serverTime: string; validUntil: string;
|
|
474
|
+
periodSeconds: 30;
|
|
475
|
+
};
|
|
476
|
+
};
|
|
477
|
+
```
|
|
478
|
+
|
|
479
|
+
#### `POST /requests/{requestId}/result`
|
|
480
|
+
|
|
481
|
+
同样验证 session、CSRF 和请求归属。后台调用 AuthLink 状态接口,将 `displayCode` 原样返回,不能缓存跨过 `validUntil`。新代码由后台实时获取,不是前端自行生成。
|
|
482
|
+
|
|
483
|
+
尚未完成时返回 `{requestId,status,displayCode?}`;状态可为 new / pending / approved / consumed / rejected / expired。`approved` 不能触发成功事件。
|
|
484
|
+
|
|
485
|
+
收到 approved / consumed 后,后台使用保存的稳定消费幂等键调用 `/consume`,核对完整结果,在自己数据库事务中记录此次 session 的确认,再返回已验证的回执。组件确认所需最小字段:
|
|
486
|
+
|
|
487
|
+
```json
|
|
488
|
+
{
|
|
489
|
+
"status":"confirmed",
|
|
490
|
+
"requestId":"43字符请求ID",
|
|
491
|
+
"verificationId":"43字符结果ID",
|
|
492
|
+
"purpose":"confirm",
|
|
493
|
+
"authenticationPolicy":"device-presence.v1",
|
|
494
|
+
"expiresAt":"与创建请求完全一致的期限"
|
|
495
|
+
}
|
|
496
|
+
```
|
|
497
|
+
|
|
498
|
+
服务端必须在同一事务内记录已验证的确认结果,并校验请求归属。网页事件只用于更新界面;受保护接口必须依赖后台的可信确认记录,不能依赖 JS 回调或浏览器传来的 confirmed。
|
|
499
|
+
|
|
500
|
+
### 数字、时钟与安全
|
|
501
|
+
|
|
502
|
+
- 每个请求独立派生数字生成 seed;seed 只通过 claim 发给手机,并纳入设备签名上下文。网页只得到当前数字、服务端时间和本步期限。
|
|
503
|
+
- 倒计时使用服务端时间锚点和单调时钟;本机系统时钟错误不直接导致倒计时漂移。网络延迟仍有影响,跨步时可能短暂显示“正在更新数字”,稳定后再核对。
|
|
504
|
+
- 旧数字一过本步期限就隐藏,即使网络请求仍未返回。整个请求过期、拒绝或失败会隐藏数字和二维码。
|
|
505
|
+
- 组件按请求检查成功结果、用途、双重认证策略和有效期。取消、卸载和切换请求后,旧响应不能触发成功回调。
|
|
506
|
+
- 每个组件实例对同一请求只发一次成功事件;刷新或重新挂载不替代网站持久化幂等。
|
|
507
|
+
- 包不接收网站业务授权内容,不提供 apiKey 配置,不执行支付/授权等业务操作。
|
|
508
|
+
- 二维码在浏览器内生成,忽略后台 qrSvg,不使用 innerHTML;不上传给第三方图片服务。应用 CSP 需允许 `img-src 'self' data:`。
|
|
509
|
+
|
|
510
|
+
### 样式与语言
|
|
511
|
+
|
|
512
|
+
CSS 单独导入,所有类使用 `authlink-` 前缀。可在宿主容器覆盖 `--authlink-foreground`、`--authlink-background`、`--authlink-accent`、`--authlink-muted`。面板语言见下表,默认简体中文。
|
|
513
|
+
|
|
514
|
+
|
|
515
|
+
| Code | Language |
|
|
516
|
+
| --- | --- |
|
|
517
|
+
| `en` | English |
|
|
518
|
+
| `zh-CN` | 简体中文 (default) |
|
|
519
|
+
| `zh-TW` | 繁體中文 |
|
|
520
|
+
| `ja` | 日本語 |
|
|
521
|
+
| `bn` | বাংলা |
|
|
522
|
+
| `es` | Español |
|
|
523
|
+
| `fil` | Filipino |
|
|
524
|
+
| `hi` | हिन्दी |
|
|
525
|
+
| `id` | Bahasa Indonesia |
|
|
526
|
+
| `ms` | Bahasa Melayu |
|
|
527
|
+
| `pt` | Português |
|
|
528
|
+
| `th` | ไทย |
|
|
529
|
+
| `vi` | Tiếng Việt |
|
|
530
|
+
|
|
531
|
+
可以从根入口导入 `supportedLocales` 获取支持列表。文档默认英文,另有中文与日文版本。
|
|
532
|
+
|
|
533
|
+
### 兼容性
|
|
534
|
+
|
|
535
|
+
React 18.2+ / 19、Vue 3.3+、TypeScript、ESM。React 和 Vue 是可选 peer,只安装所用框架。构建工具环境需 Node 22.13+。
|
|
536
|
+
|
|
537
|
+
---
|
|
538
|
+
|
|
539
|
+
## 日本語
|
|
540
|
+
|
|
541
|
+
**AuthLink のスマートフォン登録とアプリでの確認**を Web サイトに組み込む React / Vue コンポーネントです。登録用 QR コード、同期する 6 桁の照合コード、カウントダウン、確認結果を表示します。特定のサイト基盤には依存しません。
|
|
542
|
+
|
|
543
|
+
### 関数でダイアログを開く(React / Vue / JavaScript)
|
|
544
|
+
|
|
545
|
+
```ts
|
|
546
|
+
import { openAuthLinkBind, openAuthLinkConfirm, requestKey } from '@auth-link/web';
|
|
547
|
+
import '@auth-link/web/styles.css';
|
|
548
|
+
|
|
549
|
+
const ticket = await client.create('confirm', { idempotencyKey: requestKey() });
|
|
550
|
+
const modal = openAuthLinkConfirm({
|
|
551
|
+
ticket, client, locale: 'ja',
|
|
552
|
+
onConfirmed: result => console.log(result.requestId),
|
|
553
|
+
onClose: () => console.log('Closed'),
|
|
554
|
+
});
|
|
555
|
+
// 登録: openAuthLinkBind({ ticket: bindingTicket, client })
|
|
556
|
+
// modal.close(); modal.setLocale('en');
|
|
557
|
+
```
|
|
558
|
+
|
|
559
|
+
React / Vue コンポーネントもマウント時にダイアログを開きます。React の onClose、Vue の @close で ticket を消去してアンマウントしてください。SSR はダイアログも通信も生成しません。スマートフォンの画面幅、スクロール、閉じるボタン、Escape に対応し、ネイティブ dialog.showModal() が必要です。body 直下に表示するため、テーマ変数は :root または .authlink-dialog に設定します。
|
|
560
|
+
|
|
561
|
+
閉じてもサーバーのリクエストを拒否・取消しません。元の有効期限は維持されます。承認・拒否時は自動で閉じ、onConfirmed または onRejected を通知します。ユーザー操作の onClose は通知しません。関数で開いた場合はページ破棄時に modal.destroy() を呼んで通信と UI を解除してください。destroy() は onClose を通知しません。modal.setLocale() はリクエストを作り直さずに言語を切り替えます。
|
|
562
|
+
|
|
563
|
+
|
|
564
|
+
### 主な機能
|
|
565
|
+
|
|
566
|
+
- ブラウザー内で生成するスマートフォン登録用 QR コード。
|
|
567
|
+
- アプリと同期し、30 秒ごとに更新される照合コード。
|
|
568
|
+
- 承認・拒否・期限切れ・エラーのイベント。
|
|
569
|
+
- React コンポーネントと hook、Vue コンポーネントと composable、フレームワーク非依存の API。
|
|
570
|
+
- **簡体字中国語を既定とする 13 言語**。コンポーネントごとに指定可能。
|
|
571
|
+
- TypeScript、独立した CSS、任意の React / Vue peer dependency。
|
|
572
|
+
|
|
573
|
+
本パッケージはブラウザー用の接続ライブラリです。サイトのバックエンド、AuthLink サービス、モバイルアプリと併用します。AuthLink はアプリ内の承認と元の端末のハードウェア証明を検証します。システムのパスキー、指紋認証、画面ロックは要求しません。
|
|
574
|
+
|
|
575
|
+
### インストール
|
|
576
|
+
|
|
577
|
+
```sh
|
|
578
|
+
npm install @auth-link/web
|
|
579
|
+
```
|
|
580
|
+
|
|
581
|
+
対応環境:React 18.2+ / 19、Vue 3.3+、TypeScript、ESM。利用するフレームワークだけをインストールしてください。ツール環境には Node 22.13+ が必要です。
|
|
582
|
+
|
|
583
|
+
### 接続の流れ
|
|
584
|
+
|
|
585
|
+
1. サイトのバックエンドに AuthLink のテナント資格情報を設定します。
|
|
586
|
+
2. 下記の同一オリジンの作成・結果取得 API を実装します。
|
|
587
|
+
3. ユーザー操作でリクエストを作成し、取得した ticket をコンポーネントに渡します。
|
|
588
|
+
4. バックエンドが結果を検証・消費した後に確認完了を返します。
|
|
589
|
+
|
|
590
|
+
ブラウザーからこのライブラリを呼び出せますが、テナント API キーと結果の消費処理は必ずサーバー側に置きます。標準クライアントは同一オリジンのバックエンドに接続します。独自の通信方式では `AuthLinkClient` を実装できますが、資格情報と検証をブラウザーに移さないでください。
|
|
591
|
+
|
|
592
|
+
### クライアント作成
|
|
593
|
+
|
|
594
|
+
```ts
|
|
595
|
+
// authlink-client.ts
|
|
596
|
+
import { createAuthLinkClient, requestKey } from '@auth-link/web';
|
|
597
|
+
import '@auth-link/web/styles.css';
|
|
598
|
+
|
|
599
|
+
export const client = createAuthLinkClient({
|
|
600
|
+
baseUrl: '/api/authlink',
|
|
601
|
+
csrfToken: () => {
|
|
602
|
+
const token = document.querySelector<HTMLMetaElement>('meta[name="csrf-token"]')?.content;
|
|
603
|
+
if (!token) throw new Error('Missing CSRF token');
|
|
604
|
+
return token;
|
|
605
|
+
},
|
|
606
|
+
authLinkOrigin: 'https://auth.example.com',
|
|
607
|
+
});
|
|
608
|
+
|
|
609
|
+
// Call from a user action. Keep this key if you retry the same creation.
|
|
610
|
+
const idempotencyKey = requestKey();
|
|
611
|
+
const ticket = await client.create('bind', { idempotencyKey });
|
|
612
|
+
// For a mobile confirmation, use client.create('confirm', { idempotencyKey }).
|
|
613
|
+
```
|
|
614
|
+
|
|
615
|
+
meta 要素にサイトのセッションに対応する CSRF トークンを設定し、AuthLink の HTTPS オリジンを実際の設定に置き換えてください。作成処理はクリックハンドラーで実行し、マウント時の effect では実行しません。コンポーネントは既存のリクエストを監視します。同じ作成操作を再試行する際は同じ冪等キーを使います。
|
|
616
|
+
|
|
617
|
+
### React
|
|
618
|
+
|
|
619
|
+
```tsx
|
|
620
|
+
import { AuthLinkBind, AuthLinkConfirm } from '@auth-link/web/react';
|
|
621
|
+
import type { Ticket, Confirmation } from '@auth-link/web';
|
|
622
|
+
import { client } from './authlink-client';
|
|
623
|
+
|
|
624
|
+
export function VerificationPanel({ ticket }: { ticket: Ticket }) {
|
|
625
|
+
const onConfirmed = (result: Confirmation) => {
|
|
626
|
+
console.log('Confirmed request:', result.requestId);
|
|
627
|
+
};
|
|
628
|
+
return ticket.purpose === 'bind'
|
|
629
|
+
? <AuthLinkBind ticket={ticket} client={client} onConfirmed={onConfirmed} />
|
|
630
|
+
: <AuthLinkConfirm ticket={ticket} client={client} locale="en"
|
|
631
|
+
onConfirmed={onConfirmed} onExpired={() => console.log('Expired')} />;
|
|
632
|
+
}
|
|
633
|
+
```
|
|
634
|
+
|
|
635
|
+
`locale` を省略すると簡体字中国語です。日本語は `locale="ja"`、簡体字中国語は `locale="zh-CN"` を指定します。client と ticket はレンダー間で安定したオブジェクトにしてください。React エントリーは `"use client"` を保持し、SSR ではリクエストを作成しません。
|
|
636
|
+
|
|
637
|
+
### Vue
|
|
638
|
+
|
|
639
|
+
```vue
|
|
640
|
+
<script setup lang="ts">
|
|
641
|
+
import { AuthLinkBind, AuthLinkConfirm } from '@auth-link/web/vue';
|
|
642
|
+
import type { Ticket, Confirmation } from '@auth-link/web';
|
|
643
|
+
import { client } from './authlink-client';
|
|
644
|
+
|
|
645
|
+
defineProps<{ ticket: Ticket }>();
|
|
646
|
+
function confirmed(result: Confirmation) {
|
|
647
|
+
console.log('Confirmed request:', result.requestId);
|
|
648
|
+
}
|
|
649
|
+
</script>
|
|
650
|
+
|
|
651
|
+
<template>
|
|
652
|
+
<AuthLinkBind v-if="ticket.purpose === 'bind'" :ticket="ticket" :client="client"
|
|
653
|
+
@confirmed="confirmed" />
|
|
654
|
+
<AuthLinkConfirm v-else :ticket="ticket" :client="client" locale="ja"
|
|
655
|
+
@confirmed="confirmed" />
|
|
656
|
+
</template>
|
|
657
|
+
```
|
|
658
|
+
|
|
659
|
+
`:locale="selectedLanguage"` で表示言語を切り替えられます。言語切り替えによって新しい認証リクエストは作成されません。
|
|
660
|
+
|
|
661
|
+
### レイアウトの選択
|
|
662
|
+
|
|
663
|
+
初期化時の `variant` で選択します。省略時は **A** です。
|
|
664
|
+
|
|
665
|
+
| 値 | デスクトップ | モバイル |
|
|
666
|
+
| --- | --- | --- |
|
|
667
|
+
| `A`(既定) | ミニマルなカードと細いコード更新バー | 中央のカード |
|
|
668
|
+
| `B` | QR と照合コードの二列表示 | 縦並び |
|
|
669
|
+
| `C` | コンパクトなカードとキャンセル操作 | セーフエリア対応のボトムシート |
|
|
670
|
+
|
|
671
|
+
```ts
|
|
672
|
+
openAuthLinkBind({ ticket, client }); // A、既定言語は簡体字中国語
|
|
673
|
+
openAuthLinkConfirm({ ticket, client, variant: 'B' });
|
|
674
|
+
// modal.setVariant('C');
|
|
675
|
+
```
|
|
676
|
+
|
|
677
|
+
```tsx
|
|
678
|
+
<AuthLinkBind ticket={ticket} client={client} variant="C" />
|
|
679
|
+
```
|
|
680
|
+
|
|
681
|
+
```vue
|
|
682
|
+
<AuthLinkConfirm :ticket="ticket" :client="client" variant="B" />
|
|
683
|
+
```
|
|
684
|
+
|
|
685
|
+
すべてのレイアウトで細い進捗バーと「照合コードは30秒ごとに自動更新」を表示します。通常は「スマートフォンの確認待ち」を表示し、残り30秒になったときだけオレンジ色の期限警告と 00:29 形式に切り替わります。コード変更時の短いフェードはモーション低減設定に従い、リクエスト期限は延長されません。
|
|
686
|
+
|
|
687
|
+
React / Vue では表示中にも変更でき、リクエストやポーリングは再作成されません。未対応の値は A に戻ります。B / C のキャンセル操作は閉じるアイコンと同じく表示の監視を停止し、サーバーのリクエストは拒否しません。確認成功時は自動で閉じます。閉じる操作のタッチ領域は 44px で、キーボードのフォーカス表示も維持します。
|
|
688
|
+
|
|
689
|
+
### コンポーネントの設定
|
|
690
|
+
|
|
691
|
+
| 設定 | 型 | 説明 |
|
|
692
|
+
| --- | --- | --- |
|
|
693
|
+
| `ticket` | `Ticket`、必須 | バックエンドで作成したリクエスト |
|
|
694
|
+
| `client` | `AuthLinkClient`、必須 | バックエンドの結果を取得するクライアント |
|
|
695
|
+
| `locale` | `Locale` | 既定は `zh-CN` |
|
|
696
|
+
| `variant` | `DialogVariant` | `A`(既定)、`B`、`C` |
|
|
697
|
+
| `showQr` | `boolean` | 確認画面にも QR を表示。登録画面では常に表示 |
|
|
698
|
+
| `intervalMs` | `number` | 250~10000 ミリ秒。既定は 1500 ミリ秒 |
|
|
699
|
+
| `onRefresh` | `() => void` | 期限切れ後、新規作成して ticket を更新する処理 |
|
|
700
|
+
|
|
701
|
+
| React コールバック / Vue イベント | 引数 | 意味 |
|
|
702
|
+
| --- | --- | --- |
|
|
703
|
+
| `onConfirmed` / `confirmed` | `Confirmation` | バックエンドによる検証と消費が完了 |
|
|
704
|
+
| `onRejected` / `rejected` | `Rejection` | 拒否時はダイアログを自動で閉じる |
|
|
705
|
+
| `onExpired` / `expired` | なし | 有効期限切れ |
|
|
706
|
+
| `onError` / `error` | `Error` | 通信・応答検証・コールバックのエラー |
|
|
707
|
+
|
|
708
|
+
拒否時は `{ status: 'rejected', code: 'REJECTED', requestId, purpose }` を返します。`REJECTED` は SDK の業務ステータスで、HTTP ステータスや承認証明ではありません。拒否・再試行画面は表示せず、`onClose` も通知しません。メッセージや新規作成はサイト側で処理してください。React / Vue では `onRejected` / `@rejected` で ticket を解除してコンポーネントを破棄します。ヘッドレス hook の `snapshot.status` は `rejected` のままです。
|
|
709
|
+
|
|
710
|
+
独自 UI には React の `useAuthLink(options)`、Vue の `useAuthLink(() => options)` を利用できます。返される値は `snapshot` と `retry` です。Vue の snapshot は shallow ref です。retry は既存リクエストを再開し、onRefresh はサイト側のコードで新規作成します。
|
|
711
|
+
|
|
712
|
+
### バックエンド API
|
|
713
|
+
|
|
714
|
+
既定の接頭辞は `/api/authlink` です。両 API でサイトのセッション、`X-CSRF-Token`、リクエストの所有者を検証してください。作成時は `Idempotency-Key` も送信されます。サイトのユーザー ID はセッションから決定します。
|
|
715
|
+
|
|
716
|
+
```ts
|
|
717
|
+
import type { Ticket, Result } from '@auth-link/web';
|
|
718
|
+
|
|
719
|
+
// POST /api/authlink/requests
|
|
720
|
+
// Body: { purpose: 'bind' | 'confirm' }
|
|
721
|
+
// Response: Ticket
|
|
722
|
+
|
|
723
|
+
// POST /api/authlink/requests/{requestId}/result
|
|
724
|
+
// Body: {}
|
|
725
|
+
// Response: Result
|
|
726
|
+
```
|
|
727
|
+
|
|
728
|
+
```json
|
|
729
|
+
{
|
|
730
|
+
"status": "confirmed",
|
|
731
|
+
"requestId": "<request-id>",
|
|
732
|
+
"verificationId": "<verification-id>",
|
|
733
|
+
"purpose": "confirm",
|
|
734
|
+
"authenticationPolicy": "device-presence.v1",
|
|
735
|
+
"expiresAt": "<same-expiry-as-ticket>"
|
|
736
|
+
}
|
|
737
|
+
```
|
|
738
|
+
|
|
739
|
+
作成 API は AuthLink の公開 ticket(`requestId`、`purpose`、`qr`、`pairingCode`、`expiresAt`、現在の `displayCode`)を返します。displayCode は `value`、`serverTime`、`validUntil`、`periodSeconds: 30` を持ちます。確認用 ticket には displayCode が必要です。
|
|
740
|
+
|
|
741
|
+
結果 API は未完了状態と最新の表示コードを返します。`new` / `pending` / `approved` / `consumed` だけでは成功イベントは発火しません。`confirmed` を返す前に、バックエンドは保存した state と消費用冪等キーで AuthLink の結果を消費し、完全な受領情報を検証して正しいセッションに記録します。requestId / verificationId による永続的な冪等処理が必要です。
|
|
742
|
+
|
|
743
|
+
### 対応言語
|
|
744
|
+
|
|
745
|
+
| Code | Language |
|
|
746
|
+
| --- | --- |
|
|
747
|
+
| `en` | English |
|
|
748
|
+
| `zh-CN` | 简体中文 (default) |
|
|
749
|
+
| `zh-TW` | 繁體中文 |
|
|
750
|
+
| `ja` | 日本語 |
|
|
751
|
+
| `bn` | বাংলা |
|
|
752
|
+
| `es` | Español |
|
|
753
|
+
| `fil` | Filipino |
|
|
754
|
+
| `hi` | हिन्दी |
|
|
755
|
+
| `id` | Bahasa Indonesia |
|
|
756
|
+
| `ms` | Bahasa Melayu |
|
|
757
|
+
| `pt` | Português |
|
|
758
|
+
| `th` | ไทย |
|
|
759
|
+
| `vi` | Tiếng Việt |
|
|
760
|
+
|
|
761
|
+
未指定・未対応の言語コードは簡体字中国語にフォールバックします。ルート要素には `lang` が設定されます。ルートエントリーの `supportedLocales` で一覧を取得できます。言語変更は表示文言だけに作用し、数字やリクエストは変更しません。ドキュメントは英語・中国語・日本語で提供します。
|
|
762
|
+
|
|
763
|
+
### スタイルとエントリー
|
|
764
|
+
|
|
765
|
+
`@auth-link/web/styles.css` を明示的に読み込みます。親要素で `--authlink-foreground`、`--authlink-background`、`--authlink-accent`、`--authlink-muted` を上書きできます。クラス名の接頭辞は `authlink-` です。
|
|
766
|
+
|
|
767
|
+
| エントリー | 内容 |
|
|
768
|
+
| --- | --- |
|
|
769
|
+
| `@auth-link/web` | クライアント、冪等キー、対応言語、型、基本 API |
|
|
770
|
+
| `@auth-link/web/react` | React コンポーネントと hook |
|
|
771
|
+
| `@auth-link/web/vue` | Vue コンポーネントと composable |
|
|
772
|
+
| `@auth-link/web/styles.css` | スタイル |
|
|
773
|
+
|
|
774
|
+
### セキュリティと有効期限
|
|
775
|
+
|
|
776
|
+
- 数字はアプリとの目視照合用です。数字だけで認証や承認はできません。
|
|
777
|
+
- ブラウザーに生成 seed、端末秘密鍵、テナント秘密情報を渡しません。
|
|
778
|
+
- リクエストは最大 120 秒。コード更新で期限は延長されません。サービスとアプリはモバイルプロトコル 4 に対応する必要があります。
|
|
779
|
+
- サーバー時刻と単調増加時計で残り時間を計算し、通信が止まっても期限切れの数字は隠します。更新境界で短い待ち時間が生じる場合があります。
|
|
780
|
+
- アンマウントや ticket 変更で監視を中止します。インスタンス内のイベント重複防止とは別に、バックエンドで永続的な冪等処理が必要です。
|
|
781
|
+
- フロントエンドのイベントは UI 更新用です。保護 API はバックエンドの信頼できる記録で判断します。
|
|
782
|
+
- QR はローカル生成し、バックエンドの SVG を挿入しません。CSP は `img-src 'self' data:` を許可してください。
|
|
783
|
+
|
|
784
|
+
### ライセンス
|
|
785
|
+
|
|
786
|
+
UNLICENSED。公開パッケージであってもオープンソース利用許諾は付与されません。同梱ライブラリの帰属表示は `THIRD_PARTY_NOTICES.md` を参照してください。
|