@@ -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
@@ -44,7 +44,7 @@ toc::[]
44
44
- Optionally integrates with an "attestation trust source" to verify
45
45
https://www.w3.org/TR/webauthn/#sctn-attestation[authenticator attestations]
46
46
- Reproducible builds: release signatures match fresh builds from source. See
47
- link:# reproducible-builds[Reproducible builds] below.
47
+ << reproducible-builds>> below.
48
48
49
49
50
50
=== Non-features
@@ -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:
@@ -374,26 +377,52 @@ Most importantly, you should update the signature counter. That might look somet
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
395
- 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
- option:
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
423
+ link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.4.1/com/yubico/webauthn/StartRegistrationOptions.StartRegistrationOptionsBuilder.html#authenticatorSelection(com.yubico.webauthn.data.AuthenticatorSelectionCriteria)[`authenticatorSelection`].link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.4.1/com/yubico/webauthn/data/AuthenticatorSelectionCriteria.AuthenticatorSelectionCriteriaBuilder.html#residentKey(com.yubico.webauthn.data.ResidentKeyRequirement)[`residentKey`]
424
+ option to
425
+ link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.4.1/com/yubico/webauthn/data/ResidentKeyRequirement.html#REQUIRED[`REQUIRED`]:
397
426
398
427
[source,java]
399
428
----------
@@ -413,13 +442,25 @@ The username can then be omitted when starting an authentication ceremony:
413
442
AssertionRequest request = rp.startAssertion(StartAssertionOptions.builder().build());
414
443
----------
415
444
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.
445
+ Some authenticators might create passkeys even if not required, and setting
446
+ the
447
+ link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.4.1/com/yubico/webauthn/data/AuthenticatorSelectionCriteria.AuthenticatorSelectionCriteriaBuilder.html#residentKey(com.yubico.webauthn.data.ResidentKeyRequirement)[`residentKey`]
448
+ option to
449
+ link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.4.1/com/yubico/webauthn/data/ResidentKeyRequirement.html#PREFERRED[`PREFERRED`]
450
+ will create a passkey if the authenticator supports it.
451
+ The
452
+ link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.4.1/com/yubico/webauthn/RegistrationResult.html#isDiscoverable()[`RegistrationResult.isDiscoverable()`]
453
+ method can be used to determine whether the created credential is a passkey.
454
+ This requires the
455
+ 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]
456
+ to be enabled, which it is by default.
421
457
422
- User verification can be enforced independently per authentication ceremony:
458
+
459
+ [#user-verification]
460
+ ==== User verification: passwordless multi-factor authentication
461
+
462
+ link:https://passkeys.dev/docs/reference/terms/#user-verification-uv[User verification]
463
+ can be enforced independently per authentication ceremony:
423
464
424
465
[source,java]
425
466
----------
@@ -448,6 +489,54 @@ PublicKeyCredentialCreationOptions request = rp.startRegistration(
448
489
.build());
449
490
----------
450
491
492
+ User verification can be used with both discoverable credentials (passkeys) and non-discoverable credentials.
493
+
494
+
495
+ [#autofill-ui]
496
+ ==== Using passkeys with autofill UI
497
+
498
+ Passkeys on platform authenticators may also support the WebAuthn
499
+ link:https://passkeys.dev/docs/reference/terms/#autofill-ui[autofill UI], also known as "conditional mediation".
500
+ This can help onboard users who are unfamiliar with a fully username-less login flow,
501
+ allowing a familiar username input field to opportunistically offer a shortcut using a passkey
502
+ if the user has one on their device.
503
+
504
+ This library is compatible with the autofill UI but provides no server-side options for it,
505
+ because the steps to enable it are taken on the front-end side.
506
+ Using autofill UI does not affect the response verification procedure.
507
+
508
+ See the link:https://passkeys.dev/docs/use-cases/bootstrapping/[guide on passkeys.dev]
509
+ for complete instructions on how to enable the autofill UI.
510
+ In particular you need to:
511
+
512
+ - Add the credential request option `mediation: "conditional"`
513
+ alongside the `publicKey` option generated by
514
+ 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(...)`],
515
+ - Add `autocomplete="username webauthn"` to a username input field on the page, and
516
+ - Call `navigator.credentials.get()` in the background.
517
+
518
+ If the Promise resolves, handle it like any other assertion response as described in
519
+ <<getting-started-authentication>> above.
520
+
521
+ Because of technical limitations, autofill UI is as of May 2023 only supported for platform credentials,
522
+ i.e., passkeys stored on the user's computing devices.
523
+ Autofill UI might support passkeys on external security keys in the future.
524
+
525
+
526
+ [#credential-backup-state]
527
+ ==== Credential backup state
528
+
529
+ Some authenticators may allow credentials to be backed up and/or synced between devices.
530
+ This capability and its current state is signaled via the
531
+ link:https://w3c.github.io/webauthn/#sctn-credential-backup[Credential Backup State] flags,
532
+ which are available via the `isBackedUp()` and `isBackupEligible()` methods of
533
+ link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.4.1/com/yubico/webauthn/RegistrationResult.html[`RegistrationResult`]
534
+ and
535
+ link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.4.1/com/yubico/webauthn/AssertionResult.html[`AssertionResult`].
536
+ These can be used as a hint about how vulnerable a user is to authenticator loss.
537
+ In particular, a user with only one credential which is not backed up
538
+ may risk getting locked out if they lose their authenticator.
539
+
451
540
452
541
== Migrating from version `1.x`
453
542
@@ -689,6 +778,7 @@ $ ./gradlew pitest
689
778
----------
690
779
691
780
781
+ [#reproducible-builds]
692
782
=== Reproducible builds
693
783
Starting in version `1.4.0-RC2`, artifacts are built reproducibly. Fresh builds from
694
784
tagged commits should therefore be verifiable by signatures from Maven Central
0 commit comments