Skip to content

Devise extension to use passkeys instead of passwords

License

Notifications You must be signed in to change notification settings

htcarr3/devise-passkeys

 
 

Repository files navigation

Devise::Passkeys

This Devise extension allows you to use passkeys instead of passwords for user authentication.

Devise::Passkeys is lightweight and non-configurable. It does what it has to do and leaves some manual implementation to you.

Installation

Add this line to your application's Gemfile:

gem 'devise-passkeys'

And then execute:

$ bundle

Usage

  1. Add :passkey_authenticatable in your Devise-enabled model
class User < ApplicationRecord
  devise :passkey_authenticatable, ...

  has_many :passkeys

  def self.passkeys_class
    Passkey
  end

  def self.find_for_passkey(passkey)
    self.find_by(id: passkey.user.id)
  end

  def after_passkey_authentication
  end
end

The Devise-enabled model must have a webauthn_id field in the model; which is:

  • A string
  • Has a unique index

This will allow you to explictly establish the relationship between a user & its passkeys (to help both your app & the user's authenticator with credential management)

  1. Generate the model that will store passkeys. The model name is not important, but the Devise-enabled model should have:
  • A has_many :passkeys association
  • A passkey_class class method that returns the passkey class
  • A find_for_passkey(passkey) class method that finds the user for a given passkey
rails g model Passkey user:references label:string external_id:string:index:uniq public_key:string:index sign_count:integer last_used_at:datetime

The following fields are required:

  • label:string (required, cannot be blank you'll want to scope it to the Devise-enabled model)
  • external_id:string
  • public_key:string
  • sign_count:integer
  • last_used_at:datetime

It's recommended to add unique indexes on external_id and public_key

  1. Generate custom devise controllers & views for your Devise-enabled model

Since Devise does not have built-in passkeys support yet, you'll need to customize both the controllers & the views

rails generate devise:controllers users
rails generate devise:views users

If you're trying to keep your codebase small, these instructions only concern the Users::SessionsController & Users::RegistrationsController, so you can delete any other generated custom controllers if needed. You will likely need to modify the views/users/shared/* partials though, because they assume passwords are being used.

  1. Include the passkeys concerns into your controllers

Rather than having base classes, Devise::Passkeys has a series of concerns that can be mixed into your controllers. This allows you to change behavior, and does not keep you stuck down a path that could be incompatible with your existing authentication setup.

Here are examples of common controllers

class Users::RegistrationsController < Devise::RegistrationsController
  include Devise::Passkeys::Controllers::RegistrationsControllerConcern
end


class Users::SessionsController < Devise::SessionsController
  include Devise::Passkeys::Controllers::SessionsControllerConcern
  # ... any custom code you need

  def relying_party
     WebAuthn::RelyingParty.new(...)
  end

  def set_relying_party_in_request_env
    request.env[relying_party_key] = relying_party
  end
end

# frozen_string_literal: true

class Users::ReauthenticationController < DeviseController
  include Devise::Passkeys::Controllers::ReauthenticationControllerConcern
  # ... any custom code you need

  def relying_party
     WebAuthn::RelyingParty.new(...)
  end

  def set_relying_party_in_request_env
    request.env[relying_party_key] = relying_party
  end
end

# frozen_string_literal: true

class Users::PasskeysController < DeviseController
  include Devise::Passkeys::Controllers::PasskeysControllerConcern
  # ... any custom code you need

  def relying_party
     WebAuthn::RelyingParty.new(...)
  end

  def set_relying_party_in_request_env
    request.env[relying_party_key] = relying_party
  end
end
  1. Add necessary routes

Given the customization routes usually require, you'll need to hook up the routes yourself. Here's an example:

devise_for :users, controllers: {
  registrations: 'users/registrations',
  sessions: 'users/sessions'
}

devise_scope :user do
  post 'sign_up/new_challenge', to: 'users/registrations#new_challenge', as: :new_user_registration_challenge
  post 'sign_in/new_challenge', to: 'users/sessions#new_challenge', as: :new_user_session_challenge

  post 'reauthenticate/new_challenge', to: 'users/reauthentication#new_challenge', as: :new_user_reauthentication_challenge
  post 'reauthenticate', to: 'users/reauthentication#reauthenticate', as: :user_reauthentication

  namespace :users do
    resources :passkeys, only: [:index, :create, :destroy] do
      collection do
        post :new_create_challenge
      end

      member do
        post :new_destroy_challenge
      end
    end
  end
end

What about the Webauthn javascript? Mailers? Error handling?

You will have to implement these, since Devise::Passkeys is focused on the authentication handshakes, and each app is different (with different javascript setups, mailer needs, etc.)

I need to see it in action

Here's a template repo! https://github.com/ruby-passkeys/devise-passkeys-template

Development

After checking out the repo, run bin/setup to install dependencies. Then, run rake test to run the tests. You can also run bin/console for an interactive prompt that will allow you to experiment.

To install this gem onto your local machine, run bundle exec rake install. To release a new version, update the version number in version.rb, and then run bundle exec rake release, which will create a git tag for the version, push git commits and the created tag, and push the .gem file to rubygems.org.

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/devise-passkeys. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the code of conduct.

License

The gem is available as open source under the terms of the MIT License.

Code of Conduct

Everyone interacting in the Devise::Passkeys project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the code of conduct.

Acknowledgements

This work is based on Petr Hlavicka's webauthn-with-devise.

The ethos of the library is inspired from Tiddle's straightforward, minimally-scoped approach.

About

Devise extension to use passkeys instead of passwords

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • Ruby 97.8%
  • HTML 2.1%
  • Shell 0.1%