0
# Error Handling and Services
1
2
Comprehensive error handling with detailed error codes and abort service for managing concurrent WebAuthn operations. These utilities provide detailed error information and control over WebAuthn ceremony lifecycle.
3
4
## Capabilities
5
6
### WebAuthn Error Class
7
8
Custom Error class that provides nuanced error details for WebAuthn operations, helping developers understand and handle specific failure scenarios.
9
10
```typescript { .api }
11
/**
12
* A custom Error used to return a more nuanced error detailing _why_ one of the eight documented
13
* errors in the spec was raised after calling `navigator.credentials.create()` or
14
* `navigator.credentials.get()`:
15
*
16
* - `AbortError`
17
* - `ConstraintError`
18
* - `InvalidStateError`
19
* - `NotAllowedError`
20
* - `NotSupportedError`
21
* - `SecurityError`
22
* - `TypeError`
23
* - `UnknownError`
24
*/
25
class WebAuthnError extends Error {
26
code: WebAuthnErrorCode;
27
28
constructor(options: {
29
message: string;
30
code: WebAuthnErrorCode;
31
cause: Error;
32
name?: string;
33
});
34
}
35
```
36
37
**Usage Examples:**
38
39
```typescript
40
import { startRegistration, WebAuthnError } from "@simplewebauthn/browser";
41
42
try {
43
const response = await startRegistration({ optionsJSON });
44
} catch (error) {
45
if (error instanceof WebAuthnError) {
46
console.log(`WebAuthn Error: ${error.code}`);
47
console.log(`Message: ${error.message}`);
48
console.log(`Original error:`, error.cause);
49
50
// Handle specific error types
51
switch (error.code) {
52
case 'ERROR_CEREMONY_ABORTED':
53
console.log("User cancelled the operation");
54
break;
55
case 'ERROR_AUTHENTICATOR_PREVIOUSLY_REGISTERED':
56
console.log("This authenticator is already registered");
57
break;
58
case 'ERROR_INVALID_DOMAIN':
59
console.log("WebAuthn not allowed on this domain");
60
break;
61
default:
62
console.log("Unexpected WebAuthn error occurred");
63
}
64
} else {
65
console.error("Non-WebAuthn error:", error);
66
}
67
}
68
```
69
70
### WebAuthn Error Codes
71
72
Detailed error codes that provide specific information about WebAuthn failures.
73
74
```typescript { .api }
75
type WebAuthnErrorCode =
76
| 'ERROR_CEREMONY_ABORTED'
77
| 'ERROR_INVALID_DOMAIN'
78
| 'ERROR_INVALID_RP_ID'
79
| 'ERROR_INVALID_USER_ID_LENGTH'
80
| 'ERROR_MALFORMED_PUBKEYCREDPARAMS'
81
| 'ERROR_AUTHENTICATOR_GENERAL_ERROR'
82
| 'ERROR_AUTHENTICATOR_MISSING_DISCOVERABLE_CREDENTIAL_SUPPORT'
83
| 'ERROR_AUTHENTICATOR_MISSING_USER_VERIFICATION_SUPPORT'
84
| 'ERROR_AUTHENTICATOR_PREVIOUSLY_REGISTERED'
85
| 'ERROR_AUTHENTICATOR_NO_SUPPORTED_PUBKEYCREDPARAMS_ALG'
86
| 'ERROR_AUTO_REGISTER_USER_VERIFICATION_FAILURE'
87
| 'ERROR_PASSTHROUGH_SEE_CAUSE_PROPERTY';
88
```
89
90
**Error Code Meanings:**
91
92
- `ERROR_CEREMONY_ABORTED`: User cancelled the WebAuthn operation
93
- `ERROR_INVALID_DOMAIN`: WebAuthn not allowed on the current domain
94
- `ERROR_INVALID_RP_ID`: Relying Party ID is invalid or mismatched
95
- `ERROR_INVALID_USER_ID_LENGTH`: User ID exceeds maximum allowed length
96
- `ERROR_MALFORMED_PUBKEYCREDPARAMS`: Invalid public key credential parameters
97
- `ERROR_AUTHENTICATOR_GENERAL_ERROR`: General authenticator hardware/software error
98
- `ERROR_AUTHENTICATOR_MISSING_DISCOVERABLE_CREDENTIAL_SUPPORT`: Authenticator doesn't support resident/discoverable credentials
99
- `ERROR_AUTHENTICATOR_MISSING_USER_VERIFICATION_SUPPORT`: Authenticator can't perform required user verification
100
- `ERROR_AUTHENTICATOR_PREVIOUSLY_REGISTERED`: Attempting to register an already-registered authenticator
101
- `ERROR_AUTHENTICATOR_NO_SUPPORTED_PUBKEYCREDPARAMS_ALG`: No supported cryptographic algorithms
102
- `ERROR_AUTO_REGISTER_USER_VERIFICATION_FAILURE`: Auto-registration failed user verification
103
- `ERROR_PASSTHROUGH_SEE_CAUSE_PROPERTY`: Error details are in the cause property
104
105
### WebAuthn Abort Service
106
107
Service singleton to ensure only a single WebAuthn ceremony is active at a time, preventing conflicts between concurrent operations.
108
109
```typescript { .api }
110
/**
111
* A service singleton to help ensure that only a single WebAuthn ceremony is active at a time.
112
*
113
* Users of **@simplewebauthn/browser** shouldn't typically need to use this, but it can help e.g.
114
* developers building projects that use client-side routing to better control the behavior of
115
* their UX in response to router navigation events.
116
*/
117
interface WebAuthnAbortService {
118
/**
119
* Prepare an abort signal that will help support multiple auth attempts without needing to
120
* reload the page. This is automatically called whenever `startRegistration()` and
121
* `startAuthentication()` are called.
122
*/
123
createNewAbortSignal(): AbortSignal;
124
125
/**
126
* Manually cancel any active WebAuthn registration or authentication attempt.
127
*/
128
cancelCeremony(): void;
129
}
130
131
// Exported as a singleton instance
132
const WebAuthnAbortService: WebAuthnAbortService;
133
```
134
135
**Usage Examples:**
136
137
```typescript
138
import { WebAuthnAbortService, startRegistration } from "@simplewebauthn/browser";
139
140
// Manual ceremony cancellation (advanced usage)
141
function setupCancelButton() {
142
const cancelButton = document.getElementById('cancel-webauthn');
143
cancelButton.addEventListener('click', () => {
144
WebAuthnAbortService.cancelCeremony();
145
console.log("WebAuthn ceremony cancelled");
146
});
147
}
148
149
// Router navigation cleanup (SPA usage)
150
function onRouteChange() {
151
// Cancel any ongoing WebAuthn operations when navigating
152
WebAuthnAbortService.cancelCeremony();
153
}
154
155
// The service is used automatically by startRegistration/startAuthentication
156
try {
157
// This automatically calls WebAuthnAbortService.createNewAbortSignal()
158
const response = await startRegistration({ optionsJSON });
159
} catch (error) {
160
if (error.name === 'AbortError') {
161
console.log("WebAuthn operation was aborted");
162
}
163
}
164
165
// Advanced: Manual abort signal creation
166
const abortSignal = WebAuthnAbortService.createNewAbortSignal();
167
console.log("New abort signal created for WebAuthn operation");
168
```
169
170
## Error Handling Patterns
171
172
### Basic Error Handling
173
174
```typescript
175
import { startRegistration, startAuthentication, WebAuthnError } from "@simplewebauthn/browser";
176
177
async function handleWebAuthnRegistration(options) {
178
try {
179
const response = await startRegistration({ optionsJSON: options });
180
return { success: true, data: response };
181
} catch (error) {
182
if (error instanceof WebAuthnError) {
183
return {
184
success: false,
185
error: error.code,
186
message: error.message
187
};
188
}
189
return {
190
success: false,
191
error: 'UNKNOWN_ERROR',
192
message: error.message
193
};
194
}
195
}
196
```
197
198
### User-Friendly Error Messages
199
200
```typescript
201
function getErrorMessage(error: WebAuthnError): string {
202
switch (error.code) {
203
case 'ERROR_CEREMONY_ABORTED':
204
return "Authentication was cancelled. Please try again.";
205
case 'ERROR_INVALID_DOMAIN':
206
return "Authentication is not allowed on this website.";
207
case 'ERROR_AUTHENTICATOR_PREVIOUSLY_REGISTERED':
208
return "This security key has already been registered.";
209
case 'ERROR_AUTHENTICATOR_MISSING_USER_VERIFICATION_SUPPORT':
210
return "Your security key doesn't support the required verification method.";
211
case 'ERROR_AUTHENTICATOR_NO_SUPPORTED_PUBKEYCREDPARAMS_ALG':
212
return "Your security key doesn't support the required encryption methods.";
213
default:
214
return "An unexpected error occurred during authentication.";
215
}
216
}
217
218
// Usage
219
try {
220
await startRegistration({ optionsJSON });
221
} catch (error) {
222
if (error instanceof WebAuthnError) {
223
showUserError(getErrorMessage(error));
224
}
225
}
226
```
227
228
### Retry Logic with Error Handling
229
230
```typescript
231
async function retryableWebAuthnOperation(operationFn, maxRetries = 3) {
232
for (let attempt = 1; attempt <= maxRetries; attempt++) {
233
try {
234
return await operationFn();
235
} catch (error) {
236
if (error instanceof WebAuthnError) {
237
// Don't retry certain errors
238
const nonRetryableErrors = [
239
'ERROR_INVALID_DOMAIN',
240
'ERROR_INVALID_RP_ID',
241
'ERROR_AUTHENTICATOR_PREVIOUSLY_REGISTERED',
242
];
243
244
if (nonRetryableErrors.includes(error.code)) {
245
throw error; // Don't retry these
246
}
247
248
if (attempt === maxRetries) {
249
throw error; // Final attempt failed
250
}
251
252
console.log(`Attempt ${attempt} failed: ${error.code}, retrying...`);
253
await new Promise(resolve => setTimeout(resolve, 1000 * attempt));
254
} else {
255
throw error; // Non-WebAuthn errors aren't retryable
256
}
257
}
258
}
259
}
260
261
// Usage
262
try {
263
const response = await retryableWebAuthnOperation(() =>
264
startRegistration({ optionsJSON })
265
);
266
} catch (error) {
267
console.error("All retry attempts failed:", error);
268
}
269
```
270
271
## Service Integration
272
273
The abort service integrates seamlessly with the main WebAuthn functions:
274
275
```typescript
276
import { startRegistration, startAuthentication, WebAuthnAbortService } from "@simplewebauthn/browser";
277
278
// Both functions automatically use the abort service
279
const registrationPromise = startRegistration({ optionsJSON: regOptions });
280
const authenticationPromise = startAuthentication({ optionsJSON: authOptions });
281
282
// If you start a new operation, the previous one is automatically cancelled
283
// This prevents conflicts and ensures only one ceremony runs at a time
284
285
// Manual cancellation is available for advanced use cases
286
document.getElementById('cancel').addEventListener('click', () => {
287
WebAuthnAbortService.cancelCeremony();
288
});
289
```
290
291
This service is particularly useful in single-page applications where users might navigate between pages or trigger multiple authentication attempts.