0
# SimpleWebAuthn Server
1
2
Server-side WebAuthn functionality for Node.js, Deno, and other JavaScript runtimes. Provides functions to generate WebAuthn options, verify responses, and manage authenticator metadata.
3
4
## Additional Imports
5
6
The server package also exports helper functions for advanced use cases:
7
8
```typescript
9
import {
10
generateChallenge,
11
generateUserID,
12
convertAAGUIDToString,
13
decodeAttestationObject,
14
decodeClientDataJSON,
15
parseAuthenticatorData,
16
toHash,
17
convertCOSEtoPKCS,
18
verifySignature,
19
isoBase64URL,
20
isoUint8Array,
21
} from "@simplewebauthn/server/helpers";
22
```
23
24
## Capabilities
25
26
### Registration Options Generation
27
28
Generates options for WebAuthn registration ceremonies.
29
30
```typescript { .api }
31
/**
32
* Prepare a value to pass into navigator.credentials.create(...) for authenticator registration
33
*/
34
function generateRegistrationOptions(options: {
35
rpName: string;
36
rpID: string;
37
userName: string;
38
userID?: Uint8Array;
39
challenge?: string | Uint8Array;
40
userDisplayName?: string;
41
timeout?: number;
42
attestationType?: 'direct' | 'enterprise' | 'none';
43
excludeCredentials?: {
44
id: Base64URLString;
45
transports?: AuthenticatorTransportFuture[];
46
}[];
47
authenticatorSelection?: AuthenticatorSelectionCriteria;
48
extensions?: AuthenticationExtensionsClientInputs;
49
supportedAlgorithmIDs?: COSEAlgorithmIdentifier[];
50
preferredAuthenticatorType?: 'securityKey' | 'localDevice' | 'remoteDevice';
51
}): Promise<PublicKeyCredentialCreationOptionsJSON>;
52
53
type GenerateRegistrationOptionsOpts = Parameters<typeof generateRegistrationOptions>[0];
54
```
55
56
**Usage Examples:**
57
58
```typescript
59
import { generateRegistrationOptions } from "@simplewebauthn/server";
60
61
// Basic registration options
62
const registrationOptions = await generateRegistrationOptions({
63
rpName: "My App",
64
rpID: "example.com",
65
userName: "user@example.com",
66
userDisplayName: "John Doe",
67
});
68
69
// Advanced registration with exclusions and authenticator selection
70
const advancedOptions = await generateRegistrationOptions({
71
rpName: "My App",
72
rpID: "example.com",
73
userName: "user@example.com",
74
userDisplayName: "John Doe",
75
attestationType: "direct",
76
excludeCredentials: existingCredentials.map(cred => ({
77
id: cred.id,
78
transports: cred.transports,
79
})),
80
authenticatorSelection: {
81
authenticatorAttachment: "platform",
82
requireResidentKey: true,
83
userVerification: "required",
84
},
85
timeout: 120000,
86
});
87
```
88
89
### Registration Response Verification
90
91
Verifies WebAuthn registration responses from the client.
92
93
```typescript { .api }
94
/**
95
* Verify that the user has legitimately completed the registration process
96
*/
97
function verifyRegistrationResponse(options: {
98
response: RegistrationResponseJSON;
99
expectedChallenge: string | ((challenge: string) => boolean | Promise<boolean>);
100
expectedOrigin: string | string[];
101
expectedRPID?: string | string[];
102
expectedType?: string | string[];
103
requireUserPresence?: boolean;
104
requireUserVerification?: boolean;
105
supportedAlgorithmIDs?: COSEAlgorithmIdentifier[];
106
}): Promise<VerifiedRegistrationResponse>;
107
108
interface VerifiedRegistrationResponse {
109
verified: boolean;
110
registrationInfo?: {
111
fmt: AttestationFormat;
112
counter: number;
113
aaguid: string;
114
credentialID: Uint8Array;
115
credentialPublicKey: Uint8Array;
116
userVerified: boolean;
117
credentialDeviceType: CredentialDeviceType;
118
credentialBackedUp: boolean;
119
};
120
}
121
```
122
123
**Usage Examples:**
124
125
```typescript
126
import { verifyRegistrationResponse } from "@simplewebauthn/server";
127
128
const verification = await verifyRegistrationResponse({
129
response: registrationResponseFromClient,
130
expectedChallenge: expectedChallenge,
131
expectedOrigin: "https://example.com",
132
expectedRPID: "example.com",
133
requireUserVerification: true,
134
});
135
136
if (verification.verified && verification.registrationInfo) {
137
// Store credential in database
138
await saveCredential({
139
id: verification.registrationInfo.credentialID,
140
publicKey: verification.registrationInfo.credentialPublicKey,
141
counter: verification.registrationInfo.counter,
142
deviceType: verification.registrationInfo.credentialDeviceType,
143
backedUp: verification.registrationInfo.credentialBackedUp,
144
});
145
}
146
```
147
148
### Authentication Options Generation
149
150
Generates options for WebAuthn authentication ceremonies.
151
152
```typescript { .api }
153
/**
154
* Prepare a value to pass into navigator.credentials.get(...) for authenticator authentication
155
*/
156
function generateAuthenticationOptions(options: {
157
rpID: string;
158
allowCredentials?: {
159
id: Base64URLString;
160
transports?: AuthenticatorTransportFuture[];
161
}[];
162
challenge?: string | Uint8Array;
163
timeout?: number;
164
userVerification?: 'required' | 'preferred' | 'discouraged';
165
extensions?: AuthenticationExtensionsClientInputs;
166
}): Promise<PublicKeyCredentialRequestOptionsJSON>;
167
168
type GenerateAuthenticationOptionsOpts = Parameters<typeof generateAuthenticationOptions>[0];
169
```
170
171
**Usage Examples:**
172
173
```typescript
174
import { generateAuthenticationOptions } from "@simplewebauthn/server";
175
176
// Basic authentication options (passwordless)
177
const authOptions = await generateAuthenticationOptions({
178
rpID: "example.com",
179
userVerification: "preferred",
180
});
181
182
// Authentication with specific credentials (second factor)
183
const specificAuthOptions = await generateAuthenticationOptions({
184
rpID: "example.com",
185
allowCredentials: userCredentials.map(cred => ({
186
id: cred.id,
187
transports: cred.transports,
188
})),
189
userVerification: "required",
190
timeout: 60000,
191
});
192
```
193
194
### Authentication Response Verification
195
196
Verifies WebAuthn authentication responses from the client.
197
198
```typescript { .api }
199
/**
200
* Verify that the user has legitimately completed the authentication process
201
*/
202
function verifyAuthenticationResponse(options: {
203
response: AuthenticationResponseJSON;
204
expectedChallenge: string | ((challenge: string) => boolean | Promise<boolean>);
205
expectedOrigin: string | string[];
206
expectedRPID?: string | string[];
207
credential: {
208
id: Base64URLString;
209
publicKey: Uint8Array;
210
counter: number;
211
transports?: AuthenticatorTransportFuture[];
212
};
213
requireUserPresence?: boolean;
214
requireUserVerification?: boolean;
215
advancedFIDOConfig?: {
216
userVerification?: 'required' | 'preferred' | 'discouraged';
217
};
218
}): Promise<VerifiedAuthenticationResponse>;
219
220
interface VerifiedAuthenticationResponse {
221
verified: boolean;
222
authenticationInfo?: {
223
newCounter: number;
224
userVerified: boolean;
225
credentialDeviceType: CredentialDeviceType;
226
credentialBackedUp: boolean;
227
};
228
}
229
```
230
231
**Usage Examples:**
232
233
```typescript
234
import { verifyAuthenticationResponse } from "@simplewebauthn/server";
235
236
const credential = await getCredentialFromDB(credentialID);
237
238
const verification = await verifyAuthenticationResponse({
239
response: authenticationResponseFromClient,
240
expectedChallenge: expectedChallenge,
241
expectedOrigin: "https://example.com",
242
expectedRPID: "example.com",
243
credential: {
244
id: credential.id,
245
publicKey: credential.publicKey,
246
counter: credential.counter,
247
transports: credential.transports,
248
},
249
requireUserVerification: true,
250
});
251
252
if (verification.verified && verification.authenticationInfo) {
253
// Update counter in database
254
await updateCredentialCounter(
255
credentialID,
256
verification.authenticationInfo.newCounter
257
);
258
259
// User is authenticated
260
return loginUser(userID);
261
}
262
```
263
264
### Metadata Service
265
266
Service for managing FIDO Metadata Service (MDS) data to validate authenticators.
267
268
```typescript { .api }
269
/**
270
* Global service for interacting with the FIDO Metadata Service
271
*/
272
interface MetadataService {
273
/**
274
* Prepare the service to handle remote MDS servers and/or cache local metadata statements
275
*/
276
initialize(options?: {
277
verificationMode?: VerificationMode;
278
mdsServers?: string[];
279
statements?: MetadataStatement[];
280
}): Promise<void>;
281
282
/**
283
* Get a metadata statement for a given AAGUID
284
*/
285
getStatement(aaguid: string): Promise<MetadataStatement | undefined>;
286
287
/**
288
* Get all cached metadata statements
289
*/
290
getStatements(): Promise<MetadataStatement[]>;
291
}
292
293
type VerificationMode = 'permissive' | 'strict';
294
295
// Exported singleton instance
296
const MetadataService: MetadataService;
297
```
298
299
**Usage Examples:**
300
301
```typescript
302
import { MetadataService } from "@simplewebauthn/server";
303
304
// Initialize with default FIDO MDS
305
await MetadataService.initialize();
306
307
// Initialize with custom verification mode
308
await MetadataService.initialize({
309
verificationMode: 'strict', // Only allow registered AAGUIDs
310
mdsServers: ['https://mds.fidoalliance.org/'],
311
});
312
313
// Get metadata for a specific authenticator
314
const statement = await MetadataService.getStatement(aaguid);
315
if (statement) {
316
console.log("Authenticator:", statement.description);
317
console.log("Attestation types:", statement.attestationTypes);
318
}
319
```
320
321
### Settings Service
322
323
Service for managing root certificates and attestation format settings.
324
325
```typescript { .api }
326
/**
327
* Global service for managing attestation format settings
328
*/
329
interface SettingsService {
330
/**
331
* Set potential root certificates for attestation formats that use them
332
*/
333
setRootCertificates(opts: {
334
identifier: RootCertIdentifier;
335
certificates: (Uint8Array | string)[];
336
}): void;
337
338
/**
339
* Get any registered root certificates for the specified attestation format
340
*/
341
getRootCertificates(opts: {
342
identifier: RootCertIdentifier;
343
}): string[];
344
}
345
346
type RootCertIdentifier = AttestationFormat | 'mds';
347
348
// Exported singleton instance
349
const SettingsService: SettingsService;
350
```
351
352
**Usage Examples:**
353
354
```typescript
355
import { SettingsService } from "@simplewebauthn/server";
356
357
// Add custom root certificates for Android attestation
358
SettingsService.setRootCertificates({
359
identifier: 'android-key',
360
certificates: [customRootCertPEM],
361
});
362
363
// Get root certificates for a format
364
const certs = SettingsService.getRootCertificates({
365
identifier: 'apple',
366
});
367
```
368
369
## Supported Algorithm Identifiers
370
371
```typescript { .api }
372
/**
373
* Supported crypto algo identifiers
374
* See https://w3c.github.io/webauthn/#sctn-alg-identifier
375
* and https://www.iana.org/assignments/cose/cose.xhtml#algorithms
376
*/
377
const supportedCOSEAlgorithmIdentifiers: COSEAlgorithmIdentifier[] = [
378
-8, // EdDSA
379
-7, // ECDSA w/ SHA-256
380
-36, // ECDSA w/ SHA-512
381
-37, // RSASSA-PSS w/ SHA-256
382
-38, // RSASSA-PSS w/ SHA-384
383
-39, // RSASSA-PSS w/ SHA-512
384
-257, // RSASSA-PKCS1-v1_5 w/ SHA-256
385
-258, // RSASSA-PKCS1-v1_5 w/ SHA-384
386
-259, // RSASSA-PKCS1-v1_5 w/ SHA-512
387
-65535 // RSASSA-PKCS1-v1_5 w/ SHA-1 (Deprecated)
388
];
389
```
390
391
## Complete Server Workflow
392
393
```typescript
394
import {
395
generateRegistrationOptions,
396
verifyRegistrationResponse,
397
generateAuthenticationOptions,
398
verifyAuthenticationResponse,
399
MetadataService,
400
} from "@simplewebauthn/server";
401
402
// Initialize metadata service
403
await MetadataService.initialize();
404
405
// Registration flow
406
app.post('/webauthn/register/begin', async (req, res) => {
407
const options = await generateRegistrationOptions({
408
rpName: "My App",
409
rpID: "example.com",
410
userName: req.user.email,
411
userDisplayName: req.user.name,
412
excludeCredentials: req.user.credentials?.map(cred => ({
413
id: cred.id,
414
transports: cred.transports,
415
})),
416
});
417
418
req.session.challenge = options.challenge;
419
res.json(options);
420
});
421
422
app.post('/webauthn/register/finish', async (req, res) => {
423
const verification = await verifyRegistrationResponse({
424
response: req.body,
425
expectedChallenge: req.session.challenge,
426
expectedOrigin: "https://example.com",
427
expectedRPID: "example.com",
428
});
429
430
if (verification.verified && verification.registrationInfo) {
431
// Save credential to database
432
await saveCredential(req.user.id, verification.registrationInfo);
433
res.json({ verified: true });
434
} else {
435
res.status(400).json({ error: "Registration failed" });
436
}
437
});
438
439
// Authentication flow
440
app.post('/webauthn/authenticate/begin', async (req, res) => {
441
const options = await generateAuthenticationOptions({
442
rpID: "example.com",
443
allowCredentials: req.user.credentials?.map(cred => ({
444
id: cred.id,
445
transports: cred.transports,
446
})),
447
});
448
449
req.session.challenge = options.challenge;
450
res.json(options);
451
});
452
453
app.post('/webauthn/authenticate/finish', async (req, res) => {
454
const credential = await getCredential(req.body.id);
455
456
const verification = await verifyAuthenticationResponse({
457
response: req.body,
458
expectedChallenge: req.session.challenge,
459
expectedOrigin: "https://example.com",
460
expectedRPID: "example.com",
461
credential: credential,
462
});
463
464
if (verification.verified) {
465
// Update counter and log in user
466
await updateCredentialCounter(credential.id, verification.authenticationInfo.newCounter);
467
req.session.loggedIn = true;
468
res.json({ verified: true });
469
} else {
470
res.status(400).json({ error: "Authentication failed" });
471
}
472
});
473
```