@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
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
# AuthLink Web
|
|
2
|
+
|
|
3
|
+
**English** | [简体中文](./README.zh-CN.md) | [日本語](./README.ja.md)
|
|
4
|
+
|
|
5
|
+
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.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- Phone-binding QR codes generated locally in the browser.
|
|
10
|
+
- Matching numbers that update every 30 seconds, synchronized with the AuthLink app.
|
|
11
|
+
- Confirmation, rejection, expiry and error callbacks.
|
|
12
|
+
- React components and hook, Vue components and composable, and framework-independent tools.
|
|
13
|
+
- 13 interface languages, **Simplified Chinese by default**, with per-component language selection.
|
|
14
|
+
- TypeScript declarations, separate CSS and optional React/Vue peer dependencies.
|
|
15
|
+
|
|
16
|
+
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.
|
|
17
|
+
|
|
18
|
+
## Install
|
|
19
|
+
|
|
20
|
+
```sh
|
|
21
|
+
npm install @auth-link/web
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Compatibility: React 18.2+ / 19, Vue 3.3+, TypeScript, ESM. Install only the framework you use. The package declares Node 22.13+ for tooling.
|
|
25
|
+
|
|
26
|
+
## Browser integration
|
|
27
|
+
|
|
28
|
+
1. Configure your backend's AuthLink tenant credentials.
|
|
29
|
+
2. Implement the two same-origin endpoints below, authenticated to the current website user.
|
|
30
|
+
3. Create a request on a user action and pass its ticket to a component.
|
|
31
|
+
4. Your backend verifies and consumes the result before returning a confirmed response.
|
|
32
|
+
|
|
33
|
+
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.
|
|
34
|
+
|
|
35
|
+
## Create a client
|
|
36
|
+
|
|
37
|
+
```ts
|
|
38
|
+
// authlink-client.ts
|
|
39
|
+
import { createAuthLinkClient, requestKey } from '@auth-link/web';
|
|
40
|
+
import '@auth-link/web/styles.css';
|
|
41
|
+
|
|
42
|
+
export const client = createAuthLinkClient({
|
|
43
|
+
baseUrl: '/api/authlink',
|
|
44
|
+
csrfToken: () => {
|
|
45
|
+
const token = document.querySelector<HTMLMetaElement>('meta[name="csrf-token"]')?.content;
|
|
46
|
+
if (!token) throw new Error('Missing CSRF token');
|
|
47
|
+
return token;
|
|
48
|
+
},
|
|
49
|
+
authLinkOrigin: 'https://auth.example.com',
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
// Call from a user action. Keep this key if you retry the same creation.
|
|
53
|
+
const idempotencyKey = requestKey();
|
|
54
|
+
const ticket = await client.create('bind', { idempotencyKey });
|
|
55
|
+
// For a mobile confirmation, use client.create('confirm', { idempotencyKey }).
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
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.
|
|
59
|
+
|
|
60
|
+
## Open a dialog directly (React, Vue or plain JavaScript)
|
|
61
|
+
|
|
62
|
+
```ts
|
|
63
|
+
import { openAuthLinkBind, openAuthLinkConfirm, requestKey } from '@auth-link/web';
|
|
64
|
+
import '@auth-link/web/styles.css';
|
|
65
|
+
|
|
66
|
+
// Call in your button handler, after your backend creates the request.
|
|
67
|
+
const ticket = await client.create('confirm', { idempotencyKey: requestKey() });
|
|
68
|
+
const modal = openAuthLinkConfirm({
|
|
69
|
+
ticket, client, locale: 'en',
|
|
70
|
+
onConfirmed: result => console.log('Confirmed:', result.requestId),
|
|
71
|
+
onRejected: result => console.log(result.code), // REJECTED; dialog already closed
|
|
72
|
+
onClose: () => console.log('Dialog closed'),
|
|
73
|
+
});
|
|
74
|
+
// Bind instead: openAuthLinkBind({ ticket: bindingTicket, client }).
|
|
75
|
+
// modal.close(); modal.setLocale('ja');
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
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`.
|
|
79
|
+
|
|
80
|
+
## React
|
|
81
|
+
|
|
82
|
+
```tsx
|
|
83
|
+
import { AuthLinkBind, AuthLinkConfirm } from '@auth-link/web/react';
|
|
84
|
+
import type { Ticket, Confirmation } from '@auth-link/web';
|
|
85
|
+
import { client } from './authlink-client';
|
|
86
|
+
|
|
87
|
+
export function VerificationPanel({ ticket }: { ticket: Ticket }) {
|
|
88
|
+
const onConfirmed = (result: Confirmation) => {
|
|
89
|
+
console.log('Confirmed request:', result.requestId);
|
|
90
|
+
};
|
|
91
|
+
return ticket.purpose === 'bind'
|
|
92
|
+
? <AuthLinkBind ticket={ticket} client={client} onConfirmed={onConfirmed} />
|
|
93
|
+
: <AuthLinkConfirm ticket={ticket} client={client} locale="en"
|
|
94
|
+
onConfirmed={onConfirmed} onExpired={() => console.log('Expired')} />;
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
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.
|
|
99
|
+
|
|
100
|
+
## Vue
|
|
101
|
+
|
|
102
|
+
```vue
|
|
103
|
+
<script setup lang="ts">
|
|
104
|
+
import { AuthLinkBind, AuthLinkConfirm } from '@auth-link/web/vue';
|
|
105
|
+
import type { Ticket, Confirmation } from '@auth-link/web';
|
|
106
|
+
import { client } from './authlink-client';
|
|
107
|
+
|
|
108
|
+
defineProps<{ ticket: Ticket }>();
|
|
109
|
+
function confirmed(result: Confirmation) {
|
|
110
|
+
console.log('Confirmed request:', result.requestId);
|
|
111
|
+
}
|
|
112
|
+
</script>
|
|
113
|
+
|
|
114
|
+
<template>
|
|
115
|
+
<AuthLinkBind v-if="ticket.purpose === 'bind'" :ticket="ticket" :client="client"
|
|
116
|
+
@confirmed="confirmed" />
|
|
117
|
+
<AuthLinkConfirm v-else :ticket="ticket" :client="client" locale="ja"
|
|
118
|
+
@confirmed="confirmed" />
|
|
119
|
+
</template>
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
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.
|
|
123
|
+
|
|
124
|
+
## Choose a layout
|
|
125
|
+
|
|
126
|
+
Set `variant` when opening a dialog or mounting a component. Omit it for **A**.
|
|
127
|
+
|
|
128
|
+
| Value | Desktop | Mobile |
|
|
129
|
+
| --- | --- | --- |
|
|
130
|
+
| `A` (default) | Minimal centered card with a thin code-refresh progress bar | Centered card |
|
|
131
|
+
| `B` | QR and matching code in two columns | Vertical layout |
|
|
132
|
+
| `C` | Compact card with footer cancellation | Bottom sheet with safe-area padding |
|
|
133
|
+
|
|
134
|
+
```ts
|
|
135
|
+
openAuthLinkBind({ ticket, client }); // A, Simplified Chinese by default
|
|
136
|
+
openAuthLinkConfirm({ ticket, client, variant: 'B' });
|
|
137
|
+
// modal.setVariant('C'); // Presentation only; preserves the request and polling.
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
```tsx
|
|
141
|
+
<AuthLinkBind ticket={ticket} client={client} variant="C" />
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
```vue
|
|
145
|
+
<AuthLinkConfirm :ticket="ticket" :client="client" variant="B" />
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
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.
|
|
149
|
+
|
|
150
|
+
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.
|
|
151
|
+
|
|
152
|
+
## Component options
|
|
153
|
+
|
|
154
|
+
| Option | Type | Meaning |
|
|
155
|
+
| --- | --- | --- |
|
|
156
|
+
| `ticket` | `Ticket`, required | Existing request returned by your backend |
|
|
157
|
+
| `client` | `AuthLinkClient`, required | Client that queries your backend |
|
|
158
|
+
| `locale` | `Locale` | Interface language; defaults to `zh-CN` |
|
|
159
|
+
| `variant` | `DialogVariant` | `A` (default), `B` or `C`; changes layout only |
|
|
160
|
+
| `showQr` | `boolean` | Optional QR on confirmation panels; binding panels always show it |
|
|
161
|
+
| `intervalMs` | `number` | Polling interval, 250–10000 ms; default 1500 ms |
|
|
162
|
+
| `onRefresh` | `() => void` | Create a new request from the expiry panel and replace the ticket |
|
|
163
|
+
|
|
164
|
+
| React callback / Vue event | Argument | Meaning |
|
|
165
|
+
| --- | --- | --- |
|
|
166
|
+
| `onConfirmed` / `confirmed` | `Confirmation` | Backend has verified and consumed the result |
|
|
167
|
+
| `onRejected` / `rejected` | `Rejection` | Phone rejected; dialog closes automatically |
|
|
168
|
+
| `onExpired` / `expired` | None | Request expired |
|
|
169
|
+
| `onClose` / `close` | None | Dialog closed; unmount the component / clear its ticket |
|
|
170
|
+
| `onError` / `error` | `Error` | Network, response validation or callback failure |
|
|
171
|
+
|
|
172
|
+
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.
|
|
173
|
+
|
|
174
|
+
`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.
|
|
175
|
+
|
|
176
|
+
## Backend contract
|
|
177
|
+
|
|
178
|
+
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.
|
|
179
|
+
|
|
180
|
+
```ts
|
|
181
|
+
import type { Ticket, Result } from '@auth-link/web';
|
|
182
|
+
|
|
183
|
+
// POST /api/authlink/requests
|
|
184
|
+
// Body: { purpose: 'bind' | 'confirm' }
|
|
185
|
+
// Response: Ticket
|
|
186
|
+
|
|
187
|
+
// POST /api/authlink/requests/{requestId}/result
|
|
188
|
+
// Body: {}
|
|
189
|
+
// Response: Result
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
```json
|
|
193
|
+
{
|
|
194
|
+
"status": "confirmed",
|
|
195
|
+
"requestId": "<request-id>",
|
|
196
|
+
"verificationId": "<verification-id>",
|
|
197
|
+
"purpose": "confirm",
|
|
198
|
+
"authenticationPolicy": "device-presence.v1",
|
|
199
|
+
"expiresAt": "<same-expiry-as-ticket>"
|
|
200
|
+
}
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
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.
|
|
204
|
+
|
|
205
|
+
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.
|
|
206
|
+
|
|
207
|
+
## Languages
|
|
208
|
+
|
|
209
|
+
| Code | Language |
|
|
210
|
+
| --- | --- |
|
|
211
|
+
| `en` | English |
|
|
212
|
+
| `zh-CN` | 简体中文 (default) |
|
|
213
|
+
| `zh-TW` | 繁體中文 |
|
|
214
|
+
| `ja` | 日本語 |
|
|
215
|
+
| `bn` | বাংলা |
|
|
216
|
+
| `es` | Español |
|
|
217
|
+
| `fil` | Filipino |
|
|
218
|
+
| `hi` | हिन्दी |
|
|
219
|
+
| `id` | Bahasa Indonesia |
|
|
220
|
+
| `ms` | Bahasa Melayu |
|
|
221
|
+
| `pt` | Português |
|
|
222
|
+
| `th` | ไทย |
|
|
223
|
+
| `vi` | Tiếng Việt |
|
|
224
|
+
|
|
225
|
+
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.
|
|
226
|
+
|
|
227
|
+
## Styles and entry points
|
|
228
|
+
|
|
229
|
+
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.
|
|
230
|
+
|
|
231
|
+
| Import | Contents |
|
|
232
|
+
| --- | --- |
|
|
233
|
+
| `@auth-link/web` | Client, request key, supported locales, types and core tools |
|
|
234
|
+
| `@auth-link/web/react` | React components and hook |
|
|
235
|
+
| `@auth-link/web/vue` | Vue components and composable |
|
|
236
|
+
| `@auth-link/web/styles.css` | Component styles |
|
|
237
|
+
|
|
238
|
+
## Security and timing
|
|
239
|
+
|
|
240
|
+
- Matching numbers are for visual comparison with the app, not standalone passwords or authorization proofs.
|
|
241
|
+
- The browser receives only public tickets and current display frames, never generation seeds, device keys or tenant secrets.
|
|
242
|
+
- A request lasts at most 120 seconds; rotating the code does not extend it. Backend and app must support AuthLink mobile protocol 7.
|
|
243
|
+
- 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.
|
|
244
|
+
- Component unmount/request changes abort polling. Confirmation is deduplicated per mounted instance; your backend still needs persistent idempotence.
|
|
245
|
+
- Frontend callbacks update UI. Protected APIs must rely on trusted backend records, never a JavaScript callback or a browser-supplied success flag.
|
|
246
|
+
- 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.
|
|
247
|
+
|
|
248
|
+
## License
|
|
249
|
+
|
|
250
|
+
UNLICENSED. Public availability of the package does not grant an open-source license. Bundled third-party attribution is in `THIRD_PARTY_NOTICES.md`.
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
# AuthLink Web
|
|
2
|
+
|
|
3
|
+
[English](../README.md) | [简体中文](./README.zh-CN.md) | **日本語**
|
|
4
|
+
|
|
5
|
+
**AuthLink のスマートフォン登録とアプリでの確認**を Web サイトに組み込む React / Vue コンポーネントです。登録用 QR コード、同期する 6 桁の照合コード、カウントダウン、確認結果を表示します。特定のサイト基盤には依存しません。
|
|
6
|
+
|
|
7
|
+
## 関数でダイアログを開く(React / Vue / JavaScript)
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
import { openAuthLinkBind, openAuthLinkConfirm, requestKey } from '@auth-link/web';
|
|
11
|
+
import '@auth-link/web/styles.css';
|
|
12
|
+
|
|
13
|
+
const ticket = await client.create('confirm', { idempotencyKey: requestKey() });
|
|
14
|
+
const modal = openAuthLinkConfirm({
|
|
15
|
+
ticket, client, locale: 'ja',
|
|
16
|
+
onConfirmed: result => console.log(result.requestId),
|
|
17
|
+
onClose: () => console.log('Closed'),
|
|
18
|
+
});
|
|
19
|
+
// 登録: openAuthLinkBind({ ticket: bindingTicket, client })
|
|
20
|
+
// modal.close(); modal.setLocale('en');
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
React / Vue コンポーネントもマウント時にダイアログを開きます。React の onClose、Vue の @close で ticket を消去してアンマウントしてください。SSR はダイアログも通信も生成しません。スマートフォンの画面幅、スクロール、閉じるボタン、Escape に対応し、ネイティブ dialog.showModal() が必要です。body 直下に表示するため、テーマ変数は :root または .authlink-dialog に設定します。
|
|
24
|
+
|
|
25
|
+
閉じてもサーバーのリクエストを拒否・取消しません。元の有効期限は維持されます。承認・拒否時は自動で閉じ、onConfirmed または onRejected を通知します。ユーザー操作の onClose は通知しません。関数で開いた場合はページ破棄時に modal.destroy() を呼んで通信と UI を解除してください。destroy() は onClose を通知しません。modal.setLocale() はリクエストを作り直さずに言語を切り替えます。
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
## 主な機能
|
|
29
|
+
|
|
30
|
+
- ブラウザー内で生成するスマートフォン登録用 QR コード。
|
|
31
|
+
- アプリと同期し、30 秒ごとに更新される照合コード。
|
|
32
|
+
- 承認・拒否・期限切れ・エラーのイベント。
|
|
33
|
+
- React コンポーネントと hook、Vue コンポーネントと composable、フレームワーク非依存の API。
|
|
34
|
+
- **簡体字中国語を既定とする 13 言語**。コンポーネントごとに指定可能。
|
|
35
|
+
- TypeScript、独立した CSS、任意の React / Vue peer dependency。
|
|
36
|
+
|
|
37
|
+
本パッケージはブラウザー用の接続ライブラリです。サイトのバックエンド、AuthLink サービス、モバイルアプリと併用します。AuthLink はアプリ内の承認と元の端末のハードウェア証明を検証します。システムのパスキー、指紋認証、画面ロックは要求しません。
|
|
38
|
+
|
|
39
|
+
## インストール
|
|
40
|
+
|
|
41
|
+
```sh
|
|
42
|
+
npm install @auth-link/web
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
対応環境:React 18.2+ / 19、Vue 3.3+、TypeScript、ESM。利用するフレームワークだけをインストールしてください。ツール環境には Node 22.13+ が必要です。
|
|
46
|
+
|
|
47
|
+
## 接続の流れ
|
|
48
|
+
|
|
49
|
+
1. サイトのバックエンドに AuthLink のテナント資格情報を設定します。
|
|
50
|
+
2. 下記の同一オリジンの作成・結果取得 API を実装します。
|
|
51
|
+
3. ユーザー操作でリクエストを作成し、取得した ticket をコンポーネントに渡します。
|
|
52
|
+
4. バックエンドが結果を検証・消費した後に確認完了を返します。
|
|
53
|
+
|
|
54
|
+
ブラウザーからこのライブラリを呼び出せますが、テナント API キーと結果の消費処理は必ずサーバー側に置きます。標準クライアントは同一オリジンのバックエンドに接続します。独自の通信方式では `AuthLinkClient` を実装できますが、資格情報と検証をブラウザーに移さないでください。
|
|
55
|
+
|
|
56
|
+
## クライアント作成
|
|
57
|
+
|
|
58
|
+
```ts
|
|
59
|
+
// authlink-client.ts
|
|
60
|
+
import { createAuthLinkClient, requestKey } from '@auth-link/web';
|
|
61
|
+
import '@auth-link/web/styles.css';
|
|
62
|
+
|
|
63
|
+
export const client = createAuthLinkClient({
|
|
64
|
+
baseUrl: '/api/authlink',
|
|
65
|
+
csrfToken: () => {
|
|
66
|
+
const token = document.querySelector<HTMLMetaElement>('meta[name="csrf-token"]')?.content;
|
|
67
|
+
if (!token) throw new Error('Missing CSRF token');
|
|
68
|
+
return token;
|
|
69
|
+
},
|
|
70
|
+
authLinkOrigin: 'https://auth.example.com',
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
// Call from a user action. Keep this key if you retry the same creation.
|
|
74
|
+
const idempotencyKey = requestKey();
|
|
75
|
+
const ticket = await client.create('bind', { idempotencyKey });
|
|
76
|
+
// For a mobile confirmation, use client.create('confirm', { idempotencyKey }).
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
meta 要素にサイトのセッションに対応する CSRF トークンを設定し、AuthLink の HTTPS オリジンを実際の設定に置き換えてください。作成処理はクリックハンドラーで実行し、マウント時の effect では実行しません。コンポーネントは既存のリクエストを監視します。同じ作成操作を再試行する際は同じ冪等キーを使います。
|
|
80
|
+
|
|
81
|
+
## React
|
|
82
|
+
|
|
83
|
+
```tsx
|
|
84
|
+
import { AuthLinkBind, AuthLinkConfirm } from '@auth-link/web/react';
|
|
85
|
+
import type { Ticket, Confirmation } from '@auth-link/web';
|
|
86
|
+
import { client } from './authlink-client';
|
|
87
|
+
|
|
88
|
+
export function VerificationPanel({ ticket }: { ticket: Ticket }) {
|
|
89
|
+
const onConfirmed = (result: Confirmation) => {
|
|
90
|
+
console.log('Confirmed request:', result.requestId);
|
|
91
|
+
};
|
|
92
|
+
return ticket.purpose === 'bind'
|
|
93
|
+
? <AuthLinkBind ticket={ticket} client={client} onConfirmed={onConfirmed} />
|
|
94
|
+
: <AuthLinkConfirm ticket={ticket} client={client} locale="en"
|
|
95
|
+
onConfirmed={onConfirmed} onExpired={() => console.log('Expired')} />;
|
|
96
|
+
}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
`locale` を省略すると簡体字中国語です。日本語は `locale="ja"`、簡体字中国語は `locale="zh-CN"` を指定します。client と ticket はレンダー間で安定したオブジェクトにしてください。React エントリーは `"use client"` を保持し、SSR ではリクエストを作成しません。
|
|
100
|
+
|
|
101
|
+
## Vue
|
|
102
|
+
|
|
103
|
+
```vue
|
|
104
|
+
<script setup lang="ts">
|
|
105
|
+
import { AuthLinkBind, AuthLinkConfirm } from '@auth-link/web/vue';
|
|
106
|
+
import type { Ticket, Confirmation } from '@auth-link/web';
|
|
107
|
+
import { client } from './authlink-client';
|
|
108
|
+
|
|
109
|
+
defineProps<{ ticket: Ticket }>();
|
|
110
|
+
function confirmed(result: Confirmation) {
|
|
111
|
+
console.log('Confirmed request:', result.requestId);
|
|
112
|
+
}
|
|
113
|
+
</script>
|
|
114
|
+
|
|
115
|
+
<template>
|
|
116
|
+
<AuthLinkBind v-if="ticket.purpose === 'bind'" :ticket="ticket" :client="client"
|
|
117
|
+
@confirmed="confirmed" />
|
|
118
|
+
<AuthLinkConfirm v-else :ticket="ticket" :client="client" locale="ja"
|
|
119
|
+
@confirmed="confirmed" />
|
|
120
|
+
</template>
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
`:locale="selectedLanguage"` で表示言語を切り替えられます。言語切り替えによって新しい認証リクエストは作成されません。
|
|
124
|
+
|
|
125
|
+
## レイアウトの選択
|
|
126
|
+
|
|
127
|
+
初期化時の `variant` で選択します。省略時は **A** です。
|
|
128
|
+
|
|
129
|
+
| 値 | デスクトップ | モバイル |
|
|
130
|
+
| --- | --- | --- |
|
|
131
|
+
| `A`(既定) | ミニマルなカードと細いコード更新バー | 中央のカード |
|
|
132
|
+
| `B` | QR と照合コードの二列表示 | 縦並び |
|
|
133
|
+
| `C` | コンパクトなカードとキャンセル操作 | セーフエリア対応のボトムシート |
|
|
134
|
+
|
|
135
|
+
```ts
|
|
136
|
+
openAuthLinkBind({ ticket, client }); // A、既定言語は簡体字中国語
|
|
137
|
+
openAuthLinkConfirm({ ticket, client, variant: 'B' });
|
|
138
|
+
// modal.setVariant('C');
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
```tsx
|
|
142
|
+
<AuthLinkBind ticket={ticket} client={client} variant="C" />
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
```vue
|
|
146
|
+
<AuthLinkConfirm :ticket="ticket" :client="client" variant="B" />
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
すべてのレイアウトで細い進捗バーと「照合コードは30秒ごとに自動更新」を表示します。通常は「スマートフォンの確認待ち」を表示し、残り30秒になったときだけオレンジ色の期限警告と 00:29 形式に切り替わります。コード変更時の短いフェードはモーション低減設定に従い、リクエスト期限は延長されません。
|
|
150
|
+
|
|
151
|
+
React / Vue では表示中にも変更でき、リクエストやポーリングは再作成されません。未対応の値は A に戻ります。B / C のキャンセル操作は閉じるアイコンと同じく表示の監視を停止し、サーバーのリクエストは拒否しません。確認成功時は自動で閉じます。閉じる操作のタッチ領域は 44px で、キーボードのフォーカス表示も維持します。
|
|
152
|
+
|
|
153
|
+
## コンポーネントの設定
|
|
154
|
+
|
|
155
|
+
| 設定 | 型 | 説明 |
|
|
156
|
+
| --- | --- | --- |
|
|
157
|
+
| `ticket` | `Ticket`、必須 | バックエンドで作成したリクエスト |
|
|
158
|
+
| `client` | `AuthLinkClient`、必須 | バックエンドの結果を取得するクライアント |
|
|
159
|
+
| `locale` | `Locale` | 既定は `zh-CN` |
|
|
160
|
+
| `variant` | `DialogVariant` | `A`(既定)、`B`、`C` |
|
|
161
|
+
| `showQr` | `boolean` | 確認画面にも QR を表示。登録画面では常に表示 |
|
|
162
|
+
| `intervalMs` | `number` | 250~10000 ミリ秒。既定は 1500 ミリ秒 |
|
|
163
|
+
| `onRefresh` | `() => void` | 期限切れ後、新規作成して ticket を更新する処理 |
|
|
164
|
+
|
|
165
|
+
| React コールバック / Vue イベント | 引数 | 意味 |
|
|
166
|
+
| --- | --- | --- |
|
|
167
|
+
| `onConfirmed` / `confirmed` | `Confirmation` | バックエンドによる検証と消費が完了 |
|
|
168
|
+
| `onRejected` / `rejected` | `Rejection` | 拒否時はダイアログを自動で閉じる |
|
|
169
|
+
| `onExpired` / `expired` | なし | 有効期限切れ |
|
|
170
|
+
| `onError` / `error` | `Error` | 通信・応答検証・コールバックのエラー |
|
|
171
|
+
|
|
172
|
+
拒否時は `{ status: 'rejected', code: 'REJECTED', requestId, purpose }` を返します。`REJECTED` は SDK の業務ステータスで、HTTP ステータスや承認証明ではありません。拒否・再試行画面は表示せず、`onClose` も通知しません。メッセージや新規作成はサイト側で処理してください。React / Vue では `onRejected` / `@rejected` で ticket を解除してコンポーネントを破棄します。ヘッドレス hook の `snapshot.status` は `rejected` のままです。
|
|
173
|
+
|
|
174
|
+
独自 UI には React の `useAuthLink(options)`、Vue の `useAuthLink(() => options)` を利用できます。返される値は `snapshot` と `retry` です。Vue の snapshot は shallow ref です。retry は既存リクエストを再開し、onRefresh はサイト側のコードで新規作成します。
|
|
175
|
+
|
|
176
|
+
## バックエンド API
|
|
177
|
+
|
|
178
|
+
既定の接頭辞は `/api/authlink` です。両 API でサイトのセッション、`X-CSRF-Token`、リクエストの所有者を検証してください。作成時は `Idempotency-Key` も送信されます。サイトのユーザー ID はセッションから決定します。
|
|
179
|
+
|
|
180
|
+
```ts
|
|
181
|
+
import type { Ticket, Result } from '@auth-link/web';
|
|
182
|
+
|
|
183
|
+
// POST /api/authlink/requests
|
|
184
|
+
// Body: { purpose: 'bind' | 'confirm' }
|
|
185
|
+
// Response: Ticket
|
|
186
|
+
|
|
187
|
+
// POST /api/authlink/requests/{requestId}/result
|
|
188
|
+
// Body: {}
|
|
189
|
+
// Response: Result
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
```json
|
|
193
|
+
{
|
|
194
|
+
"status": "confirmed",
|
|
195
|
+
"requestId": "<request-id>",
|
|
196
|
+
"verificationId": "<verification-id>",
|
|
197
|
+
"purpose": "confirm",
|
|
198
|
+
"authenticationPolicy": "device-presence.v1",
|
|
199
|
+
"expiresAt": "<same-expiry-as-ticket>"
|
|
200
|
+
}
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
作成 API は AuthLink の公開 ticket(`requestId`、`purpose`、`qr`、`pairingCode`、`expiresAt`、現在の `displayCode`)を返します。displayCode は `value`、`serverTime`、`validUntil`、`periodSeconds: 30` を持ちます。確認用 ticket には displayCode が必要です。
|
|
204
|
+
|
|
205
|
+
結果 API は未完了状態と最新の表示コードを返します。`new` / `pending` / `approved` / `consumed` だけでは成功イベントは発火しません。`confirmed` を返す前に、バックエンドは保存した state と消費用冪等キーで AuthLink の結果を消費し、完全な受領情報を検証して正しいセッションに記録します。requestId / verificationId による永続的な冪等処理が必要です。
|
|
206
|
+
|
|
207
|
+
## 対応言語
|
|
208
|
+
|
|
209
|
+
| Code | Language |
|
|
210
|
+
| --- | --- |
|
|
211
|
+
| `en` | English |
|
|
212
|
+
| `zh-CN` | 简体中文 (default) |
|
|
213
|
+
| `zh-TW` | 繁體中文 |
|
|
214
|
+
| `ja` | 日本語 |
|
|
215
|
+
| `bn` | বাংলা |
|
|
216
|
+
| `es` | Español |
|
|
217
|
+
| `fil` | Filipino |
|
|
218
|
+
| `hi` | हिन्दी |
|
|
219
|
+
| `id` | Bahasa Indonesia |
|
|
220
|
+
| `ms` | Bahasa Melayu |
|
|
221
|
+
| `pt` | Português |
|
|
222
|
+
| `th` | ไทย |
|
|
223
|
+
| `vi` | Tiếng Việt |
|
|
224
|
+
|
|
225
|
+
未指定・未対応の言語コードは簡体字中国語にフォールバックします。ルート要素には `lang` が設定されます。ルートエントリーの `supportedLocales` で一覧を取得できます。言語変更は表示文言だけに作用し、数字やリクエストは変更しません。ドキュメントは英語・中国語・日本語で提供します。
|
|
226
|
+
|
|
227
|
+
## スタイルとエントリー
|
|
228
|
+
|
|
229
|
+
`@auth-link/web/styles.css` を明示的に読み込みます。親要素で `--authlink-foreground`、`--authlink-background`、`--authlink-accent`、`--authlink-muted` を上書きできます。クラス名の接頭辞は `authlink-` です。
|
|
230
|
+
|
|
231
|
+
| エントリー | 内容 |
|
|
232
|
+
| --- | --- |
|
|
233
|
+
| `@auth-link/web` | クライアント、冪等キー、対応言語、型、基本 API |
|
|
234
|
+
| `@auth-link/web/react` | React コンポーネントと hook |
|
|
235
|
+
| `@auth-link/web/vue` | Vue コンポーネントと composable |
|
|
236
|
+
| `@auth-link/web/styles.css` | スタイル |
|
|
237
|
+
|
|
238
|
+
## セキュリティと有効期限
|
|
239
|
+
|
|
240
|
+
- 数字はアプリとの目視照合用です。数字だけで認証や承認はできません。
|
|
241
|
+
- ブラウザーに生成 seed、端末秘密鍵、テナント秘密情報を渡しません。
|
|
242
|
+
- リクエストは最大 120 秒。コード更新で期限は延長されません。サービスとアプリはモバイルプロトコル 4 に対応する必要があります。
|
|
243
|
+
- サーバー時刻と単調増加時計で残り時間を計算し、通信が止まっても期限切れの数字は隠します。更新境界で短い待ち時間が生じる場合があります。
|
|
244
|
+
- アンマウントや ticket 変更で監視を中止します。インスタンス内のイベント重複防止とは別に、バックエンドで永続的な冪等処理が必要です。
|
|
245
|
+
- フロントエンドのイベントは UI 更新用です。保護 API はバックエンドの信頼できる記録で判断します。
|
|
246
|
+
- QR はローカル生成し、バックエンドの SVG を挿入しません。CSP は `img-src 'self' data:` を許可してください。
|
|
247
|
+
|
|
248
|
+
## ライセンス
|
|
249
|
+
|
|
250
|
+
UNLICENSED。公開パッケージであってもオープンソース利用許諾は付与されません。同梱ライブラリの帰属表示は `THIRD_PARTY_NOTICES.md` を参照してください。
|