0
# Error Handling and Abort Control
1
2
Comprehensive error handling with WebAuthn-specific error codes and abort service for managing ceremony lifecycles. This module provides detailed error classification and control mechanisms for WebAuthn operations.
3
4
## Capabilities
5
6
### WebAuthn Error Classification
7
8
The `WebAuthnError` class provides detailed error information for WebAuthn ceremony failures, with specific error codes that help identify the exact cause of failures.
9
10
```typescript { .api }
11
/**
12
* A custom Error used to return a more nuanced error detailing why one of the eight
13
* documented errors in the WebAuthn spec was raised after calling
14
* navigator.credentials.create() or navigator.credentials.get()
15
*/
16
class WebAuthnError extends Error {
17
/** Specific WebAuthn error code providing detailed failure reason */
18
code: WebAuthnErrorCode;
19
20
constructor(options: {
21
/** Human-readable error message */
22
message: string;
23
/** Specific WebAuthn error code */
24
code: WebAuthnErrorCode;
25
/** Original error that caused this WebAuthnError */
26
cause: Error;
27
/** Optional custom error name, defaults to cause.name */
28
name?: string;
29
});
30
}
31
```
32
33
### Error Codes
34
35
Comprehensive set of error codes covering all WebAuthn failure scenarios:
36
37
```typescript { .api }
38
type WebAuthnErrorCode =
39
/** User cancelled the WebAuthn ceremony */
40
| 'ERROR_CEREMONY_ABORTED'
41
/** Current domain is not valid for WebAuthn */
42
| 'ERROR_INVALID_DOMAIN'
43
/** Relying Party ID is invalid */
44
| 'ERROR_INVALID_RP_ID'
45
/** User ID length exceeds maximum allowed */
46
| 'ERROR_INVALID_USER_ID_LENGTH'
47
/** Public key credential parameters are malformed */
48
| 'ERROR_MALFORMED_PUBKEYCREDPARAMS'
49
/** General authenticator error occurred */
50
| 'ERROR_AUTHENTICATOR_GENERAL_ERROR'
51
/** Authenticator doesn't support discoverable credentials */
52
| 'ERROR_AUTHENTICATOR_MISSING_DISCOVERABLE_CREDENTIAL_SUPPORT'
53
/** Authenticator doesn't support user verification */
54
| 'ERROR_AUTHENTICATOR_MISSING_USER_VERIFICATION_SUPPORT'
55
/** Credential is already registered to this authenticator */
56
| 'ERROR_AUTHENTICATOR_PREVIOUSLY_REGISTERED'
57
/** Authenticator doesn't support any of the specified algorithms */
58
| 'ERROR_AUTHENTICATOR_NO_SUPPORTED_PUBKEYCREDPARAMS_ALG'
59
/** Auto-registration failed due to user verification failure */
60
| 'ERROR_AUTO_REGISTER_USER_VERIFICATION_FAILURE'
61
/** Generic error - check the cause property for details */
62
| 'ERROR_PASSTHROUGH_SEE_CAUSE_PROPERTY';
63
```
64
65
**Usage Examples:**
66
67
```typescript
68
import { startRegistration, WebAuthnError } from '@simplewebauthn/browser';
69
70
try {
71
const response = await startRegistration({ optionsJSON });
72
// Success - send to server
73
await verifyWithServer(response);
74
} catch (error) {
75
if (error instanceof WebAuthnError) {
76
switch (error.code) {
77
case 'ERROR_CEREMONY_ABORTED':
78
showMessage('Registration was cancelled. Please try again.');
79
break;
80
81
case 'ERROR_AUTHENTICATOR_PREVIOUSLY_REGISTERED':
82
showMessage('This authenticator is already registered. Please use a different one.');
83
break;
84
85
case 'ERROR_AUTHENTICATOR_MISSING_USER_VERIFICATION_SUPPORT':
86
showMessage('Your authenticator doesn\'t support user verification. Please use a different one.');
87
break;
88
89
case 'ERROR_INVALID_DOMAIN':
90
showMessage('WebAuthn is not supported on this domain.');
91
break;
92
93
case 'ERROR_PASSTHROUGH_SEE_CAUSE_PROPERTY':
94
console.error('WebAuthn error details:', error.cause);
95
showMessage('An unexpected error occurred. Please try again.');
96
break;
97
98
default:
99
console.error('WebAuthn error:', error.code, error.message);
100
showMessage('Registration failed. Please try again.');
101
}
102
} else {
103
// Handle non-WebAuthn errors
104
console.error('General error:', error.message);
105
showMessage('An error occurred. Please check your browser support.');
106
}
107
}
108
```
109
110
### Abort Service
111
112
The `WebAuthnAbortService` ensures only one WebAuthn ceremony is active at a time and provides manual cancellation capabilities.
113
114
```typescript { .api }
115
/**
116
* A service singleton to help ensure that only a single WebAuthn ceremony
117
* is active at a time. Automatically manages AbortSignal creation and cleanup.
118
*/
119
interface WebAuthnAbortService {
120
/**
121
* Prepare an abort signal that will help support multiple auth attempts
122
* without needing to reload the page. Automatically called by startRegistration()
123
* and startAuthentication().
124
* @returns AbortSignal for the current WebAuthn operation
125
*/
126
createNewAbortSignal(): AbortSignal;
127
128
/**
129
* Manually cancel any active WebAuthn registration or authentication attempt.
130
* Useful for handling navigation events or user-initiated cancellations.
131
*/
132
cancelCeremony(): void;
133
}
134
135
/** Singleton instance of the abort service */
136
declare const WebAuthnAbortService: WebAuthnAbortService;
137
```
138
139
**Usage Examples:**
140
141
```typescript
142
import { WebAuthnAbortService, startAuthentication } from '@simplewebauthn/browser';
143
144
// Manual cancellation (useful for navigation handling)
145
function handleRouteChange() {
146
// Cancel any ongoing WebAuthn ceremony
147
WebAuthnAbortService.cancelCeremony();
148
149
// Navigate to new route
150
navigateToRoute('/dashboard');
151
}
152
153
// Custom timeout handling
154
async function authenticateWithTimeout(optionsJSON: any, timeoutMs: number) {
155
const timeoutId = setTimeout(() => {
156
WebAuthnAbortService.cancelCeremony();
157
}, timeoutMs);
158
159
try {
160
const response = await startAuthentication({ optionsJSON });
161
clearTimeout(timeoutId);
162
return response;
163
} catch (error) {
164
clearTimeout(timeoutId);
165
166
if (error instanceof WebAuthnError && error.code === 'ERROR_CEREMONY_ABORTED') {
167
throw new Error('Authentication timed out');
168
}
169
170
throw error;
171
}
172
}
173
174
// The service is automatically used by startRegistration/startAuthentication
175
// No manual signal creation needed for normal usage
176
const response = await startAuthentication({ optionsJSON });
177
```
178
179
## Error Handling Patterns
180
181
### Comprehensive Error Handling
182
183
Handle both WebAuthn-specific and general errors appropriately:
184
185
```typescript
186
import { startRegistration, WebAuthnError, browserSupportsWebAuthn } from '@simplewebauthn/browser';
187
188
async function registerWithErrorHandling(optionsJSON: any) {
189
// Pre-flight checks
190
if (!browserSupportsWebAuthn()) {
191
throw new Error('WebAuthn is not supported in this browser');
192
}
193
194
try {
195
const response = await startRegistration({ optionsJSON });
196
return { success: true, response };
197
} catch (error) {
198
if (error instanceof WebAuthnError) {
199
return {
200
success: false,
201
errorCode: error.code,
202
userMessage: getUserFriendlyMessage(error.code),
203
technicalDetails: error.message
204
};
205
} else {
206
return {
207
success: false,
208
errorCode: 'GENERAL_ERROR',
209
userMessage: 'Registration failed. Please try again.',
210
technicalDetails: error.message
211
};
212
}
213
}
214
}
215
216
function getUserFriendlyMessage(errorCode: WebAuthnErrorCode): string {
217
const messages = {
218
'ERROR_CEREMONY_ABORTED': 'Registration was cancelled.',
219
'ERROR_AUTHENTICATOR_PREVIOUSLY_REGISTERED': 'This authenticator is already registered.',
220
'ERROR_AUTHENTICATOR_MISSING_USER_VERIFICATION_SUPPORT': 'Your authenticator doesn\'t support the required verification.',
221
'ERROR_INVALID_DOMAIN': 'This website cannot use passwordless authentication.',
222
'ERROR_INVALID_RP_ID': 'Website configuration error. Please contact support.',
223
'ERROR_INVALID_USER_ID_LENGTH': 'User ID is too long. Please contact support.',
224
'ERROR_MALFORMED_PUBKEYCREDPARAMS': 'Invalid registration parameters. Please contact support.',
225
'ERROR_AUTHENTICATOR_GENERAL_ERROR': 'Your authenticator encountered an error.',
226
'ERROR_AUTHENTICATOR_MISSING_DISCOVERABLE_CREDENTIAL_SUPPORT': 'Your authenticator doesn\'t support this type of credential.',
227
'ERROR_AUTHENTICATOR_NO_SUPPORTED_PUBKEYCREDPARAMS_ALG': 'Your authenticator doesn\'t support the required algorithms.',
228
'ERROR_AUTO_REGISTER_USER_VERIFICATION_FAILURE': 'Automatic registration failed. Please try manual registration.',
229
'ERROR_PASSTHROUGH_SEE_CAUSE_PROPERTY': 'An unexpected error occurred.'
230
};
231
232
return messages[errorCode] || 'An unknown error occurred.';
233
}
234
```
235
236
### Retry Logic with Abort Handling
237
238
Implement smart retry logic that respects user cancellations:
239
240
```typescript
241
import { startAuthentication, WebAuthnError, WebAuthnAbortService } from '@simplewebauthn/browser';
242
243
async function authenticateWithRetry(optionsJSON: any, maxRetries: number = 3) {
244
for (let attempt = 1; attempt <= maxRetries; attempt++) {
245
try {
246
const response = await startAuthentication({ optionsJSON });
247
return response;
248
} catch (error) {
249
if (error instanceof WebAuthnError) {
250
// Don't retry if user explicitly cancelled
251
if (error.code === 'ERROR_CEREMONY_ABORTED') {
252
throw new Error('Authentication was cancelled by user');
253
}
254
255
// Don't retry configuration errors
256
if (['ERROR_INVALID_DOMAIN', 'ERROR_INVALID_RP_ID'].includes(error.code)) {
257
throw error;
258
}
259
260
// Retry other WebAuthn errors
261
if (attempt < maxRetries) {
262
console.log(`Authentication attempt ${attempt} failed, retrying...`);
263
await new Promise(resolve => setTimeout(resolve, 1000)); // Brief delay
264
continue;
265
}
266
}
267
268
// Final attempt or non-WebAuthn error
269
throw error;
270
}
271
}
272
}
273
```
274
275
### Navigation and Cleanup
276
277
Handle navigation events to prevent memory leaks:
278
279
```typescript
280
import { WebAuthnAbortService } from '@simplewebauthn/browser';
281
282
// React Router example
283
import { useEffect } from 'react';
284
import { useNavigate } from 'react-router-dom';
285
286
function WebAuthnPage() {
287
const navigate = useNavigate();
288
289
useEffect(() => {
290
// Cleanup on component unmount
291
return () => {
292
WebAuthnAbortService.cancelCeremony();
293
};
294
}, []);
295
296
const handleBackButton = () => {
297
WebAuthnAbortService.cancelCeremony();
298
navigate('/login');
299
};
300
301
// ... component logic
302
}
303
304
// Vanilla JS example
305
window.addEventListener('beforeunload', () => {
306
WebAuthnAbortService.cancelCeremony();
307
});
308
309
// Single Page App routing
310
document.addEventListener('route-change', () => {
311
WebAuthnAbortService.cancelCeremony();
312
});
313
```
314
315
## Browser Error Mapping
316
317
WebAuthn errors are derived from the eight documented DOM exceptions:
318
319
- **AbortError** → `ERROR_CEREMONY_ABORTED`
320
- **ConstraintError** → Various constraint-related codes
321
- **InvalidStateError** → `ERROR_AUTHENTICATOR_PREVIOUSLY_REGISTERED`
322
- **NotAllowedError** → Various permission/policy codes
323
- **NotSupportedError** → Various capability-related codes
324
- **SecurityError** → `ERROR_INVALID_DOMAIN`, `ERROR_INVALID_RP_ID`
325
- **TypeError** → `ERROR_MALFORMED_PUBKEYCREDPARAMS`, `ERROR_INVALID_USER_ID_LENGTH`
326
- **UnknownError** → `ERROR_PASSTHROUGH_SEE_CAUSE_PROPERTY`
327
328
The library analyzes the original browser error and maps it to the most specific `WebAuthnErrorCode` possible, providing better debugging and user experience.