@bsv/sdk 1.9.3 → 1.9.4
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/dist/cjs/package.json +1 -1
- package/docs/fast-docs.png +0 -0
- package/docs/index.md +49 -44
- package/docs/swagger.png +0 -0
- package/package.json +1 -1
- package/docs/MARKDOWN_VALIDATION_GUIDE.md +0 -175
- package/docs/concepts/beef.md +0 -92
- package/docs/concepts/chain-tracking.md +0 -134
- package/docs/concepts/decentralized-identity.md +0 -221
- package/docs/concepts/fees.md +0 -249
- package/docs/concepts/identity-certificates.md +0 -307
- package/docs/concepts/index.md +0 -77
- package/docs/concepts/key-management.md +0 -185
- package/docs/concepts/script-templates.md +0 -176
- package/docs/concepts/sdk-philosophy.md +0 -80
- package/docs/concepts/signatures.md +0 -194
- package/docs/concepts/spv-verification.md +0 -118
- package/docs/concepts/transaction-encoding.md +0 -167
- package/docs/concepts/transaction-structure.md +0 -67
- package/docs/concepts/trust-model.md +0 -139
- package/docs/concepts/verification.md +0 -250
- package/docs/concepts/wallet-integration.md +0 -101
- package/docs/guides/development-wallet-setup.md +0 -374
- package/docs/guides/direct-transaction-creation.md +0 -147
- package/docs/guides/http-client-configuration.md +0 -488
- package/docs/guides/index.md +0 -138
- package/docs/guides/large-transactions.md +0 -448
- package/docs/guides/multisig-transactions.md +0 -792
- package/docs/guides/security-best-practices.md +0 -494
- package/docs/guides/transaction-batching.md +0 -132
- package/docs/guides/transaction-signing-methods.md +0 -419
- package/docs/reference/arc-config.md +0 -698
- package/docs/reference/brc-100.md +0 -33
- package/docs/reference/configuration.md +0 -835
- package/docs/reference/debugging.md +0 -705
- package/docs/reference/errors.md +0 -597
- package/docs/reference/index.md +0 -111
- package/docs/reference/network-config.md +0 -914
- package/docs/reference/op-codes.md +0 -325
- package/docs/reference/transaction-signatures.md +0 -95
- package/docs/tutorials/advanced-transaction.md +0 -572
- package/docs/tutorials/aes-encryption.md +0 -949
- package/docs/tutorials/authfetch-tutorial.md +0 -986
- package/docs/tutorials/ecdh-key-exchange.md +0 -549
- package/docs/tutorials/elliptic-curve-fundamentals.md +0 -606
- package/docs/tutorials/error-handling.md +0 -1216
- package/docs/tutorials/first-transaction-low-level.md +0 -205
- package/docs/tutorials/first-transaction.md +0 -275
- package/docs/tutorials/hashes-and-hmacs.md +0 -788
- package/docs/tutorials/identity-management.md +0 -729
- package/docs/tutorials/index.md +0 -219
- package/docs/tutorials/key-management.md +0 -538
- package/docs/tutorials/protowallet-development.md +0 -743
- package/docs/tutorials/script-construction.md +0 -690
- package/docs/tutorials/spv-merkle-proofs.md +0 -685
- package/docs/tutorials/testnet-transactions-low-level.md +0 -359
- package/docs/tutorials/transaction-broadcasting.md +0 -538
- package/docs/tutorials/transaction-types.md +0 -420
- package/docs/tutorials/type-42.md +0 -568
- package/docs/tutorials/uhrp-storage.md +0 -599
|
@@ -1,986 +0,0 @@
|
|
|
1
|
-
# Authenticated HTTP Requests with AuthFetch
|
|
2
|
-
|
|
3
|
-
**Duration**: 60 minutes
|
|
4
|
-
**Prerequisites**: Node.js, basic TypeScript knowledge, understanding of HTTP and authentication
|
|
5
|
-
**Learning Goals**:
|
|
6
|
-
|
|
7
|
-
- Understand BRC-103/104 authentication protocols
|
|
8
|
-
- Implement authenticated HTTP requests with AuthFetch
|
|
9
|
-
- Handle peer-to-peer authentication and certificate exchange
|
|
10
|
-
- Build secure API communication systems
|
|
11
|
-
|
|
12
|
-
## When to Use AuthFetch
|
|
13
|
-
|
|
14
|
-
**Use AuthFetch when you need:**
|
|
15
|
-
|
|
16
|
-
- BRC-103/104 cryptographic authentication
|
|
17
|
-
- Wallet-signed HTTP requests for identity verification
|
|
18
|
-
- Certificate-based peer authentication
|
|
19
|
-
- Secure peer-to-peer communication between BSV applications
|
|
20
|
-
- APIs that require cryptographic proof of identity
|
|
21
|
-
|
|
22
|
-
**For general HTTP client configuration, use [HTTP Client Configuration Guide](../guides/http-client-configuration.md) instead:**
|
|
23
|
-
|
|
24
|
-
- Custom HTTP client setup (Axios, fetch, etc.)
|
|
25
|
-
- Transaction broadcasting via ARC endpoints
|
|
26
|
-
- Environment-specific configuration (timeouts, retries)
|
|
27
|
-
- Testing and mocking HTTP clients
|
|
28
|
-
- Integration with existing HTTP infrastructure
|
|
29
|
-
|
|
30
|
-
## Introduction
|
|
31
|
-
|
|
32
|
-
AuthFetch is a specialized HTTP client that implements BRC-103 and BRC-104 authentication protocols for secure peer-to-peer communication in the BSV ecosystem. Unlike traditional API authentication (like JWT tokens), AuthFetch uses cryptographic signatures and certificate-based authentication.
|
|
33
|
-
|
|
34
|
-
## Key Features
|
|
35
|
-
|
|
36
|
-
- **BRC-103 Authentication**: Cryptographic request signing
|
|
37
|
-
- **BRC-104 Certificate Exchange**: Peer identity verification
|
|
38
|
-
- **Automatic Session Management**: Handles authentication state
|
|
39
|
-
- **Certificate Validation**: Verifies peer credentials
|
|
40
|
-
- **Secure Communication**: End-to-end authenticated requests
|
|
41
|
-
|
|
42
|
-
## What You'll Build
|
|
43
|
-
|
|
44
|
-
In this tutorial, you'll create:
|
|
45
|
-
|
|
46
|
-
- Basic authenticated HTTP client
|
|
47
|
-
- Peer-to-peer communication system
|
|
48
|
-
- Certificate exchange mechanism
|
|
49
|
-
- Secure API integration
|
|
50
|
-
|
|
51
|
-
## Setting Up AuthFetch with `WalletClient`
|
|
52
|
-
|
|
53
|
-
### Basic AuthFetch Configuration
|
|
54
|
-
|
|
55
|
-
```typescript
|
|
56
|
-
import { AuthFetch, WalletClient } from '@bsv/sdk'
|
|
57
|
-
|
|
58
|
-
async function createAuthFetch() {
|
|
59
|
-
// Create wallet for authentication - connects to local wallet (e.g., MetaNet Desktop)
|
|
60
|
-
const wallet = new WalletClient('auto', 'localhost')
|
|
61
|
-
|
|
62
|
-
// Check if wallet is connected
|
|
63
|
-
try {
|
|
64
|
-
const authStatus = await wallet.isAuthenticated()
|
|
65
|
-
console.log('Wallet authenticated:', authStatus.authenticated)
|
|
66
|
-
|
|
67
|
-
const network = await wallet.getNetwork()
|
|
68
|
-
console.log('Connected to network:', network.network)
|
|
69
|
-
} catch (error) {
|
|
70
|
-
console.log('Wallet connection status:', error.message)
|
|
71
|
-
// This is expected if no wallet is running
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
// Create AuthFetch instance
|
|
75
|
-
const authFetch = new AuthFetch(wallet)
|
|
76
|
-
|
|
77
|
-
console.log('AuthFetch client created')
|
|
78
|
-
return authFetch
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
async function basicAuthenticatedRequest() {
|
|
82
|
-
const authFetch = await createAuthFetch()
|
|
83
|
-
|
|
84
|
-
try {
|
|
85
|
-
// Make authenticated request to a real, working endpoint
|
|
86
|
-
const response = await authFetch.fetch('https://httpbin.org/get', {
|
|
87
|
-
method: 'GET',
|
|
88
|
-
headers: {
|
|
89
|
-
'Content-Type': 'application/json',
|
|
90
|
-
'X-BSV-Tutorial': 'AuthFetch-Example'
|
|
91
|
-
}
|
|
92
|
-
})
|
|
93
|
-
|
|
94
|
-
if (response.ok) {
|
|
95
|
-
const data = await response.json()
|
|
96
|
-
console.log('Authenticated request successful!')
|
|
97
|
-
console.log('Request URL:', data.url)
|
|
98
|
-
console.log('Headers sent:', Object.keys(data.headers).length)
|
|
99
|
-
return data
|
|
100
|
-
} else {
|
|
101
|
-
console.error('Request failed:', response.status, response.statusText)
|
|
102
|
-
}
|
|
103
|
-
} catch (error) {
|
|
104
|
-
console.error('Authentication error:', error.message)
|
|
105
|
-
if (error.message.includes('No wallet available')) {
|
|
106
|
-
console.log(' Install and run MetaNet Desktop Wallet to test with real authentication')
|
|
107
|
-
console.log(' For now, this demonstrates the AuthFetch API structure')
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
// Test the basic functionality
|
|
113
|
-
basicAuthenticatedRequest().catch(console.error)
|
|
114
|
-
```
|
|
115
|
-
|
|
116
|
-
### AuthFetch with Certificate Requirements
|
|
117
|
-
|
|
118
|
-
```typescript
|
|
119
|
-
import { AuthFetch, WalletClient } from '@bsv/sdk'
|
|
120
|
-
|
|
121
|
-
async function createAuthFetchWithCertificates() {
|
|
122
|
-
const wallet = new WalletClient('auto', 'localhost')
|
|
123
|
-
|
|
124
|
-
// Define required certificates from peers
|
|
125
|
-
const requestedCertificates = {
|
|
126
|
-
certifiers: {
|
|
127
|
-
// Require identity certificates from trusted certifier
|
|
128
|
-
'identity-certifier-key': {
|
|
129
|
-
certificateTypes: ['identity-cert'],
|
|
130
|
-
fieldsRequired: ['name', 'email']
|
|
131
|
-
}
|
|
132
|
-
},
|
|
133
|
-
acquisitionProtocol: 'direct' as const
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
const authFetch = new AuthFetch(wallet, requestedCertificates)
|
|
137
|
-
|
|
138
|
-
console.log('AuthFetch with certificate requirements created')
|
|
139
|
-
return authFetch
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
async function testCertificateRequirements() {
|
|
143
|
-
const authFetch = await createAuthFetchWithCertificates()
|
|
144
|
-
|
|
145
|
-
try {
|
|
146
|
-
// Test with a real endpoint that will show our certificate headers (using a dummy URL for demo purposes)
|
|
147
|
-
const response = await authFetch.fetch('https://httpbin.org/headers', {
|
|
148
|
-
method: 'GET'
|
|
149
|
-
})
|
|
150
|
-
|
|
151
|
-
if (response.ok) {
|
|
152
|
-
const data = await response.json()
|
|
153
|
-
console.log('Certificate-enabled request successful!')
|
|
154
|
-
console.log('Headers sent to server:', data.headers)
|
|
155
|
-
|
|
156
|
-
// AuthFetch will include certificate-related headers when available
|
|
157
|
-
const certHeaders = Object.keys(data.headers).filter(h =>
|
|
158
|
-
h.toLowerCase().includes('cert') || h.toLowerCase().includes('auth')
|
|
159
|
-
)
|
|
160
|
-
console.log('Certificate/Auth headers:', certHeaders)
|
|
161
|
-
|
|
162
|
-
} else {
|
|
163
|
-
console.error('Request failed:', response.status)
|
|
164
|
-
}
|
|
165
|
-
} catch (error) {
|
|
166
|
-
console.error('Certificate request error:', error.message)
|
|
167
|
-
if (error.message.includes('No wallet available')) {
|
|
168
|
-
console.log(' Certificate exchange requires a connected wallet')
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
testCertificateRequirements().catch(console.error)
|
|
174
|
-
```
|
|
175
|
-
|
|
176
|
-
## Certificate Exchange and Verification
|
|
177
|
-
|
|
178
|
-
### Requesting Certificates from Peers
|
|
179
|
-
|
|
180
|
-
```typescript
|
|
181
|
-
import { AuthFetch, WalletClient } from '@bsv/sdk'
|
|
182
|
-
|
|
183
|
-
class CertificateManager {
|
|
184
|
-
private authFetch: AuthFetch
|
|
185
|
-
|
|
186
|
-
constructor(wallet: WalletClient) {
|
|
187
|
-
this.authFetch = new AuthFetch(wallet)
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
async requestPeerCertificates(
|
|
191
|
-
peerBaseUrl: string,
|
|
192
|
-
certificateRequirements: any
|
|
193
|
-
): Promise<any[]> {
|
|
194
|
-
try {
|
|
195
|
-
console.log('Requesting certificates from peer:', peerBaseUrl)
|
|
196
|
-
|
|
197
|
-
const certificates = await this.authFetch.sendCertificateRequest(
|
|
198
|
-
peerBaseUrl,
|
|
199
|
-
certificateRequirements
|
|
200
|
-
)
|
|
201
|
-
|
|
202
|
-
console.log('Received certificates:', certificates.length)
|
|
203
|
-
return certificates
|
|
204
|
-
} catch (error) {
|
|
205
|
-
console.error('Certificate request failed:', error)
|
|
206
|
-
throw error
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
async verifyPeerIdentity(peerUrl: string): Promise<{
|
|
211
|
-
verified: boolean
|
|
212
|
-
identity: string | null
|
|
213
|
-
certificates: any[]
|
|
214
|
-
}> {
|
|
215
|
-
const certificateRequirements = {
|
|
216
|
-
certifiers: {
|
|
217
|
-
'trusted-identity-provider': {
|
|
218
|
-
certificateTypes: ['identity'],
|
|
219
|
-
fieldsRequired: ['name']
|
|
220
|
-
}
|
|
221
|
-
},
|
|
222
|
-
acquisitionProtocol: 'direct' as const
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
try {
|
|
226
|
-
const certificates = await this.requestPeerCertificates(
|
|
227
|
-
peerUrl,
|
|
228
|
-
certificateRequirements
|
|
229
|
-
)
|
|
230
|
-
|
|
231
|
-
// Verify certificates (simplified verification)
|
|
232
|
-
const verified = certificates.length > 0
|
|
233
|
-
const identity = verified ? certificates[0].subject : null
|
|
234
|
-
|
|
235
|
-
return { verified, identity, certificates }
|
|
236
|
-
} catch (error) {
|
|
237
|
-
console.error('Identity verification failed:', error)
|
|
238
|
-
return { verified: false, identity: null, certificates: [] }
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
async function demonstrateCertificateExchange() {
|
|
244
|
-
const wallet = new WalletClient('auto', 'localhost')
|
|
245
|
-
const certManager = new CertificateManager(wallet)
|
|
246
|
-
|
|
247
|
-
// Example peer URLs (replace with actual peer endpoints)
|
|
248
|
-
const peerUrls = [
|
|
249
|
-
'https://peer1.example.com',
|
|
250
|
-
'https://peer2.example.com'
|
|
251
|
-
]
|
|
252
|
-
|
|
253
|
-
for (const peerUrl of peerUrls) {
|
|
254
|
-
console.log(`\n=== Verifying peer: ${peerUrl} ===`)
|
|
255
|
-
|
|
256
|
-
try {
|
|
257
|
-
const verification = await certManager.verifyPeerIdentity(peerUrl)
|
|
258
|
-
|
|
259
|
-
if (verification.verified) {
|
|
260
|
-
console.log(' Peer verified successfully')
|
|
261
|
-
console.log('Identity:', verification.identity)
|
|
262
|
-
console.log('Certificates received:', verification.certificates.length)
|
|
263
|
-
} else {
|
|
264
|
-
console.log(' Peer verification failed')
|
|
265
|
-
}
|
|
266
|
-
} catch (error) {
|
|
267
|
-
console.log(' Peer unreachable or invalid')
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
demonstrateCertificateExchange().catch(console.error)
|
|
273
|
-
```
|
|
274
|
-
|
|
275
|
-
## Building Secure API Clients
|
|
276
|
-
|
|
277
|
-
### Authenticated API Client
|
|
278
|
-
|
|
279
|
-
```typescript
|
|
280
|
-
import { AuthFetch, WalletClient } from '@bsv/sdk'
|
|
281
|
-
|
|
282
|
-
class SecureAPIClient {
|
|
283
|
-
private authFetch: AuthFetch
|
|
284
|
-
private baseUrl: string
|
|
285
|
-
|
|
286
|
-
constructor(baseUrl: string, wallet?: WalletClient) {
|
|
287
|
-
this.baseUrl = baseUrl
|
|
288
|
-
this.authFetch = new AuthFetch(wallet || new WalletClient('auto', 'localhost'))
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
async get(endpoint: string, options: any = {}): Promise<any> {
|
|
292
|
-
return this.request('GET', endpoint, null, options)
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
async post(endpoint: string, data: any, options: any = {}): Promise<any> {
|
|
296
|
-
return this.request('POST', endpoint, data, options)
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
async put(endpoint: string, data: any, options: any = {}): Promise<any> {
|
|
300
|
-
return this.request('PUT', endpoint, data, options)
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
async delete(endpoint: string, options: any = {}): Promise<any> {
|
|
304
|
-
return this.request('DELETE', endpoint, null, options)
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
private async request(
|
|
308
|
-
method: string,
|
|
309
|
-
endpoint: string,
|
|
310
|
-
data: any = null,
|
|
311
|
-
options: any = {}
|
|
312
|
-
): Promise<any> {
|
|
313
|
-
const url = `${this.baseUrl}${endpoint}`
|
|
314
|
-
|
|
315
|
-
const requestOptions: any = {
|
|
316
|
-
method,
|
|
317
|
-
headers: {
|
|
318
|
-
'Content-Type': 'application/json',
|
|
319
|
-
...options.headers
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
if (data) {
|
|
324
|
-
requestOptions.body = JSON.stringify(data)
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
try {
|
|
328
|
-
console.log(`Making authenticated ${method} request to ${endpoint}`)
|
|
329
|
-
|
|
330
|
-
const response = await this.authFetch.fetch(url, requestOptions)
|
|
331
|
-
|
|
332
|
-
if (!response.ok) {
|
|
333
|
-
throw new Error(`HTTP ${response.status}: ${response.statusText}`)
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
const contentType = response.headers.get('content-type')
|
|
337
|
-
if (contentType && contentType.includes('application/json')) {
|
|
338
|
-
return await response.json()
|
|
339
|
-
} else {
|
|
340
|
-
return await response.text()
|
|
341
|
-
}
|
|
342
|
-
} catch (error) {
|
|
343
|
-
console.error(`Request failed for ${method} ${endpoint}:`, error)
|
|
344
|
-
throw error
|
|
345
|
-
}
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
async healthCheck(): Promise<boolean> {
|
|
349
|
-
try {
|
|
350
|
-
await this.get('/health')
|
|
351
|
-
return true
|
|
352
|
-
} catch (error) {
|
|
353
|
-
return false
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
async function demonstrateSecureAPIClient() {
|
|
359
|
-
// Create secure API client using real, testable endpoints
|
|
360
|
-
const apiClient = new SecureAPIClient('https://httpbin.org')
|
|
361
|
-
|
|
362
|
-
try {
|
|
363
|
-
// Health check using a real endpoint
|
|
364
|
-
console.log('Testing API client with real endpoints...')
|
|
365
|
-
|
|
366
|
-
// Test GET request
|
|
367
|
-
const getResult = await apiClient.get('/get?test=true&client=secure')
|
|
368
|
-
console.log('✅ GET request successful')
|
|
369
|
-
console.log('Request URL:', getResult.url)
|
|
370
|
-
console.log('Query parameters received:', getResult.args)
|
|
371
|
-
|
|
372
|
-
// Test POST request with data
|
|
373
|
-
const postResult = await apiClient.post('/post', {
|
|
374
|
-
user: 'demo-user',
|
|
375
|
-
action: 'test-post',
|
|
376
|
-
timestamp: new Date().toISOString(),
|
|
377
|
-
authenticated: true
|
|
378
|
-
})
|
|
379
|
-
console.log('✅ POST request successful')
|
|
380
|
-
console.log('Data sent:', postResult.json)
|
|
381
|
-
console.log('Content-Type:', postResult.headers['Content-Type'])
|
|
382
|
-
|
|
383
|
-
// Test PUT request
|
|
384
|
-
const putResult = await apiClient.put('/put', {
|
|
385
|
-
resource: 'user-settings',
|
|
386
|
-
theme: 'dark',
|
|
387
|
-
notifications: true,
|
|
388
|
-
updated: new Date().toISOString()
|
|
389
|
-
})
|
|
390
|
-
console.log('✅ PUT request successful')
|
|
391
|
-
console.log('PUT data received:', putResult.json)
|
|
392
|
-
|
|
393
|
-
// Test DELETE request
|
|
394
|
-
const deleteResult = await apiClient.delete('/delete')
|
|
395
|
-
console.log('✅ DELETE request successful')
|
|
396
|
-
console.log('DELETE method confirmed:', deleteResult.url)
|
|
397
|
-
|
|
398
|
-
// Test custom headers
|
|
399
|
-
const headersResult = await apiClient.get('/headers')
|
|
400
|
-
console.log('✅ Headers test successful')
|
|
401
|
-
console.log('Custom headers sent:', Object.keys(headersResult.headers).length)
|
|
402
|
-
|
|
403
|
-
return {
|
|
404
|
-
get: getResult,
|
|
405
|
-
post: postResult,
|
|
406
|
-
put: putResult,
|
|
407
|
-
delete: deleteResult,
|
|
408
|
-
headers: headersResult
|
|
409
|
-
}
|
|
410
|
-
} catch (error) {
|
|
411
|
-
console.error('API operations failed:', error.message)
|
|
412
|
-
if (error.message.includes('No wallet available')) {
|
|
413
|
-
console.log('💡 Install MetaNet Desktop Wallet to test with real authentication')
|
|
414
|
-
console.log(' The API calls work, but authentication requires a connected wallet')
|
|
415
|
-
}
|
|
416
|
-
}
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
demonstrateSecureAPIClient().catch(console.error)
|
|
420
|
-
```
|
|
421
|
-
|
|
422
|
-
### Multi-Peer Communication System
|
|
423
|
-
|
|
424
|
-
```typescript
|
|
425
|
-
import { AuthFetch, WalletClient } from '@bsv/sdk'
|
|
426
|
-
|
|
427
|
-
interface PeerInfo {
|
|
428
|
-
url: string
|
|
429
|
-
identity: string | null
|
|
430
|
-
verified: boolean
|
|
431
|
-
lastContact: Date
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
class PeerNetwork {
|
|
435
|
-
private authFetch: AuthFetch
|
|
436
|
-
private peers: Map<string, PeerInfo> = new Map()
|
|
437
|
-
|
|
438
|
-
constructor(wallet?: WalletClient) {
|
|
439
|
-
this.authFetch = new AuthFetch(wallet || new WalletClient('auto', 'localhost'))
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
async addPeer(peerUrl: string): Promise<boolean> {
|
|
443
|
-
try {
|
|
444
|
-
console.log(`Adding peer: ${peerUrl}`)
|
|
445
|
-
|
|
446
|
-
// Verify peer identity
|
|
447
|
-
const verification = await this.verifyPeer(peerUrl)
|
|
448
|
-
|
|
449
|
-
const peerInfo: PeerInfo = {
|
|
450
|
-
url: peerUrl,
|
|
451
|
-
identity: verification.identity,
|
|
452
|
-
verified: verification.verified,
|
|
453
|
-
lastContact: new Date()
|
|
454
|
-
}
|
|
455
|
-
|
|
456
|
-
this.peers.set(peerUrl, peerInfo)
|
|
457
|
-
|
|
458
|
-
console.log(`Peer ${peerUrl} ${verification.verified ? 'verified' : 'unverified'}`)
|
|
459
|
-
return verification.verified
|
|
460
|
-
} catch (error) {
|
|
461
|
-
console.error(`Failed to add peer ${peerUrl}:`, error)
|
|
462
|
-
return false
|
|
463
|
-
}
|
|
464
|
-
}
|
|
465
|
-
|
|
466
|
-
private async verifyPeer(peerUrl: string): Promise<{
|
|
467
|
-
verified: boolean
|
|
468
|
-
identity: string | null
|
|
469
|
-
}> {
|
|
470
|
-
try {
|
|
471
|
-
// Simple ping to verify peer is reachable
|
|
472
|
-
const response = await this.authFetch.fetch(`${peerUrl}/ping`, {
|
|
473
|
-
method: 'GET'
|
|
474
|
-
})
|
|
475
|
-
|
|
476
|
-
if (response.ok) {
|
|
477
|
-
// In a real implementation, you would verify certificates here
|
|
478
|
-
return { verified: true, identity: 'peer-identity' }
|
|
479
|
-
} else {
|
|
480
|
-
return { verified: false, identity: null }
|
|
481
|
-
}
|
|
482
|
-
} catch (error) {
|
|
483
|
-
return { verified: false, identity: null }
|
|
484
|
-
}
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
async broadcastMessage(message: any): Promise<{
|
|
488
|
-
successful: string[]
|
|
489
|
-
failed: string[]
|
|
490
|
-
}> {
|
|
491
|
-
const successful: string[] = []
|
|
492
|
-
const failed: string[] = []
|
|
493
|
-
|
|
494
|
-
console.log(`Broadcasting message to ${this.peers.size} peers`)
|
|
495
|
-
|
|
496
|
-
const promises = Array.from(this.peers.entries()).map(async ([url, peerInfo]) => {
|
|
497
|
-
try {
|
|
498
|
-
const response = await this.authFetch.fetch(`${url}/message`, {
|
|
499
|
-
method: 'POST',
|
|
500
|
-
headers: {
|
|
501
|
-
'Content-Type': 'application/json'
|
|
502
|
-
},
|
|
503
|
-
body: JSON.stringify(message)
|
|
504
|
-
})
|
|
505
|
-
|
|
506
|
-
if (response.ok) {
|
|
507
|
-
successful.push(url)
|
|
508
|
-
// Update last contact
|
|
509
|
-
peerInfo.lastContact = new Date()
|
|
510
|
-
} else {
|
|
511
|
-
failed.push(url)
|
|
512
|
-
}
|
|
513
|
-
} catch (error) {
|
|
514
|
-
failed.push(url)
|
|
515
|
-
console.error(`Failed to send message to ${url}:`, error)
|
|
516
|
-
}
|
|
517
|
-
})
|
|
518
|
-
|
|
519
|
-
await Promise.all(promises)
|
|
520
|
-
|
|
521
|
-
console.log(`Broadcast complete: ${successful.length} successful, ${failed.length} failed`)
|
|
522
|
-
return { successful, failed }
|
|
523
|
-
}
|
|
524
|
-
|
|
525
|
-
async sendDirectMessage(peerUrl: string, message: any): Promise<any> {
|
|
526
|
-
const peer = this.peers.get(peerUrl)
|
|
527
|
-
if (!peer) {
|
|
528
|
-
throw new Error(`Peer ${peerUrl} not found`)
|
|
529
|
-
}
|
|
530
|
-
|
|
531
|
-
if (!peer.verified) {
|
|
532
|
-
throw new Error(`Peer ${peerUrl} not verified`)
|
|
533
|
-
}
|
|
534
|
-
|
|
535
|
-
try {
|
|
536
|
-
const response = await this.authFetch.fetch(`${peerUrl}/direct-message`, {
|
|
537
|
-
method: 'POST',
|
|
538
|
-
headers: {
|
|
539
|
-
'Content-Type': 'application/json'
|
|
540
|
-
},
|
|
541
|
-
body: JSON.stringify(message)
|
|
542
|
-
})
|
|
543
|
-
|
|
544
|
-
if (response.ok) {
|
|
545
|
-
peer.lastContact = new Date()
|
|
546
|
-
return await response.json()
|
|
547
|
-
} else {
|
|
548
|
-
throw new Error(`HTTP ${response.status}: ${response.statusText}`)
|
|
549
|
-
}
|
|
550
|
-
} catch (error) {
|
|
551
|
-
console.error(`Direct message to ${peerUrl} failed:`, error)
|
|
552
|
-
throw error
|
|
553
|
-
}
|
|
554
|
-
}
|
|
555
|
-
|
|
556
|
-
getPeerStatus(): { total: number; verified: number; unverified: number } {
|
|
557
|
-
const total = this.peers.size
|
|
558
|
-
let verified = 0
|
|
559
|
-
let unverified = 0
|
|
560
|
-
|
|
561
|
-
for (const peer of this.peers.values()) {
|
|
562
|
-
if (peer.verified) {
|
|
563
|
-
verified++
|
|
564
|
-
} else {
|
|
565
|
-
unverified++
|
|
566
|
-
}
|
|
567
|
-
}
|
|
568
|
-
|
|
569
|
-
return { total, verified, unverified }
|
|
570
|
-
}
|
|
571
|
-
|
|
572
|
-
listPeers(): PeerInfo[] {
|
|
573
|
-
return Array.from(this.peers.values())
|
|
574
|
-
}
|
|
575
|
-
}
|
|
576
|
-
|
|
577
|
-
async function demonstratePeerNetwork() {
|
|
578
|
-
const network = new PeerNetwork()
|
|
579
|
-
|
|
580
|
-
// Add peers (replace with actual peer URLs)
|
|
581
|
-
const peerUrls = [
|
|
582
|
-
'https://peer1.example.com',
|
|
583
|
-
'https://peer2.example.com',
|
|
584
|
-
'https://peer3.example.com'
|
|
585
|
-
]
|
|
586
|
-
|
|
587
|
-
console.log('=== Setting up peer network ===')
|
|
588
|
-
|
|
589
|
-
for (const peerUrl of peerUrls) {
|
|
590
|
-
await network.addPeer(peerUrl)
|
|
591
|
-
}
|
|
592
|
-
|
|
593
|
-
const status = network.getPeerStatus()
|
|
594
|
-
console.log('Network status:', status)
|
|
595
|
-
|
|
596
|
-
// Broadcast message
|
|
597
|
-
const broadcastMessage = {
|
|
598
|
-
type: 'announcement',
|
|
599
|
-
content: 'Hello from authenticated peer network!',
|
|
600
|
-
timestamp: new Date().toISOString()
|
|
601
|
-
}
|
|
602
|
-
|
|
603
|
-
console.log('\n=== Broadcasting message ===')
|
|
604
|
-
const broadcastResult = await network.broadcastMessage(broadcastMessage)
|
|
605
|
-
console.log('Broadcast result:', broadcastResult)
|
|
606
|
-
|
|
607
|
-
// Send direct message to first verified peer
|
|
608
|
-
const peers = network.listPeers()
|
|
609
|
-
const verifiedPeer = peers.find(p => p.verified)
|
|
610
|
-
|
|
611
|
-
if (verifiedPeer) {
|
|
612
|
-
console.log('\n=== Sending direct message ===')
|
|
613
|
-
try {
|
|
614
|
-
const directMessage = {
|
|
615
|
-
type: 'direct',
|
|
616
|
-
content: 'This is a direct authenticated message',
|
|
617
|
-
timestamp: new Date().toISOString()
|
|
618
|
-
}
|
|
619
|
-
|
|
620
|
-
const response = await network.sendDirectMessage(verifiedPeer.url, directMessage)
|
|
621
|
-
console.log('Direct message response:', response)
|
|
622
|
-
} catch (error) {
|
|
623
|
-
console.log('Direct message failed (expected in demo)')
|
|
624
|
-
}
|
|
625
|
-
}
|
|
626
|
-
|
|
627
|
-
return { status, broadcastResult, peers }
|
|
628
|
-
}
|
|
629
|
-
|
|
630
|
-
demonstratePeerNetwork().catch(console.error)
|
|
631
|
-
```
|
|
632
|
-
|
|
633
|
-
## Advanced Authentication Patterns
|
|
634
|
-
|
|
635
|
-
### Session Management and Reconnection
|
|
636
|
-
|
|
637
|
-
```typescript
|
|
638
|
-
import { AuthFetch, WalletClient } from '@bsv/sdk'
|
|
639
|
-
|
|
640
|
-
class RobustAuthClient {
|
|
641
|
-
private authFetch: AuthFetch
|
|
642
|
-
private maxRetries: number = 3
|
|
643
|
-
private retryDelay: number = 1000
|
|
644
|
-
|
|
645
|
-
constructor(wallet?: WalletClient) {
|
|
646
|
-
this.authFetch = new AuthFetch(wallet || new WalletClient('auto', 'localhost'))
|
|
647
|
-
}
|
|
648
|
-
|
|
649
|
-
async authenticatedRequest(
|
|
650
|
-
url: string,
|
|
651
|
-
options: any = {},
|
|
652
|
-
retryCount: number = 0
|
|
653
|
-
): Promise<Response> {
|
|
654
|
-
try {
|
|
655
|
-
const response = await this.authFetch.fetch(url, options)
|
|
656
|
-
|
|
657
|
-
if (response.status === 401 && retryCount < this.maxRetries) {
|
|
658
|
-
console.log(`Authentication failed, retrying... (${retryCount + 1}/${this.maxRetries})`)
|
|
659
|
-
|
|
660
|
-
// Wait and retry - AuthFetch will handle session management automatically
|
|
661
|
-
await this.delay(this.retryDelay * (retryCount + 1))
|
|
662
|
-
|
|
663
|
-
return this.authenticatedRequest(url, options, retryCount + 1)
|
|
664
|
-
}
|
|
665
|
-
|
|
666
|
-
return response
|
|
667
|
-
} catch (error) {
|
|
668
|
-
if (retryCount < this.maxRetries) {
|
|
669
|
-
console.log(`Request failed, retrying... (${retryCount + 1}/${this.maxRetries})`)
|
|
670
|
-
await this.delay(this.retryDelay * (retryCount + 1))
|
|
671
|
-
return this.authenticatedRequest(url, options, retryCount + 1)
|
|
672
|
-
}
|
|
673
|
-
|
|
674
|
-
throw error
|
|
675
|
-
}
|
|
676
|
-
}
|
|
677
|
-
|
|
678
|
-
private delay(ms: number): Promise<void> {
|
|
679
|
-
return new Promise(resolve => setTimeout(resolve, ms))
|
|
680
|
-
}
|
|
681
|
-
|
|
682
|
-
async batchRequests(requests: Array<{
|
|
683
|
-
url: string
|
|
684
|
-
options?: any
|
|
685
|
-
}>): Promise<Array<{
|
|
686
|
-
success: boolean
|
|
687
|
-
response?: any
|
|
688
|
-
error?: string
|
|
689
|
-
}>> {
|
|
690
|
-
const results = await Promise.allSettled(
|
|
691
|
-
requests.map(req => this.authenticatedRequest(req.url, req.options))
|
|
692
|
-
)
|
|
693
|
-
|
|
694
|
-
return results.map((result, index) => {
|
|
695
|
-
if (result.status === 'fulfilled') {
|
|
696
|
-
return {
|
|
697
|
-
success: true,
|
|
698
|
-
response: result.value
|
|
699
|
-
}
|
|
700
|
-
} else {
|
|
701
|
-
return {
|
|
702
|
-
success: false,
|
|
703
|
-
error: result.reason.message
|
|
704
|
-
}
|
|
705
|
-
}
|
|
706
|
-
})
|
|
707
|
-
}
|
|
708
|
-
}
|
|
709
|
-
|
|
710
|
-
async function demonstrateRobustAuthentication() {
|
|
711
|
-
const robustClient = new RobustAuthClient()
|
|
712
|
-
|
|
713
|
-
console.log('=== Testing robust authentication ===')
|
|
714
|
-
|
|
715
|
-
// Single request with retry logic
|
|
716
|
-
try {
|
|
717
|
-
const response = await robustClient.authenticatedRequest('https://api.example.com/data')
|
|
718
|
-
console.log('Single request successful:', response.ok)
|
|
719
|
-
} catch (error) {
|
|
720
|
-
console.log('Single request failed after retries:', error.message)
|
|
721
|
-
}
|
|
722
|
-
|
|
723
|
-
// Batch requests
|
|
724
|
-
const batchRequests = [
|
|
725
|
-
{ url: 'https://api.example.com/endpoint1' },
|
|
726
|
-
{ url: 'https://api.example.com/endpoint2' },
|
|
727
|
-
{ url: 'https://api.example.com/endpoint3' }
|
|
728
|
-
]
|
|
729
|
-
|
|
730
|
-
console.log('\n=== Testing batch requests ===')
|
|
731
|
-
const batchResults = await robustClient.batchRequests(batchRequests)
|
|
732
|
-
|
|
733
|
-
batchResults.forEach((result, index) => {
|
|
734
|
-
console.log(`Request ${index + 1}:`, result.success ? 'SUCCESS' : `FAILED - ${result.error}`)
|
|
735
|
-
})
|
|
736
|
-
|
|
737
|
-
return batchResults
|
|
738
|
-
}
|
|
739
|
-
|
|
740
|
-
demonstrateRobustAuthentication().catch(console.error)
|
|
741
|
-
```
|
|
742
|
-
|
|
743
|
-
## Error Handling and Debugging
|
|
744
|
-
|
|
745
|
-
### Comprehensive Error Handling
|
|
746
|
-
|
|
747
|
-
```typescript
|
|
748
|
-
import { AuthFetch, WalletClient } from '@bsv/sdk'
|
|
749
|
-
|
|
750
|
-
enum AuthErrorType {
|
|
751
|
-
NETWORK_ERROR = 'network_error',
|
|
752
|
-
AUTHENTICATION_FAILED = 'authentication_failed',
|
|
753
|
-
CERTIFICATE_INVALID = 'certificate_invalid',
|
|
754
|
-
PEER_UNREACHABLE = 'peer_unreachable',
|
|
755
|
-
SESSION_EXPIRED = 'session_expired'
|
|
756
|
-
}
|
|
757
|
-
|
|
758
|
-
class AuthError extends Error {
|
|
759
|
-
constructor(
|
|
760
|
-
public type: AuthErrorType,
|
|
761
|
-
message: string,
|
|
762
|
-
public originalError?: Error
|
|
763
|
-
) {
|
|
764
|
-
super(message)
|
|
765
|
-
this.name = 'AuthError'
|
|
766
|
-
}
|
|
767
|
-
}
|
|
768
|
-
|
|
769
|
-
class AuthFetchWithErrorHandling {
|
|
770
|
-
private authFetch: AuthFetch
|
|
771
|
-
private debugMode: boolean = false
|
|
772
|
-
|
|
773
|
-
constructor(wallet?: WalletClient, debugMode: boolean = false) {
|
|
774
|
-
this.authFetch = new AuthFetch(wallet || new WalletClient('auto', 'localhost'))
|
|
775
|
-
this.debugMode = debugMode
|
|
776
|
-
}
|
|
777
|
-
|
|
778
|
-
async safeRequest(url: string, options: any = {}): Promise<{
|
|
779
|
-
success: boolean
|
|
780
|
-
data?: any
|
|
781
|
-
error?: AuthError
|
|
782
|
-
}> {
|
|
783
|
-
try {
|
|
784
|
-
if (this.debugMode) {
|
|
785
|
-
console.log(`[DEBUG] Making request to: ${url}`)
|
|
786
|
-
console.log(`[DEBUG] Options:`, JSON.stringify(options, null, 2))
|
|
787
|
-
}
|
|
788
|
-
|
|
789
|
-
const response = await this.authFetch.fetch(url, options)
|
|
790
|
-
|
|
791
|
-
if (this.debugMode) {
|
|
792
|
-
console.log(`[DEBUG] Response status: ${response.status}`)
|
|
793
|
-
console.log(`[DEBUG] Response headers:`, Object.fromEntries(response.headers.entries()))
|
|
794
|
-
}
|
|
795
|
-
|
|
796
|
-
if (!response.ok) {
|
|
797
|
-
const errorType = this.categorizeHttpError(response.status)
|
|
798
|
-
const errorMessage = await this.extractErrorMessage(response)
|
|
799
|
-
|
|
800
|
-
return {
|
|
801
|
-
success: false,
|
|
802
|
-
error: new AuthError(errorType, errorMessage)
|
|
803
|
-
}
|
|
804
|
-
}
|
|
805
|
-
|
|
806
|
-
const data = await this.parseResponse(response)
|
|
807
|
-
return { success: true, data }
|
|
808
|
-
|
|
809
|
-
} catch (error) {
|
|
810
|
-
if (this.debugMode) {
|
|
811
|
-
console.log(`[DEBUG] Request failed:`, error)
|
|
812
|
-
}
|
|
813
|
-
|
|
814
|
-
const authError = this.categorizeError(error)
|
|
815
|
-
return { success: false, error: authError }
|
|
816
|
-
}
|
|
817
|
-
}
|
|
818
|
-
|
|
819
|
-
private categorizeHttpError(status: number): AuthErrorType {
|
|
820
|
-
switch (status) {
|
|
821
|
-
case 401:
|
|
822
|
-
return AuthErrorType.AUTHENTICATION_FAILED
|
|
823
|
-
case 403:
|
|
824
|
-
return AuthErrorType.CERTIFICATE_INVALID
|
|
825
|
-
case 408:
|
|
826
|
-
case 504:
|
|
827
|
-
return AuthErrorType.PEER_UNREACHABLE
|
|
828
|
-
default:
|
|
829
|
-
return AuthErrorType.NETWORK_ERROR
|
|
830
|
-
}
|
|
831
|
-
}
|
|
832
|
-
|
|
833
|
-
private async extractErrorMessage(response: Response): Promise<string> {
|
|
834
|
-
try {
|
|
835
|
-
const contentType = response.headers.get('content-type')
|
|
836
|
-
if (contentType && contentType.includes('application/json')) {
|
|
837
|
-
const errorData = await response.json()
|
|
838
|
-
return errorData.message || errorData.error || `HTTP ${response.status}`
|
|
839
|
-
} else {
|
|
840
|
-
return await response.text() || `HTTP ${response.status}`
|
|
841
|
-
}
|
|
842
|
-
} catch {
|
|
843
|
-
return `HTTP ${response.status}: ${response.statusText}`
|
|
844
|
-
}
|
|
845
|
-
}
|
|
846
|
-
|
|
847
|
-
private async parseResponse(response: Response): Promise<any> {
|
|
848
|
-
const contentType = response.headers.get('content-type')
|
|
849
|
-
if (contentType && contentType.includes('application/json')) {
|
|
850
|
-
return await response.json()
|
|
851
|
-
} else {
|
|
852
|
-
return await response.text()
|
|
853
|
-
}
|
|
854
|
-
}
|
|
855
|
-
|
|
856
|
-
private categorizeError(error: any): AuthError {
|
|
857
|
-
if (error.name === 'TypeError' && error.message.includes('fetch')) {
|
|
858
|
-
return new AuthError(
|
|
859
|
-
AuthErrorType.NETWORK_ERROR,
|
|
860
|
-
'Network connection failed',
|
|
861
|
-
error
|
|
862
|
-
)
|
|
863
|
-
}
|
|
864
|
-
|
|
865
|
-
if (error.message.includes('certificate')) {
|
|
866
|
-
return new AuthError(
|
|
867
|
-
AuthErrorType.CERTIFICATE_INVALID,
|
|
868
|
-
'Certificate validation failed',
|
|
869
|
-
error
|
|
870
|
-
)
|
|
871
|
-
}
|
|
872
|
-
|
|
873
|
-
if (error.message.includes('session')) {
|
|
874
|
-
return new AuthError(
|
|
875
|
-
AuthErrorType.SESSION_EXPIRED,
|
|
876
|
-
'Authentication session expired',
|
|
877
|
-
error
|
|
878
|
-
)
|
|
879
|
-
}
|
|
880
|
-
|
|
881
|
-
return new AuthError(
|
|
882
|
-
AuthErrorType.NETWORK_ERROR,
|
|
883
|
-
error.message || 'Unknown error occurred',
|
|
884
|
-
error
|
|
885
|
-
)
|
|
886
|
-
}
|
|
887
|
-
|
|
888
|
-
async testConnectivity(urls: string[]): Promise<{
|
|
889
|
-
reachable: string[]
|
|
890
|
-
unreachable: string[]
|
|
891
|
-
errors: Record<string, string>
|
|
892
|
-
}> {
|
|
893
|
-
const reachable: string[] = []
|
|
894
|
-
const unreachable: string[] = []
|
|
895
|
-
const errors: Record<string, string> = {}
|
|
896
|
-
|
|
897
|
-
console.log(`Testing connectivity to ${urls.length} endpoints...`)
|
|
898
|
-
|
|
899
|
-
const results = await Promise.allSettled(
|
|
900
|
-
urls.map(url => this.safeRequest(`${url}/ping`))
|
|
901
|
-
)
|
|
902
|
-
|
|
903
|
-
results.forEach((result, index) => {
|
|
904
|
-
const url = urls[index]
|
|
905
|
-
|
|
906
|
-
if (result.status === 'fulfilled' && result.value.success) {
|
|
907
|
-
reachable.push(url)
|
|
908
|
-
console.log(` ${url} - reachable`)
|
|
909
|
-
} else {
|
|
910
|
-
unreachable.push(url)
|
|
911
|
-
const error = result.status === 'fulfilled'
|
|
912
|
-
? result.value.error?.message || 'Unknown error'
|
|
913
|
-
: result.reason.message
|
|
914
|
-
errors[url] = error
|
|
915
|
-
console.log(` ${url} - ${error}`)
|
|
916
|
-
}
|
|
917
|
-
})
|
|
918
|
-
|
|
919
|
-
return { reachable, unreachable, errors }
|
|
920
|
-
}
|
|
921
|
-
}
|
|
922
|
-
|
|
923
|
-
async function demonstrateErrorHandling() {
|
|
924
|
-
const authClient = new AuthFetchWithErrorHandling(undefined, true) // Debug mode on
|
|
925
|
-
|
|
926
|
-
console.log('=== Testing error handling ===')
|
|
927
|
-
|
|
928
|
-
// Test various scenarios
|
|
929
|
-
const testUrls = [
|
|
930
|
-
'https://httpbin.org/status/200', // Should succeed
|
|
931
|
-
'https://httpbin.org/status/401', // Authentication error
|
|
932
|
-
'https://httpbin.org/status/403', // Certificate error
|
|
933
|
-
'https://httpbin.org/status/500', // Server error
|
|
934
|
-
'https://invalid-domain-12345.com' // Network error
|
|
935
|
-
]
|
|
936
|
-
|
|
937
|
-
for (const url of testUrls) {
|
|
938
|
-
console.log(`\n--- Testing: ${url} ---`)
|
|
939
|
-
const result = await authClient.safeRequest(url)
|
|
940
|
-
|
|
941
|
-
if (result.success) {
|
|
942
|
-
console.log(' Request successful')
|
|
943
|
-
} else {
|
|
944
|
-
console.log(` Request failed: ${result.error?.type} - ${result.error?.message}`)
|
|
945
|
-
}
|
|
946
|
-
}
|
|
947
|
-
|
|
948
|
-
// Test connectivity to multiple endpoints
|
|
949
|
-
console.log('\n=== Testing connectivity ===')
|
|
950
|
-
const connectivityTest = await authClient.testConnectivity([
|
|
951
|
-
'https://httpbin.org',
|
|
952
|
-
'https://jsonplaceholder.typicode.com',
|
|
953
|
-
'https://invalid-endpoint.example.com'
|
|
954
|
-
])
|
|
955
|
-
|
|
956
|
-
console.log('Connectivity results:', connectivityTest)
|
|
957
|
-
|
|
958
|
-
return connectivityTest
|
|
959
|
-
}
|
|
960
|
-
|
|
961
|
-
demonstrateErrorHandling().catch(console.error)
|
|
962
|
-
```
|
|
963
|
-
|
|
964
|
-
## Conclusion
|
|
965
|
-
|
|
966
|
-
Congratulations! You've successfully implemented a comprehensive authenticated communication system using the BSV TypeScript SDK. In this tutorial, you've learned how to:
|
|
967
|
-
|
|
968
|
-
### Core Concepts Mastered
|
|
969
|
-
|
|
970
|
-
1. **AuthFetch Integration**: Implemented authentication using identity-based signing
|
|
971
|
-
2. **Certificate Management**: Created and managed identity certificates for secure communication
|
|
972
|
-
3. **Request Signing**: Automatically signed requests with proper identity validation
|
|
973
|
-
4. **Error Handling**: Built robust error handling for authentication failures
|
|
974
|
-
5. **Network Resilience**: Implemented retry logic and connectivity testing
|
|
975
|
-
|
|
976
|
-
## Next Steps
|
|
977
|
-
|
|
978
|
-
- Learn about [Identity Management](./identity-management.md) for advanced identity workflows
|
|
979
|
-
- Explore [Authenticated API Communication](../guides/authenticated-api-communication.md) for server-side implementation
|
|
980
|
-
- Understand [Security Best Practices](../guides/security-best-practices.md) for production deployments
|
|
981
|
-
|
|
982
|
-
## Additional Resources
|
|
983
|
-
|
|
984
|
-
- [AuthFetch API Reference](../reference/auth.md)
|
|
985
|
-
- [Identity Client Documentation](../reference/identity.md)
|
|
986
|
-
- [BSV Identity Protocols](https://projectbabbage.com/docs/guides/identity)
|