Module: Devise::Passkeys::Controllers::RegistrationsControllerConcern
- Extended by:
- ActiveSupport::Concern
- Defined in:
- lib/devise/passkeys/controllers/registrations_controller_concern.rb
Overview
This concern should be included in any controller that handles
user (resource
) registration management (ie: signup/deleting an account),
and defines:
- Useful methods and before filters to streamline user (
resource
) registration management using session variables - Controller actions for:
- Issuing a new WebAuthn challenge
- A
create
action that creates a passkey if the user (resource
) has been persisted
- Helper modules from
Warden::WebAuthn
that are required to complete the registration process
The registration_user_id_key
and registration_challenge_key
are defined
using the resource_name
, to keep the generated IDs unique between resources
during the registration process.
A raw_credential
method is provided to streamline access to
passkey_params[:passkey_credential]
.
Note: the implementing controller must define a relying_party
method in order for
registrations to work.
Instance Method Summary collapse
-
#configure_sign_up_params ⇒ Object
Adds the generated WebAuthn User ID to
devise_parameter_sanitizer
's permitted keys. -
#create ⇒ Object
This controller action creates a new user (
resource
), using the given email & passkey. -
#create_passkey(resource:) ⇒ Object
Generates a passkey for the given
resource
, using theresource.passkeys.create!
method with the following attributes:. -
#create_passkey_for_resource(resource:) {|resource, passkey| ... } ⇒ Object
Creates a passkey for given user (
resource
). -
#exclude_external_ids_for_registration ⇒ Object
Override this method if you need to exclude certain WebAuthn credentials from a registration request.
-
#new_challenge ⇒ Object
This controller action issues a new challenge for the registration handshake.
-
#passkey_params ⇒ Object
The subset of parameters used when verifying the passkey.
-
#reauthentication_params ⇒ Object
The subset of parameters used when verifying a reauthentication_token.
-
#require_email_and_passkey_label ⇒ Object
Verifies that the
sign_up_params
has an:email
and:passkey_label
. -
#update_resource(resource, params) ⇒ Object
An override of
DeviseController
's implementation, to circumvent theupdate_with_password
method. -
#user_details_for_registration ⇒ Object
Prepares the user details for a WebAuthn registration request.
-
#verify_passkey_registration_challenge ⇒ Object
Verifies the registration challenge is correct.
-
#verify_reauthentication_token ⇒ Object
Verifies that the given reauthentication token matches the expected value stored in the session.
Instance Method Details
#configure_sign_up_params ⇒ Object
Adds the generated WebAuthn User ID to devise_parameter_sanitizer
's permitted keys
252 253 254 255 |
# File 'lib/devise/passkeys/controllers/registrations_controller_concern.rb', line 252 def configure_sign_up_params params[resource_name][:webauthn_id] = registration_user_id devise_parameter_sanitizer.permit(:sign_up, keys: [:webauthn_id]) end |
#create ⇒ Object
This controller action creates a new user (resource
), using the given
email & passkey. It:
- calls the parent class's
#create
method - calls
#create_passkey_for_resource
to finish creating the passkey if the user (resource
) was actually persisted - Finishes the rest of the parent class's
#create
method
The following before actions are called:
require_email_and_passkey_label
verify_passkey_registration_challenge
configure_sign_up_params
110 111 112 113 114 |
# File 'lib/devise/passkeys/controllers/registrations_controller_concern.rb', line 110 def create super do |resource| create_passkey_for_resource(resource: resource) end end |
#create_passkey(resource:) ⇒ Object
Generates a passkey for the given resource
, using the resource.passkeys.create!
method with the following attributes:
label
: Thepasskey_params[:passkey_label]
public_key
: The@webauthn_credential.public_key
external_id
: The credential ID, strictly encoded as a Base 64 stringsign_count
: The@webauthn_credential.sign_count
last_used_at
: The current time, since this is the first time the passkey is being used
152 153 154 155 156 157 158 159 160 |
# File 'lib/devise/passkeys/controllers/registrations_controller_concern.rb', line 152 def create_passkey(resource:) resource.passkeys.create!( label: passkey_params[:passkey_label], public_key: @webauthn_credential.public_key, external_id: Base64.strict_encode64(@webauthn_credential.raw_id), sign_count: @webauthn_credential.sign_count, last_used_at: Time.now.utc ) end |
#create_passkey_for_resource(resource:) {|resource, passkey| ... } ⇒ Object
Creates a passkey for given user (resource
).
The method tests that the user (resource
) is in the database
before saving the passkey for the given user (resource
).
This method also ensures that the generated WebAuthn User ID is deleted from the session to prevent data leaks.
132 133 134 135 136 137 138 139 |
# File 'lib/devise/passkeys/controllers/registrations_controller_concern.rb', line 132 def create_passkey_for_resource(resource:) return unless resource.persisted? passkey = create_passkey(resource: resource) yield [resource, passkey] if block_given? delete_registration_user_id! end |
#exclude_external_ids_for_registration ⇒ Object
Override this method if you need to exclude certain WebAuthn credentials from a registration request.
200 201 202 |
# File 'lib/devise/passkeys/controllers/registrations_controller_concern.rb', line 200 def exclude_external_ids_for_registration [] end |
#new_challenge ⇒ Object
This controller action issues a new challenge for the registration handshake.
The challenge is stored in a session variable, and renders the WebAuthn registration options as a JSON response.
The following before filters are called:
require_no_authentication
require_email_and_passkey_label
79 80 81 82 83 84 85 86 87 88 89 |
# File 'lib/devise/passkeys/controllers/registrations_controller_concern.rb', line 79 def new_challenge = ( relying_party: , user_details: user_details_for_registration, exclude: exclude_external_ids_for_registration ) store_challenge_in_session(options_for_registration: ) render json: end |
#passkey_params ⇒ Object
The subset of parameters used when verifying the passkey
206 207 208 |
# File 'lib/devise/passkeys/controllers/registrations_controller_concern.rb', line 206 def passkey_params params.require(resource_name).permit(:passkey_label, :passkey_credential) end |
#reauthentication_params ⇒ Object
The subset of parameters used when verifying a reauthentication_token
183 184 185 |
# File 'lib/devise/passkeys/controllers/registrations_controller_concern.rb', line 183 def reauthentication_params params.require(resource_name).permit(:reauthentication_token) end |
#require_email_and_passkey_label ⇒ Object
Verifies that the sign_up_params
has an :email
and :passkey_label
.
If either is missing or blank, a 400 Bad Request
JSON response is rendered.
217 218 219 220 221 222 223 224 225 226 227 228 229 |
# File 'lib/devise/passkeys/controllers/registrations_controller_concern.rb', line 217 def require_email_and_passkey_label if sign_up_params[:email].blank? render json: { message: (:email_missing) }, status: :bad_request return false end if passkey_params[:passkey_label].blank? render json: { message: (:passkey_label_missing) }, status: :bad_request return false end true end |
#update_resource(resource, params) ⇒ Object
An override of DeviseController
's implementation, to circumvent the
update_with_password
method
191 192 193 |
# File 'lib/devise/passkeys/controllers/registrations_controller_concern.rb', line 191 def update_resource(resource, params) resource.update(params) end |
#user_details_for_registration ⇒ Object
Prepares the user details for a WebAuthn registration request
261 262 263 264 |
# File 'lib/devise/passkeys/controllers/registrations_controller_concern.rb', line 261 def user_details_for_registration store_registration_user_id { id: registration_user_id, name: sign_up_params[:email] } end |
#verify_passkey_registration_challenge ⇒ Object
Verifies the registration challenge is correct.
If the challenge failed, a 400 Bad Request
JSON
response is rendered.
243 244 245 246 247 248 |
# File 'lib/devise/passkeys/controllers/registrations_controller_concern.rb', line 243 def verify_passkey_registration_challenge @webauthn_credential = verify_registration(relying_party: ) rescue ::WebAuthn::Error => e error_key = Warden::WebAuthn::ErrorKeyFinder.webauthn_error_key(exception: e) render json: { message: (error_key) }, status: :bad_request end |
#verify_reauthentication_token ⇒ Object
Verifies that the given reauthentication token matches the expected value stored in the session.
If the reauthentication token is not valid,
a 400 Bad Request
JSON response is rendered.
175 176 177 178 179 |
# File 'lib/devise/passkeys/controllers/registrations_controller_concern.rb', line 175 def verify_reauthentication_token return if valid_reauthentication_token?(given_reauthentication_token: reauthentication_params[:reauthentication_token]) render json: { error: (:not_reauthenticated) }, status: :bad_request end |