passwordless icon indicating copy to clipboard operation
passwordless copied to clipboard

Feature Request: Constraint helpers for `config/routes.rb`

Open henrikbjorn opened this issue 1 year ago • 6 comments

When using devise they have helpers that can be used in config/routes.rb like:

authenticated :admin do
  root to: 'admin/dashboard#show', as: :admin_root
end

Which i personally like very much and better than doing require_user! in my controllers.

Would be a awesome addition to passwordless.

Not sure how it would work with Passwordless as most the user finding and authorizing etc. is in the ControllerHelper where Devise calls Warden e.g: https://github.com/heartcombo/devise/blob/main/lib/devise/rails/routes.rb#L477

henrikbjorn avatar May 25 '23 07:05 henrikbjorn

Good question! PRs welcome ❤️

mikker avatar May 25 '23 07:05 mikker

on a custom app that I built, something like this works for me:

# app/constraints/user_constraint.rb
class UserConstraint
  def initialize(&block)
    @block = block
  end

  def matches?(request)
    user = current_user(request)
    user.present? && @block.call(user)
  end

  def current_user(request)
    User.find_by(id: request.session[:user_id])
  end
end
# config/routes.rb
  # authenticate :user, ->(user) { user.admin? } do # <- devise syntax
  constraints UserConstraint.new { |user| user.admin? } do
    mount GoodJob::Engine, at: "good_job"
    mount Avo::Engine, at: Avo.configuration.root_path
  end

  # authenticated :user do # <- devise syntax
  constraints UserConstraint.new { |user| user.present? } do
    get 'dashboard', to: 'static#dashboard'
  end

yshmarov avatar Oct 27 '23 14:10 yshmarov

Similarly to the above, but the following works for me with Passwordless:

# app/constraints/user_constraint.rb
class PasswordlessConstraint
  include Passwordless::ControllerHelpers

  attr_reader :authenticatable_type, :session, :lambda

  def initialize(authenticatable_type, lambda)
    @authenticatable_type = authenticatable_type
    @lambda = lambda
  end

  def matches?(request)
    @session = request.session
    authenticatable = authenticate_by_session(authenticatable_type)
    authenticatable.present? && lambda.call(authenticatable)
  end
end
# config/routes.rb
constraints PasswordlessConstraint.new(User, ->(user) { user.has_role?("admin") }) do
  root to: 'admin/dashboard#show', as: :admin_root
end

The rest is syntactic sugar, but a bit too much magic.

tdegrunt avatar Apr 30 '24 18:04 tdegrunt

@tdegrunt That's amazing! Would love to include it if you're up for creating a PR with a test and some docs?

mikker avatar Apr 30 '24 21:04 mikker

Will this re-query the database for the user?

We might want to think about using env as warden does.

henrikbjorn avatar May 01 '24 05:05 henrikbjorn

Took me a while, been on holiday, but added a PR for this.

tdegrunt avatar May 23 '24 12:05 tdegrunt