@@ -11,8 +11,7 @@ image:https://github.com/Yubico/java-webauthn-server/actions/workflows/release-v
11
11
Server-side https://www.w3.org/TR/webauthn/[Web Authentication] library for
12
12
Java. Provides implementations of the
13
13
https://www.w3.org/TR/webauthn/#sctn-rp-operations[Relying Party operations] required
14
- for a server to support Web Authentication. This includes registering
15
- authenticators and authenticating registered authenticators.
14
+ for a server to support Web Authentication, including https://passkeys.dev/[passkey authentication].
16
15
17
16
18
17
[WARNING]
@@ -30,6 +29,7 @@ If you are, we urge you to upgrade your Java deployment to a version that is saf
30
29
31
30
*Table of contents*
32
31
32
+ :toclevels: 3
33
33
toc::[]
34
34
35
35
@@ -133,7 +133,7 @@ The server side involves:
133
133
and
134
134
link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.4.1/com/yubico/webauthn/RelyingParty.html#finishAssertion(com.yubico.webauthn.FinishAssertionOptions)[`RelyingParty.finishAssertion(...)`]
135
135
methods to perform authentication ceremonies.
136
- 5. Use the outputs of `finishRegistration` and `finishAssertion` to update your database, initiate sessions, etc .
136
+ 5. Optionally use additional features: passkeys, passwordless multi-factor authentication, credential backup state .
137
137
138
138
The client side involves:
139
139
@@ -275,18 +275,21 @@ Here is an example of things you will likely want to store:
275
275
276
276
[source,java]
277
277
----------
278
- storeCredential( // Some database access method of your own design
279
- "alice", // Username or other appropriate user identifier
280
- result.getKeyId(), // Credential ID and transports for allowCredentials
281
- result.getPublicKeyCose(), // Public key for verifying authentication signatures
282
- result.isDiscoverable(), // Can this key be used for username-less auth?
283
- result.signatureCount(), // Initial signature counter value
278
+ storeCredential( // Some database access method of your own design
279
+ "alice", // Username or other appropriate user identifier
280
+ result.getKeyId(), // Credential ID and transports for allowCredentials
281
+ result.getPublicKeyCose(), // Public key for verifying authentication signatures
282
+ result.getSignatureCount(), // Initial signature counter value
283
+ result.isDiscoverable(), // Is this a passkey?
284
+ result.isBackupEligible(), // Can this credential be backed up (synced)?
285
+ result.isBackedUp(), // Is this credential currently backed up?
284
286
pkc.getResponse().getAttestationObject(), // Store attestation object for future reference
285
287
pkc.getResponse().getClientDataJSON() // Store client data for re-verifying signature if needed
286
288
);
287
289
----------
288
290
289
291
292
+ [#getting-started-authentication]
290
293
=== 4. Authentication
291
294
292
295
Like registration ceremonies, an authentication ceremony consists of 5 main steps:
@@ -367,31 +370,56 @@ throw new RuntimeException("Authentication failed");
367
370
368
371
Finally, if the previous step was successful, update your database using the
369
372
link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.4.1/com/yubico/webauthn/AssertionResult.html[`AssertionResult`].
370
- Most importantly, you should update the signature counter. That might look something like this:
373
+ Most importantly you should update the signature counter, and the backup state flag if you use it . That might look something like this:
371
374
372
375
[source,java]
373
376
----------
374
377
updateCredential( // Some database access method of your own design
375
378
"alice", // Query by username or other appropriate user identifier
376
379
result.getCredentialId(), // Query by credential ID of the credential used
377
- result.signatureCount(), // Set new signature counter value
380
+ result.getSignatureCount(), // Set new signature counter value
381
+ result.isBackedUp(), // Set new backup state flag
378
382
Clock.systemUTC().instant() // Set time of last use (now)
379
383
);
380
384
----------
381
385
382
386
Then do whatever else you need - for example, initiate a user session.
383
387
384
388
385
- === 5. Passwordless, username-less authentication
389
+ === 5. Optional features: passkeys, multi-factor, backup state
386
390
387
- WebAuthn supports passwordless multi-factor authentication via on-authenticator
388
- https://www.w3.org/TR/webauthn-2/#user-verification[user verification],
389
- and username-less authentication via
390
- https://www.w3.org/TR/webauthn-2/#discoverable-credential[discoverable credentials]
391
- (sometimes the term "passwordless" is used to mean the combination of both, but
392
- here the two are treated separately).
391
+ WebAuthn supports a number of additional features beyond the basics:
393
392
394
- Discoverable credentials must be enabled at registration time by setting the
393
+ - <<passkeys,Passkeys>>: passwordless, username-less authentication.
394
+ link:https://passkey.org[Try it on passkey.org!]
395
+ - <<user-verification,User verification>>: passwordless, streamlined multi-factor authentication.
396
+ - <<autofill-ui,Autofill UI>>: Unintrusive passkey integration in traditional login forms.
397
+ - <<credential-backup-state,Credential backup state>>: hints on how vulnerable the user is to authenticator loss.
398
+
399
+
400
+ [#passkeys]
401
+ ==== Passkeys: passwordless, username-less authentication
402
+
403
+ A https://passkeys.dev/[passkey] is a WebAuthn credential that can simultaneously both _identify_ and _authenticate_ the user.
404
+ This is also called a link:https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#discoverable-credential[discoverable credential].
405
+ By default, credentials are created non-discoverable, which means the server
406
+ must list them in the
407
+ https://www.w3.org/TR/webauthn/#dom-publickeycredentialrequestoptions-allowcredentials[`allowCredentials`]
408
+ parameter before the user can use them to authenticate.
409
+ This is typically because the credential private key is not stored within the authenticator,
410
+ but instead encoded into one of the credential IDs in `allowCredentials`.
411
+ This way even a small hardware authenticator can have an unlimited credential capacity,
412
+ but with the drawback that the user must first identify themself to the server
413
+ so the server can retrieve the correct `allowCredentials` list.
414
+
415
+ Passkeys are instead stored within the authenticator, and also include the user's
416
+ link:https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#user-handle[user handle]
417
+ in addition to the credential ID.
418
+ This way the user can be both identified and authenticated simultaneously.
419
+ Many passkey-capable authenticators also offer a credential sync mechanism
420
+ to allow one passkey to be used on multiple devices.
421
+
422
+ Passkeys can be created by setting the
395
423
link:https://www.w3.org/TR/webauthn-2/#dom-publickeycredentialcreationoptions-authenticatorselection[`authenticatorSelection`].link:https://www.w3.org/TR/webauthn-2/#dom-authenticatorselectioncriteria-residentkey[`residentKey`]
396
424
option:
397
425
@@ -413,13 +441,22 @@ The username can then be omitted when starting an authentication ceremony:
413
441
AssertionRequest request = rp.startAssertion(StartAssertionOptions.builder().build());
414
442
----------
415
443
416
- Some authenticators might enable this feature even if not required, and setting
417
- the `residentKey` option to `ResidentKeyRequirement.PREFERRED` will enable it if the
418
- authenticator supports it. The
419
- https://www.w3.org/TR/webauthn-2/#sctn-authenticator-credential-properties-extension[`credProps` extension]
420
- can be used to determine whether the created credential is discoverable, and is enabled by default.
444
+ Some authenticators might create passkeys even if not required, and setting
445
+ the `residentKey` option to `ResidentKeyRequirement.PREFERRED` will create a passkey
446
+ if the authenticator supports it.
447
+ The
448
+ link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.4.1/com/yubico/webauthn/RegistrationResult.html#isDiscoverable()[`RegistrationResult.isDiscoverable()`]
449
+ method can be used to determine whether the created credential is a passkey.
450
+ This requires the
451
+ link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.4.1/com/yubico/webauthn/data/RegistrationExtensionInputs.RegistrationExtensionInputsBuilder.html#credProps()[`credProps` extension],
452
+ to be enabled, which it is by default.
421
453
422
- User verification can be enforced independently per authentication ceremony:
454
+
455
+ [#user-verification]
456
+ ==== User verification: passwordless multi-factor authentication
457
+
458
+ link:https://passkeys.dev/docs/reference/terms/#user-verification-uv[User verification]
459
+ can be enforced independently per authentication ceremony:
423
460
424
461
[source,java]
425
462
----------
@@ -448,6 +485,54 @@ PublicKeyCredentialCreationOptions request = rp.startRegistration(
448
485
.build());
449
486
----------
450
487
488
+ User verification can be used with both discoverable credentials (passkeys) and non-discoverable credentials.
489
+
490
+
491
+ [#autofill-ui]
492
+ ==== Using passkeys with autofill UI
493
+
494
+ Passkeys on platform authenticators may also support the WebAuthn
495
+ link:https://passkeys.dev/docs/reference/terms/#autofill-ui[autofill UI], also known as "conditional mediation".
496
+ This can help onboard users who are unfamiliar with a fully username-less login flow,
497
+ allowing a familiar username input field to opportunistically offer a shortcut using a passkey
498
+ if the user has one on their device.
499
+
500
+ This library is compatible with the autofill UI but provides no server-side options for it,
501
+ because the steps to enable it are taken on the front-end side.
502
+ Using autofill UI does not affect the response verification procedure.
503
+
504
+ See the link:https://passkeys.dev/docs/use-cases/bootstrapping/[guide on passkeys.dev]
505
+ for complete instructions on how to enable the autofill UI.
506
+ In particular you need to:
507
+
508
+ - Add the credential request option `mediation: "conditional"`
509
+ alongside the `publicKey` option generated by
510
+ link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.4.1/com/yubico/webauthn/RelyingParty.html#startAssertion(com.yubico.webauthn.StartAssertionOptions)[`RelyingParty.startAssertion(...)`],
511
+ - Add `autocomplete="username webauthn"` to a username input field on the page, and
512
+ - Call `navigator.credentials.get()` in the background.
513
+
514
+ If the Promise resolves, handle it like any other assertion response as described in
515
+ <<getting-started-authentication>> above.
516
+
517
+ Because of technical limitations, autofill UI is as of May 2023 only supported for platform credentials,
518
+ i.e., passkeys stored on the user's computing devices.
519
+ Autofill UI might support passkeys on external security keys in the future.
520
+
521
+
522
+ [#credential-backup-state]
523
+ ==== Credential backup state
524
+
525
+ Some authenticators may allow credentials to be backed up and/or synced between devices.
526
+ This capability and its current state is signaled via the
527
+ link:https://w3c.github.io/webauthn/#sctn-credential-backup[Credential Backup State] flags,
528
+ which are available via the `isBackedUp()` and `isBackupEligible()` methods of
529
+ link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.4.1/com/yubico/webauthn/RegistrationResult.html[`RegistrationResult`]
530
+ and
531
+ link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.4.1/com/yubico/webauthn/AssertionResult.html[`AssertionResult`].
532
+ These can be used as a hint about how vulnerable a user is to authenticator loss.
533
+ In particular, a user with only one credential which is not backed up
534
+ may risk getting locked out if they lose their authenticator.
535
+
451
536
452
537
== Migrating from version `1.x`
453
538
0 commit comments