@islom929/react-eimzo 0.3.0 → 0.3.2

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.
Files changed (2) hide show
  1. package/README.md +373 -0
  2. package/package.json +1 -1
package/README.md ADDED
@@ -0,0 +1,373 @@
1
+ # @islom929/react-eimzo
2
+
3
+ Headless E-IMZO digital signature integration for React. No UI included — bring your own components.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install @islom929/react-eimzo
9
+ ```
10
+
11
+ No additional setup required. SDK is bundled and auto-injected.
12
+
13
+ ## Quick Start
14
+
15
+ ### 1. Wrap your app with EimzoProvider
16
+
17
+ ```tsx
18
+ import { EimzoProvider } from '@islom929/react-eimzo'
19
+
20
+ function App() {
21
+ return (
22
+ <EimzoProvider
23
+ apiKeys={[
24
+ 'yourdomain.uz', 'YOUR_PRODUCTION_API_KEY_HERE',
25
+ 'test.yourdomain.uz', 'YOUR_TEST_API_KEY_HERE',
26
+ ]}
27
+ >
28
+ <YourApp />
29
+ </EimzoProvider>
30
+ )
31
+ }
32
+ ```
33
+
34
+ Default keys for `localhost` and `127.0.0.1` are always included.
35
+
36
+ ### 2. Use the hook
37
+
38
+ ```tsx
39
+ import { useEimzo } from '@islom929/react-eimzo'
40
+ import type { ICertificate } from '@islom929/react-eimzo'
41
+
42
+ function SignDocument() {
43
+ const { sign, loadKeys, keyList, isInstalled, isLoading } = useEimzo()
44
+
45
+ const handleSign = (cert: ICertificate) => {
46
+ sign({
47
+ keyId: cert,
48
+ data: JSON.stringify({ document: 'content' }),
49
+ onSuccess: (pkcs7) => {
50
+ console.log('Signed:', pkcs7)
51
+ },
52
+ onError: (err) => {
53
+ console.error('Error:', err)
54
+ },
55
+ })
56
+ }
57
+
58
+ return (
59
+ <div>
60
+ <button onClick={loadKeys} disabled={!isInstalled || isLoading}>
61
+ Load keys
62
+ </button>
63
+
64
+ {keyList.map((cert) => (
65
+ <button key={cert.serialNumber} onClick={() => handleSign(cert)}>
66
+ {cert.CN}
67
+ </button>
68
+ ))}
69
+ </div>
70
+ )
71
+ }
72
+ ```
73
+
74
+ ## Usage Examples
75
+
76
+ ### Sign with PFX certificate
77
+
78
+ User selects a certificate from the list. E-IMZO app prompts for password.
79
+
80
+ ```tsx
81
+ import { useEimzo } from '@islom929/react-eimzo'
82
+ import type { ICertificate } from '@islom929/react-eimzo'
83
+
84
+ function PfxSign() {
85
+ const { sign, loadKeys, keyList, isInstalled, isLoading } = useEimzo()
86
+ const [result, setResult] = useState('')
87
+
88
+ useEffect(() => {
89
+ if (isInstalled) loadKeys()
90
+ }, [isInstalled])
91
+
92
+ const handleSign = (cert: ICertificate) => {
93
+ sign({
94
+ keyId: cert,
95
+ data: JSON.stringify({ orderId: 123, amount: 50000 }),
96
+ onSuccess: (pkcs7) => {
97
+ setResult(pkcs7)
98
+ // Send to backend
99
+ fetch('/api/verify', {
100
+ method: 'POST',
101
+ body: JSON.stringify({ pkcs7 }),
102
+ })
103
+ },
104
+ onError: (err) => {
105
+ alert(err) // "Ввод пароля отменен" if user cancels
106
+ },
107
+ })
108
+ }
109
+
110
+ return (
111
+ <div>
112
+ <h3>Select certificate:</h3>
113
+ {keyList.map((cert, i) => (
114
+ <div key={`${cert.serialNumber}-${i}`}>
115
+ <p>{cert.CN} — {cert.O}</p>
116
+ <p>PINFL: {cert.PINFL} | STIR: {cert.TIN}</p>
117
+ <p>Valid until: {new Date(cert.validTo).toLocaleDateString()}</p>
118
+ <button
119
+ onClick={() => handleSign(cert)}
120
+ disabled={cert.expired || isLoading}
121
+ >
122
+ {cert.expired ? 'Expired' : 'Sign'}
123
+ </button>
124
+ </div>
125
+ ))}
126
+ </div>
127
+ )
128
+ }
129
+ ```
130
+
131
+ ### Sign with tokens
132
+
133
+ No certificate selection needed. Pass device type directly.
134
+
135
+ ```tsx
136
+ function TokenSign() {
137
+ const { sign, deviceStatus, loadKeys, isInstalled, isLoading } = useEimzo()
138
+
139
+ useEffect(() => {
140
+ if (isInstalled) loadKeys()
141
+ }, [isInstalled])
142
+
143
+ const handleTokenSign = (device: 'idcard' | 'baikey' | 'ckc') => {
144
+ sign({
145
+ keyId: device,
146
+ data: JSON.stringify({ document: 'content' }),
147
+ onSuccess: (pkcs7) => console.log('Signed:', pkcs7),
148
+ onError: (err) => console.error(err),
149
+ })
150
+ }
151
+
152
+ return (
153
+ <div>
154
+ <button
155
+ onClick={() => handleTokenSign('idcard')}
156
+ disabled={!deviceStatus.idcard || isLoading}
157
+ >
158
+ ID Card {deviceStatus.idcard ? '(connected)' : '(not connected)'}
159
+ </button>
160
+
161
+ <button
162
+ onClick={() => handleTokenSign('baikey')}
163
+ disabled={!deviceStatus.baikey || isLoading}
164
+ >
165
+ BAIK Token {deviceStatus.baikey ? '(connected)' : '(not connected)'}
166
+ </button>
167
+
168
+ <button
169
+ onClick={() => handleTokenSign('ckc')}
170
+ disabled={!deviceStatus.ckc || isLoading}
171
+ >
172
+ CKC {deviceStatus.ckc ? '(connected)' : '(not connected)'}
173
+ </button>
174
+ </div>
175
+ )
176
+ }
177
+ ```
178
+
179
+ ### Form submission with signature
180
+
181
+ Sign form data before submitting to backend.
182
+
183
+ ```tsx
184
+ function ApplicationForm() {
185
+ const { sign, loadKeys, keyList, isInstalled } = useEimzo()
186
+ const [step, setStep] = useState<'form' | 'sign'>('form')
187
+ const [formData, setFormData] = useState({ name: '', amount: 0 })
188
+
189
+ const handleSubmit = (e: React.FormEvent) => {
190
+ e.preventDefault()
191
+ loadKeys()
192
+ setStep('sign')
193
+ }
194
+
195
+ const handleSign = (cert: ICertificate) => {
196
+ sign({
197
+ keyId: cert,
198
+ data: JSON.stringify(formData),
199
+ onSuccess: async (pkcs7) => {
200
+ await fetch('/api/applications', {
201
+ method: 'POST',
202
+ headers: { 'Content-Type': 'application/json' },
203
+ body: JSON.stringify({ ...formData, pkcs7 }),
204
+ })
205
+ alert('Application submitted!')
206
+ setStep('form')
207
+ },
208
+ onError: (err) => alert(`Signing failed: ${err}`),
209
+ })
210
+ }
211
+
212
+ if (step === 'form') {
213
+ return (
214
+ <form onSubmit={handleSubmit}>
215
+ <input
216
+ value={formData.name}
217
+ onChange={(e) => setFormData({ ...formData, name: e.target.value })}
218
+ placeholder="Name"
219
+ />
220
+ <input
221
+ type="number"
222
+ value={formData.amount}
223
+ onChange={(e) => setFormData({ ...formData, amount: +e.target.value })}
224
+ placeholder="Amount"
225
+ />
226
+ <button type="submit" disabled={!isInstalled}>
227
+ Sign & Submit
228
+ </button>
229
+ </form>
230
+ )
231
+ }
232
+
233
+ return (
234
+ <div>
235
+ <h3>Select certificate to sign:</h3>
236
+ {keyList.map((cert, i) => (
237
+ <button
238
+ key={`${cert.serialNumber}-${i}`}
239
+ onClick={() => handleSign(cert)}
240
+ disabled={cert.expired}
241
+ >
242
+ {cert.CN}
243
+ </button>
244
+ ))}
245
+ <button onClick={() => setStep('form')}>Back</button>
246
+ </div>
247
+ )
248
+ }
249
+ ```
250
+
251
+ ### Check E-IMZO installation
252
+
253
+ ```tsx
254
+ function EimzoStatus() {
255
+ const { isInstalled } = useEimzo()
256
+
257
+ if (!isInstalled) {
258
+ return (
259
+ <div>
260
+ <p>E-IMZO is not installed.</p>
261
+ <a href="https://e-imzo.uz/main/downloads/">Download E-IMZO</a>
262
+ </div>
263
+ )
264
+ }
265
+
266
+ return <p>E-IMZO is ready</p>
267
+ }
268
+ ```
269
+
270
+ ## API
271
+
272
+ ### EimzoProvider
273
+
274
+ Wraps your app. Initializes E-IMZO SDK automatically.
275
+
276
+ | Prop | Type | Description |
277
+ |------|------|-------------|
278
+ | `apiKeys` | `string[]` | Optional. Additional domain + API key pairs |
279
+ | `children` | `ReactNode` | Required |
280
+
281
+ ### useEimzo()
282
+
283
+ | Property | Type | Description |
284
+ |----------|------|-------------|
285
+ | `isInstalled` | `boolean` | E-IMZO app detected and running |
286
+ | `isLoading` | `boolean` | Operation in progress (loadKeys or sign) |
287
+ | `keyList` | `ICertificate[]` | Available certificates |
288
+ | `deviceStatus` | `IDeviceStatus` | Connected hardware devices |
289
+ | `loadKeys` | `() => Promise<void>` | Load certificates and check devices |
290
+ | `sign` | `(params: ISignParams) => void` | Sign data |
291
+
292
+ ### sign(params)
293
+
294
+ | Param | Type | Description |
295
+ |-------|------|-------------|
296
+ | `keyId` | `ICertificate \| string` | Certificate object or `'idcard'` / `'baikey'` / `'ckc'` |
297
+ | `data` | `string` | Data to sign (usually JSON.stringify) |
298
+ | `onSuccess` | `(pkcs7: string) => void` | Called with base64 PKCS#7 signature |
299
+ | `onError` | `(error: string) => void` | Optional. Called on failure |
300
+
301
+ ## Types
302
+
303
+ ```tsx
304
+ import type {
305
+ ICertificate,
306
+ ISignParams,
307
+ IDeviceStatus,
308
+ IEimzoContext,
309
+ IEimzoProviderProps,
310
+ TKeyType,
311
+ } from '@islom929/react-eimzo'
312
+ ```
313
+
314
+ ### ICertificate
315
+
316
+ | Field | Type | Description |
317
+ |-------|------|-------------|
318
+ | `CN` | `string` | Full name |
319
+ | `PINFL` | `string` | Personal ID number |
320
+ | `TIN` | `string` | Tax ID (STIR) |
321
+ | `O` | `string` | Organization |
322
+ | `T` | `string` | Title/Position |
323
+ | `UID` | `string` | User ID |
324
+ | `serialNumber` | `string` | Certificate serial number |
325
+ | `validFrom` | `Date` | Start of validity |
326
+ | `validTo` | `Date` | End of validity |
327
+ | `type` | `'pfx' \| 'ftjc'` | Certificate type |
328
+ | `expired` | `boolean` | Whether certificate has expired |
329
+
330
+ ### IDeviceStatus
331
+
332
+ | Field | Type | Description |
333
+ |-------|------|-------------|
334
+ | `idcard` | `boolean` | ID card / EIMZO-Token connected |
335
+ | `baikey` | `boolean` | BAIK-Token connected |
336
+ | `ckc` | `boolean` | CKC device connected |
337
+
338
+ ## Supported Key Types
339
+
340
+ | Type | Description | E-IMZO Version |
341
+ |------|-------------|----------------|
342
+ | PFX | Local certificate file (ERI) | v3.36+ |
343
+ | ID-card / EIMZO-Token | Physical smart card | v4.12+ |
344
+ | BAIK-Token | BAIK hardware token | v4.86+ |
345
+ | CKC | CryptKeyContainer (universal) | v4.86+ |
346
+
347
+ ## How It Works
348
+
349
+ ```
350
+ Your React App
351
+ ↓ useEimzo()
352
+ @islom929/react-eimzo
353
+ ↓ WebSocket (wss://127.0.0.1:64443)
354
+ E-IMZO Desktop App
355
+
356
+ PFX files / USB tokens / ID cards
357
+ ```
358
+
359
+ 1. Package injects E-IMZO SDK into the page automatically
360
+ 2. SDK connects to E-IMZO desktop app via WebSocket
361
+ 3. `loadKeys()` fetches available certificates
362
+ 4. `sign()` sends data to E-IMZO app for signing
363
+ 5. E-IMZO app prompts user for password/PIN
364
+ 6. Signed PKCS#7 (base64) returned via `onSuccess`
365
+
366
+ ## Requirements
367
+
368
+ - React 18+
369
+ - [E-IMZO desktop application](https://e-imzo.uz/main/downloads/) installed on user's computer
370
+
371
+ ## License
372
+
373
+ MIT
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@islom929/react-eimzo",
3
- "version": "0.3.0",
3
+ "version": "0.3.2",
4
4
  "description": "Headless E-IMZO digital signature integration for React",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",