How can I disable csrf for /login?

102 views
Skip to first unread message

Jon WIlkes

unread,
Aug 2, 2021, 6:43:44 PM8/2/21
to Rodauth
I would like to expose /login to prosody, an xmpp chat server.  However, I don't see how to disable csrf.  In the old days, one could accomplish this like so:

    plugin :csrf, :skip_if => lambda{|req| req.env['CONTENT_TYPE'] =~ /application\/json/}

Is there some way to do that nowadays when using rodauth with the route_csrf plugin?

Jeremy Evans

unread,
Aug 2, 2021, 7:20:06 PM8/2/21
to rod...@googlegroups.com
With Rodauth's default use of the route_csrf plugin, you could just configure Rodauth to not check the csrf for the login route. Something like this may work:

  check_csrf?{request.remaining_path == "/login" ? false : super()}
 
Thanks,
Jeremy

--
You received this message because you are subscribed to the Google Groups "Rodauth" group.
To unsubscribe from this group and stop receiving emails from it, send an email to rodauth+u...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/rodauth/18f4501d-7045-43c5-94c6-8b5602734711n%40googlegroups.com.

Jon WIlkes

unread,
Aug 4, 2021, 5:05:58 PM8/4/21
to Rodauth
Thanks!  It works, with a little modification (path, not remaining_path).

A working example is copied below.  But, to get it to work, I had to use the json_parser plugin.  I don't ordinarily use that roda plugin because one of my routes wants to parse the json request body without symbolizing keys, while the rest of the routes in my application want the json body parsed *with* symbolizing keys.

Is the requirement for the json_parser plugin by design or by accident?
 
Thanks,
Jon

require 'roda'
require 'rodauth'
require 'sequel'

DB = Sequel.postgres('test') unless defined?(DB)

class App < Roda
  # The json plugin must be included to avoid:
  #
  #   NoMethodError: undefined method `convert_to_json' for #<App::RodaRequest POST /login>
  #
  # Is this a bug?
  plugin :json

  # The json_parser plugin must be included to avoid failure to login:
  #
  #    {"field-error":["login","no matching login"],"error":"There was an error logging in"}
  plugin :json_parser

  plugin :rodauth do
    # The :login and :json features must be enabled to get
    # application/json back after requesting "/login".
    enable(:login, :json)

    # csrf checking must be disabled on '/login' for json-based
    # requests to avoid
    #
    #   Roda::RodaPlugins::RouteCsrf::InvalidToken: encoded token is not a string
    check_csrf? { '/login' == request.path ? false : super() }
  end

  plugin(:sessions, secret: (0..64).to_a.join(''))

  route do |r|
    r.rodauth
  end
end

Jeremy Evans

unread,
Aug 4, 2021, 5:21:38 PM8/4/21
to rod...@googlegroups.com
On Wed, Aug 4, 2021 at 2:06 PM Jon WIlkes <jon.j...@gmail.com> wrote:
Thanks!  It works, with a little modification (path, not remaining_path).

A working example is copied below.  But, to get it to work, I had to use the json_parser plugin.  I don't ordinarily use that roda plugin because one of my routes wants to parse the json request body without symbolizing keys, while the rest of the routes in my application want the json body parsed *with* symbolizing keys.

Is the requirement for the json_parser plugin by design or by accident? 

You didn't provide the json: true option when loading the rodauth plugin, so Rodauth didn't load the json/json_parser plugins.  If you pass that option, Rodauth will automatically load the json/json_parser plugins and you won't need to load them manually.

Not sure exactly what the issue is with symbolizing keys, could you provide more detail or some example code/output?  The json_parser plugin should not symbolize keys by default, since it just calls JSON.parse by default.

Thanks,
Jeremy

Jon WIlkes

unread,
Aug 4, 2021, 5:53:14 PM8/4/21
to Rodauth
I loaded the plugins explicitly (rather than using :json => true) for clarity.

As for avoiding json_parser, there are 3 reasons I've disabled it.

1. Some of my application code that processes json request bodies assumes hashes with symbolized keys.  That is to say, I *want* symbolized keys most of the time.  Such code could be converted to use string keys, but until now, I've had no reason to do so; I could simply symbolize r.params in these routes and leave the other code alone.

2. One particular route in my app that handles json request bodies assumes a hash with string keys (because this route alone contains keys that will vary widely in normal use).

3. Because json_parser populates r.params, using it means that handling /r?x=1&y=2 { "x": 2 } is awkward.  If json_parser populated r.json_params, instead, this would not be a [mental paranoia] obstacle to me.

I can solve all 3 of the above, and I can use json_parser if need be.  Just wasn't sure if rodauth dependence on json_parser was intentional.

Jeremy Evans

unread,
Aug 4, 2021, 6:06:30 PM8/4/21
to rod...@googlegroups.com
On Wed, Aug 4, 2021 at 2:53 PM Jon WIlkes <jon.j...@gmail.com> wrote:
I loaded the plugins explicitly (rather than using :json => true) for clarity.

As for avoiding json_parser, there are 3 reasons I've disabled it.

1. Some of my application code that processes json request bodies assumes hashes with symbolized keys.  That is to say, I *want* symbolized keys most of the time.  Such code could be converted to use string keys, but until now, I've had no reason to do so; I could simply symbolize r.params in these routes and leave the other code alone.

This seems like the best approach until the routes could be converted to use string param keys. 

2. One particular route in my app that handles json request bodies assumes a hash with string keys (because this route alone contains keys that will vary widely in normal use).

This would not require changes.
 
3. Because json_parser populates r.params, using it means that handling /r?x=1&y=2 { "x": 2 } is awkward.  If json_parser populated r.json_params, instead, this would not be a [mental paranoia] obstacle to me.

This could be handled by a different plugin. Making a copy of the json_parser plugin and modifying it should be straightforward.  You could consider using the json_parser_wrap: :always option, which wraps the params in a separate key so they will be distinct from normal parameters. You could also just make a copy of the json_parser plugin and modify it to populate json_params instead of params.

I can solve all 3 of the above, and I can use json_parser if need be.  Just wasn't sure if rodauth dependence on json_parser was intentional.

Yes, it is intentional that the Rodauth json feature uses the Roda json_parser plugin in order to get the params submitted via JSON.

Thanks,
Jeremy
Reply all
Reply to author
Forward
0 new messages