janko / rodauth-rails

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

Disable rodauth in specific routes #208

Closed gaizkaeu closed 1 year ago

gaizkaeu commented 1 year ago

Hello.

I'm using Rodauth with the jwt plugin enabled, it works wonders. However, I'm having some trouble with non-authenticated routes.

Cloudtasker sends a POST request to /cloudtasker/run with an Authentication Header (including Bearer word). The problem is that Rodauth is intercepting that request and throwing an error displaying that the JWT is invalid.

Without the JWT plugin enabled works fine.

Is there any way to disable rodauth for that specific path?

Thanks in advance.

janko commented 1 year ago

Hi, what's intercepting your request is probably rodauth.require_authentication being called somewhere during that request, either in your Rodauth app's route block or a before filter in your controller. If you're not calling Rodauth during the request, it should not attempt to read the Authorization header.

If you think Rodauth isn't being called, could you produce a self-contained example reproducing the problem? Here is a Roda example showing the Authorization header being accepted:

require "roda"
require "sequel"
require "rack/test"

DB = Sequel.sqlite

class App < Roda
  plugin :rodauth do
    enable :jwt
  end

  route do |r|
    r.rodauth

    r.post "custom" do
      "Custom"
    end
  end
end

session = Rack::Test::Session.new(App)
session.post("/custom", {}, "HTTP_AUTHORIZATION" => "Bearer foobar")
session.last_response.body #=> "Custom"
gaizkaeu commented 1 year ago

Hello. Thank you for your reply. CloudTasker isn't calling rodauth.require_authentcation (as far as I can tell), but I have narrowed the problem to rodauth.load_memory

class RodauthApp < Rodauth::Rails::App
  # primary configuration
  configure RodauthMain

  # secondary configuration
  # configure RodauthAdmin, :admin
  route do |r|
    rodauth.load_memory # autologin remembered users

    r.rodauth # route rodauth requests
  end
end

OUTPUT with load_memory

Started POST "/cloudtasker/run" for ::1 at 2023-05-17 18:17:39 +0200
Started POST "/cloudtasker/run" for ::1 at 2023-05-17 18:17:50 +0200
Started POST "/cloudtasker/run" for ::1 at 2023-05-17 18:17:58 +0200
Started POST "/cloudtasker/run" for ::1 at 2023-05-17 18:17:59 +0200

OUTPUT without load_memory working

Processing by Cloudtasker::WorkerController#run as */*
  Parameters: {"worker"=>"CalcrTrainJob", "job_queue"=>"default", "job_id"=>"a6fca894-1b98-457f-9468-b380b91f091c", "job_meta"=>{}, "job_args"=>["calcr_W7lQWe1MRcPI"]}
[ahoy] Visit excluded
[Cloudtasker][CalcrTrainJob][a6fca894-1b98-457f-9468-b380b91f091c] Starting job... -- {:worker=>"CalcrTrainJob", :job_id=>"a6fca894-1b98-457f-9468-b380b91f091c", :job_meta=>{}, :job_queue=>"default", :task_id=>"b0d1527f-585d-4b3b-9cad-20051ce81f61"}
[Cloudtasker][CalcrTrainJob][a6fca894-1b98-457f-9468-b380b91f091c] Job failed after 0.013s -- {:worker=>"CalcrTrainJob", :job_id=>"a6fca894-1b98-457f-9468-b380b91f091c", :job_meta=>{}, :job_queue=>"default", :task_id=>"b0d1527f-585d-4b3b-9cad-20051ce81f61", :duration=>0.013}
janko commented 1 year ago

If you have a route that's a webhook with its own authentication, you don't want to call any Rodauth methods in that request. The Rodauth app's route block is called for each request before it reaches the Rails router, so can easily skip things for /cloudtasker/* routes:

route do |r|
  rodauth.load_memory unless r.path.start_with?("/cloudtasker")

  r.rodauth
end

Or maybe even skip the whole route block:

route do |r|
  next if r.path.start_with?("/cloudtasker")

  rodauth.load_memory

  r.rodauth
end