janko / rodauth-rails

Rails integration for Rodauth authentication framework
https://github.com/jeremyevans/rodauth
MIT License
584 stars 40 forks source link

Add Rodauth::Rails::Model for Active Record integration #45

Closed janko closed 3 years ago

janko commented 3 years ago

Currently, creating an account with a password programmatically is a bit cumbersome, mostly due to bcrypt-hashing, especially when the password is stored in a separate table. This adds friction in development and in tests, and also for anyone wanting to implement simple user creation by admins. Devise, Sorcery, and has_secure_password all provide this level of convenience.

Additionally, there are cases where it's useful to be able to directly query associated tables (e.g. retrieving all accounts that have MFA enabled), and it might not be obvious how to declare associations, e.g:

class Account < ApplicationRecord
  has_one :remember_key, foreign_key: :id
end

class Account::RememberKey < ApplicationRecord
end

To address this friction, we add a Rodauth::Rails::Model mixin that can be be included into the account model:

class Account < ApplicationRecord
  include Rodauth::Rails.model
end

It provides password attribute and validations (works for both password hash column and password hash table):

account = Account.new(email: "user@example.com", password: "foo")
account.password_hash #=> "$2a$12$k/Ub1I2iomi84Rac..."
account.valid? #=> false
account.errors[:password] #=> ["invalid password, does not meet requirements (minimum 6 characters)"]

And login validations:

account = Account.new(password: "secret", email: "foo")
account.valid? #=> false
account.errors[:email] #=> ["invalid login, not a valid email address"]

It also defines associations based on the Rodauth configuration:

account.remember_key #=> #<Account::RememberKey>
account.active_session_keys #=> [#<Account::ActiveSessionKey>, ...]

Alternative configurations are supported as well:

class Admin < ApplicationRecord
  include Rodauth::Rails.model(:admin)
end

Admin::RememberKey # can reference `admin_remember_keys`
janko commented 3 years ago

I've been working on this on-and-off for about a month now, but I'm still not sure whether it would make sense to merge it. It might be dangerous to bypass Rodauth hooks like this; for example, audit logs wouldn't be created when creating accounts this way.

For now I'll leave it in the pull request state, so that others know this direction exists, and are able to provide feedback.

janko commented 3 years ago

I would like to keep the password attribute and associations, but I've decided to remove the validations.

I think it's useful to be able to easily create accounts in the development console or in tests, but having validations would encourage people to build forms around the account model, which is discouraged because it doesn't execute hooks and other things. The most common use case for this is an admin that can perform authentication operations on behalf of other users, but for this the upcoming internal_request feature should be used.

The associations are also really convenient for querying, and I think it's obvious that modifying them directly is at own risk.