From db3fa324e26bcac2267bfe56f8b9e9992e58fb39 Mon Sep 17 00:00:00 2001 From: Tsukuru Tanimichi Date: Sat, 27 Jun 2026 22:41:29 +0900 Subject: [PATCH 1/7] Add authenticator_selection to options_for_create examples Most modern platform authenticators and passkey managers already support both Discoverable Credentials and user verification. Wouldn't explicitly requiring them in options_for_create lead to a simpler and more straightforward authentication flow? --- README.md | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 9fb0d6cd..37ce0a3e 100644 --- a/README.md +++ b/README.md @@ -176,7 +176,11 @@ end options = WebAuthn::Credential.options_for_create( user: { id: user.webauthn_id, name: user.name }, - exclude: user.credentials.map { |c| c.webauthn_id } + exclude: user.credentials.map { |c| c.webauthn_id }, + authenticator_selection: { + resident_key: "required", + user_verification: "required" + } ) # Store the newly generated challenge somewhere so you can have it @@ -204,7 +208,7 @@ session[:creation_challenge] = options.challenge webauthn_credential = WebAuthn::Credential.from_create(params[:publicKeyCredential]) begin - webauthn_credential.verify(session[:creation_challenge]) + webauthn_credential.verify(session[:creation_challenge], user_verification: true) # Store Credential ID, Credential Public Key and Sign Count for future authentications user.credentials.create!( @@ -290,7 +294,11 @@ Extensions can be requested in the initiation phase in both Credential Registrat creation_options = WebAuthn::Credential.options_for_create( user: { id: user.webauthn_id, name: user.name }, exclude: user.credentials.map { |c| c.webauthn_id }, - extensions: { appidExclude: domain.to_s } + extensions: { appidExclude: domain.to_s }, + authenticator_selection: { + resident_key: "required", + user_verification: "required" + } ) # OR @@ -342,8 +350,12 @@ to be used in the client-side code to call `navigator.credentials.create({ "publ ```ruby creation_options = WebAuthn::Credential.options_for_create( - user: { id: user.webauthn_id, name: user.name } - exclude: user.credentials.map { |c| c.webauthn_id } + user: { id: user.webauthn_id, name: user.name }, + exclude: user.credentials.map { |c| c.webauthn_id }, + authenticator_selection: { + resident_key: "required", + user_verification: "required" + } ) # Store the newly generated challenge somewhere so you can have it From 2da9950ecdb9a62ee4e4b42f992f8486920c8111 Mon Sep 17 00:00:00 2001 From: Tsukuru Tanimichi Date: Sun, 28 Jun 2026 19:17:51 +0900 Subject: [PATCH 2/7] Add user_verification to authentication flow examples --- README.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 37ce0a3e..c69deb6a 100644 --- a/README.md +++ b/README.md @@ -228,7 +228,10 @@ end #### Initiation phase ```ruby -options = WebAuthn::Credential.options_for_get(allow: user.credentials.map { |c| c.webauthn_id }) +options = WebAuthn::Credential.options_for_get( + allow: user.credentials.map { |c| c.webauthn_id }, + user_verification: "required" +) # Store the newly generated challenge somewhere so you can have it # for the verification phase. @@ -264,7 +267,8 @@ begin webauthn_credential.verify( session[:authentication_challenge], public_key: stored_credential.public_key, - sign_count: stored_credential.sign_count + sign_count: stored_credential.sign_count, + user_verification: true ) # Update the stored credential sign count with the value from `webauthn_credential.sign_count` @@ -351,11 +355,7 @@ to be used in the client-side code to call `navigator.credentials.create({ "publ ```ruby creation_options = WebAuthn::Credential.options_for_create( user: { id: user.webauthn_id, name: user.name }, - exclude: user.credentials.map { |c| c.webauthn_id }, - authenticator_selection: { - resident_key: "required", - user_verification: "required" - } + exclude: user.credentials.map { |c| c.webauthn_id } ) # Store the newly generated challenge somewhere so you can have it From 77bb1a6dcfe6e5482885b0f40c181e3c87005a03 Mon Sep 17 00:00:00 2001 From: Tsukuru Tanimichi Date: Mon, 6 Jul 2026 12:50:26 +0900 Subject: [PATCH 3/7] Update README.md Co-authored-by: Santiago Rodriguez <46354312+santiagorodriguez96@users.noreply.github.com> --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index c69deb6a..401b6e7f 100644 --- a/README.md +++ b/README.md @@ -178,8 +178,8 @@ options = WebAuthn::Credential.options_for_create( user: { id: user.webauthn_id, name: user.name }, exclude: user.credentials.map { |c| c.webauthn_id }, authenticator_selection: { - resident_key: "required", - user_verification: "required" + resident_key: "discouraged", # For a passwordless login or 2FA. Use "required" for a passkey-based (passwordless and usernameless) login. + user_verification: "required" # For a passwordless or passkey-based (passwordless and usernameless) login. Use "discouraged" for 2FA. } ) From 963513b1b7a86c277c37b42503bcccf870cda92f Mon Sep 17 00:00:00 2001 From: Tsukuru Tanimichi Date: Mon, 6 Jul 2026 12:50:38 +0900 Subject: [PATCH 4/7] Update README.md Co-authored-by: Santiago Rodriguez <46354312+santiagorodriguez96@users.noreply.github.com> --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 401b6e7f..8e3fbfc6 100644 --- a/README.md +++ b/README.md @@ -208,6 +208,7 @@ session[:creation_challenge] = options.challenge webauthn_credential = WebAuthn::Credential.from_create(params[:publicKeyCredential]) begin + # Enforce user verification (pairs with the "required" request above). Omit for 2FA. webauthn_credential.verify(session[:creation_challenge], user_verification: true) # Store Credential ID, Credential Public Key and Sign Count for future authentications From bab9163291b9cc542ddcc21cdc01e80fbcaa7934 Mon Sep 17 00:00:00 2001 From: Tsukuru Tanimichi Date: Mon, 6 Jul 2026 12:51:00 +0900 Subject: [PATCH 5/7] Update README.md Co-authored-by: Santiago Rodriguez <46354312+santiagorodriguez96@users.noreply.github.com> --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 8e3fbfc6..2e3d2626 100644 --- a/README.md +++ b/README.md @@ -230,8 +230,10 @@ end ```ruby options = WebAuthn::Credential.options_for_get( + # Pass `allow` when the user is already known (passwordless/2FA/reauth). + # For a passkey-based (usernameless and passwordless) login, omit it and resolve the user from `user_handle` after `from_get`. allow: user.credentials.map { |c| c.webauthn_id }, - user_verification: "required" + user_verification: "required" # For a passwordless or passkey-based (passwordless and usernameless) login. Use "discouraged" for 2FA. ) # Store the newly generated challenge somewhere so you can have it From 72f3910030fd9cde7aa5481e3b68aff006cd6143 Mon Sep 17 00:00:00 2001 From: Tsukuru Tanimichi Date: Mon, 6 Jul 2026 12:51:11 +0900 Subject: [PATCH 6/7] Update README.md Co-authored-by: Santiago Rodriguez <46354312+santiagorodriguez96@users.noreply.github.com> --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2e3d2626..bc8ed352 100644 --- a/README.md +++ b/README.md @@ -271,7 +271,7 @@ begin session[:authentication_challenge], public_key: stored_credential.public_key, sign_count: stored_credential.sign_count, - user_verification: true + user_verification: true # For a passwordless or passkey-based (passwordless and usernameless) login. Omit for 2FA. ) # Update the stored credential sign count with the value from `webauthn_credential.sign_count` From 1f0bf236ef90d6904ad4776ef1f0b1fae7ea8018 Mon Sep 17 00:00:00 2001 From: Tsukuru Tanimichi Date: Mon, 6 Jul 2026 12:51:19 +0900 Subject: [PATCH 7/7] Update README.md Co-authored-by: Santiago Rodriguez <46354312+santiagorodriguez96@users.noreply.github.com> --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index bc8ed352..e4ecb29b 100644 --- a/README.md +++ b/README.md @@ -303,8 +303,8 @@ creation_options = WebAuthn::Credential.options_for_create( exclude: user.credentials.map { |c| c.webauthn_id }, extensions: { appidExclude: domain.to_s }, authenticator_selection: { - resident_key: "required", - user_verification: "required" + resident_key: "discouraged", # For a passwordless login or 2FA. Use "required" for a passkey-based (passwordless and usernameless) login. + user_verification: "required" # For a passwordless or passkey-based (passwordless and usernameless) login. Use "discouraged" for 2FA. } )