-
Notifications
You must be signed in to change notification settings - Fork 1
Description
In OAuth 2.0, it's possible to authenticate the client when interacting with the token endpoint via HTTP Basic Authentication. It seems the devise_pam_authenticable2 module always runs, and it chokes because params[scope]
is nil: https://github.com/devkral/devise_pam_authenticatable2/blob/master/lib/devise_pam_authenticatable/strategy.rb#L10-L11
I'm not sure why exactly devise is running the PAM authentication code for every route, and not just the authentication related routes, but that's perhaps a different issue.
The error that's generated looks like the following:
Failure/Error: @app.call(env)
NoMethodError:
undefined method '[]=' for nil
# ./lib/mastodon/middleware/socket_cleanup.rb:11:in 'Mastodon::Middleware::SocketCleanup#call'
# ./lib/mastodon/middleware/public_file_server.rb:20:in 'Mastodon::Middleware::PublicFileServer#call'
# ./spec/support/signed_request_helpers.rb:23:in 'SignedRequestHelpers#post'
# ./spec/requests/oauth/token_spec.rb:8:in 'block (3 levels) in <top (required)>'
# ./spec/requests/oauth/token_spec.rb:101:in 'block (6 levels) in <top (required)>'
# ./spec/rails_helper.rb:139:in 'block (2 levels) in <top (required)>'
The full stack trace is:
Failure/Error: @app.call(env)
NoMethodError:
undefined method `[]=' for nil:NilClass
# ./vendor/bundle/ruby/3.2.0/gems/devise_pam_authenticatable2-9.2.0/lib/devise_pam_authenticatable/strategy.rb:11:in `authenticate!'
# ./vendor/bundle/ruby/3.2.0/gems/warden-1.2.9/lib/warden/strategies/base.rb:55:in `_run!'
# ./vendor/bundle/ruby/3.2.0/gems/warden-1.2.9/lib/warden/proxy.rb:372:in `block in _run_strategies_for'
# ./vendor/bundle/ruby/3.2.0/gems/warden-1.2.9/lib/warden/proxy.rb:365:in `each'
# ./vendor/bundle/ruby/3.2.0/gems/warden-1.2.9/lib/warden/proxy.rb:365:in `_run_strategies_for'
# ./vendor/bundle/ruby/3.2.0/gems/warden-1.2.9/lib/warden/proxy.rb:335:in `_perform_authentication'
# ./vendor/bundle/ruby/3.2.0/gems/warden-1.2.9/lib/warden/proxy.rb:110:in `authenticate'
# ./vendor/bundle/ruby/3.2.0/gems/devise-4.9.4/lib/devise/controllers/helpers.rb:128:in `current_user'
# ./vendor/bundle/ruby/3.2.0/gems/active_model_serializers-0.10.15/lib/action_controller/serialization.rb:40:in `serialization_scope'
# ./vendor/bundle/ruby/3.2.0/gems/active_model_serializers-0.10.15/lib/action_controller/serialization.rb:53:in `block in get_serializer'
# ./vendor/bundle/ruby/3.2.0/gems/active_model_serializers-0.10.15/lib/action_controller/serialization.rb:53:in `fetch'
# ./vendor/bundle/ruby/3.2.0/gems/active_model_serializers-0.10.15/lib/action_controller/serialization.rb:53:in `get_serializer'
# ./vendor/bundle/ruby/3.2.0/gems/active_model_serializers-0.10.15/lib/action_controller/serialization.rb:71:in `block (2 levels) in <module:Serialization>'
# ./vendor/bundle/ruby/3.2.0/gems/actionpack-8.0.2/lib/action_controller/metal/renderers.rb:148:in `block in _render_to_body_with_renderer'
# ./vendor/bundle/ruby/3.2.0/gems/actionpack-8.0.2/lib/action_controller/metal/renderers.rb:144:in `_render_to_body_with_renderer'
# ./vendor/bundle/ruby/3.2.0/gems/actionpack-8.0.2/lib/action_controller/metal/renderers.rb:140:in `render_to_body'
# ./vendor/bundle/ruby/3.2.0/gems/actionpack-8.0.2/lib/abstract_controller/rendering.rb:28:in `render'
# ./vendor/bundle/ruby/3.2.0/gems/actionpack-8.0.2/lib/action_controller/metal/rendering.rb:167:in `render'
# ./vendor/bundle/ruby/3.2.0/gems/actionpack-8.0.2/lib/action_controller/metal/instrumentation.rb:31:in `block (2 levels) in render'
# ./vendor/bundle/ruby/3.2.0/gems/activesupport-8.0.2/lib/active_support/benchmark.rb:17:in `realtime'
# ./vendor/bundle/ruby/3.2.0/gems/actionpack-8.0.2/lib/action_controller/metal/instrumentation.rb:31:in `block in render'
# ./vendor/bundle/ruby/3.2.0/gems/actionpack-8.0.2/lib/action_controller/metal/instrumentation.rb:100:in `cleanup_view_runtime'
# ./vendor/bundle/ruby/3.2.0/gems/activerecord-8.0.2/lib/active_record/railties/controller_runtime.rb:46:in `cleanup_view_runtime'
# ./vendor/bundle/ruby/3.2.0/gems/actionpack-8.0.2/lib/action_controller/metal/instrumentation.rb:30:in `render'
# ./vendor/bundle/ruby/3.2.0/gems/doorkeeper-5.8.2/app/controllers/doorkeeper/tokens_controller.rb:13:in `create'
# ./vendor/bundle/ruby/3.2.0/gems/actionpack-8.0.2/lib/action_controller/metal/basic_implicit_render.rb:8:in `send_action'
# ./vendor/bundle/ruby/3.2.0/gems/actionpack-8.0.2/lib/abstract_controller/base.rb:226:in `process_action'
# ./vendor/bundle/ruby/3.2.0/gems/actionpack-8.0.2/lib/action_controller/metal/rendering.rb:193:in `process_action'
# ./vendor/bundle/ruby/3.2.0/gems/actionpack-8.0.2/lib/abstract_controller/callbacks.rb:261:in `block in process_action'
# ./vendor/bundle/ruby/3.2.0/gems/activesupport-8.0.2/lib/active_support/callbacks.rb:109:in `run_callbacks'
# ./vendor/bundle/ruby/3.2.0/gems/actionpack-8.0.2/lib/abstract_controller/callbacks.rb:260:in `process_action'
# ./vendor/bundle/ruby/3.2.0/gems/actionpack-8.0.2/lib/action_controller/metal/rescue.rb:27:in `process_action'
# ./vendor/bundle/ruby/3.2.0/gems/actionpack-8.0.2/lib/action_controller/metal/instrumentation.rb:76:in `block in process_action'
# ./vendor/bundle/ruby/3.2.0/gems/activesupport-8.0.2/lib/active_support/notifications.rb:210:in `block in instrument'
# ./vendor/bundle/ruby/3.2.0/gems/activesupport-8.0.2/lib/active_support/notifications/instrumenter.rb:58:in `instrument'
# ./vendor/bundle/ruby/3.2.0/gems/activesupport-8.0.2/lib/active_support/notifications.rb:210:in `instrument'
# ./vendor/bundle/ruby/3.2.0/gems/actionpack-8.0.2/lib/action_controller/metal/instrumentation.rb:75:in `process_action'
# ./vendor/bundle/ruby/3.2.0/gems/actionpack-8.0.2/lib/action_controller/metal/params_wrapper.rb:259:in `process_action'
# ./vendor/bundle/ruby/3.2.0/gems/activerecord-8.0.2/lib/active_record/railties/controller_runtime.rb:39:in `process_action'
# ./vendor/bundle/ruby/3.2.0/gems/actionpack-8.0.2/lib/abstract_controller/base.rb:163:in `process'
# ./vendor/bundle/ruby/3.2.0/gems/actionpack-8.0.2/lib/action_controller/metal.rb:252:in `dispatch'
# ./vendor/bundle/ruby/3.2.0/gems/actionpack-8.0.2/lib/action_controller/metal.rb:335:in `dispatch'
# ./vendor/bundle/ruby/3.2.0/gems/actionpack-8.0.2/lib/action_dispatch/routing/route_set.rb:67:in `dispatch'
# ./vendor/bundle/ruby/3.2.0/gems/actionpack-8.0.2/lib/action_dispatch/routing/route_set.rb:50:in `serve'
# ./vendor/bundle/ruby/3.2.0/gems/actionpack-8.0.2/lib/action_dispatch/journey/router.rb:53:in `block in serve'
# ./vendor/bundle/ruby/3.2.0/gems/actionpack-8.0.2/lib/action_dispatch/journey/router.rb:133:in `block in find_routes'
# ./vendor/bundle/ruby/3.2.0/gems/actionpack-8.0.2/lib/action_dispatch/journey/router.rb:126:in `each'
# ./vendor/bundle/ruby/3.2.0/gems/actionpack-8.0.2/lib/action_dispatch/journey/router.rb:126:in `find_routes'
# ./vendor/bundle/ruby/3.2.0/gems/actionpack-8.0.2/lib/action_dispatch/journey/router.rb:34:in `serve'
# ./vendor/bundle/ruby/3.2.0/gems/actionpack-8.0.2/lib/action_dispatch/routing/route_set.rb:908:in `call'
# ./vendor/bundle/ruby/3.2.0/gems/railties-8.0.2/lib/rails/engine/lazy_route_set.rb:68:in `call'
# ./vendor/bundle/ruby/3.2.0/gems/omniauth-2.1.3/lib/omniauth/strategy.rb:472:in `call_app!'
# ./vendor/bundle/ruby/3.2.0/gems/omniauth-2.1.3/lib/omniauth/strategy.rb:314:in `mock_call!'
# ./vendor/bundle/ruby/3.2.0/gems/omniauth-2.1.3/lib/omniauth/strategy.rb:189:in `call!'
# ./vendor/bundle/ruby/3.2.0/gems/omniauth-2.1.3/lib/omniauth/strategy.rb:169:in `call'
# ./vendor/bundle/ruby/3.2.0/gems/omniauth-2.1.3/lib/omniauth/strategy.rb:472:in `call_app!'
# ./vendor/bundle/ruby/3.2.0/gems/omniauth-2.1.3/lib/omniauth/strategy.rb:314:in `mock_call!'
# ./vendor/bundle/ruby/3.2.0/gems/omniauth-2.1.3/lib/omniauth/strategy.rb:189:in `call!'
# ./vendor/bundle/ruby/3.2.0/gems/omniauth-2.1.3/lib/omniauth/strategy.rb:169:in `call'
# ./vendor/bundle/ruby/3.2.0/gems/omniauth-2.1.3/lib/omniauth/strategy.rb:472:in `call_app!'
# ./vendor/bundle/ruby/3.2.0/gems/omniauth-2.1.3/lib/omniauth/strategy.rb:314:in `mock_call!'
# ./vendor/bundle/ruby/3.2.0/gems/omniauth-2.1.3/lib/omniauth/strategy.rb:189:in `call!'
# ./vendor/bundle/ruby/3.2.0/gems/omniauth-2.1.3/lib/omniauth/strategy.rb:169:in `call'
# ./vendor/bundle/ruby/3.2.0/gems/omniauth-2.1.3/lib/omniauth/builder.rb:44:in `call'
# ./vendor/bundle/ruby/3.2.0/gems/rack-attack-6.7.0/lib/rack/attack.rb:103:in `call'
# ./vendor/bundle/ruby/3.2.0/gems/http_accept_language-2.1.1/lib/http_accept_language/middleware.rb:14:in `call'
# ./lib/mastodon/middleware/socket_cleanup.rb:11:in `call'
# ./vendor/bundle/ruby/3.2.0/gems/rack-attack-6.7.0/lib/rack/attack.rb:127:in `call'
# ./lib/mastodon/middleware/public_file_server.rb:20:in `call'
# ./vendor/bundle/ruby/3.2.0/gems/warden-1.2.9/lib/warden/manager.rb:36:in `block in call'
# ./vendor/bundle/ruby/3.2.0/gems/warden-1.2.9/lib/warden/manager.rb:34:in `catch'
# ./vendor/bundle/ruby/3.2.0/gems/warden-1.2.9/lib/warden/manager.rb:34:in `call'
# ./vendor/bundle/ruby/3.2.0/gems/rack-2.2.13/lib/rack/tempfile_reaper.rb:15:in `call'
# ./vendor/bundle/ruby/3.2.0/gems/rack-2.2.13/lib/rack/etag.rb:27:in `call'
# ./vendor/bundle/ruby/3.2.0/gems/rack-2.2.13/lib/rack/conditional_get.rb:40:in `call'
# ./vendor/bundle/ruby/3.2.0/gems/rack-2.2.13/lib/rack/head.rb:12:in `call'
# ./vendor/bundle/ruby/3.2.0/gems/actionpack-8.0.2/lib/action_dispatch/http/permissions_policy.rb:38:in `call'
# ./vendor/bundle/ruby/3.2.0/gems/actionpack-8.0.2/lib/action_dispatch/http/content_security_policy.rb:38:in `call'
# ./vendor/bundle/ruby/3.2.0/gems/rack-2.2.13/lib/rack/session/abstract/id.rb:266:in `context'
# ./vendor/bundle/ruby/3.2.0/gems/rack-2.2.13/lib/rack/session/abstract/id.rb:260:in `call'
# ./vendor/bundle/ruby/3.2.0/gems/actionpack-8.0.2/lib/action_dispatch/middleware/cookies.rb:706:in `call'
# ./vendor/bundle/ruby/3.2.0/gems/actionpack-8.0.2/lib/action_dispatch/middleware/callbacks.rb:31:in `block in call'
# ./vendor/bundle/ruby/3.2.0/gems/activesupport-8.0.2/lib/active_support/callbacks.rb:100:in `run_callbacks'
# ./vendor/bundle/ruby/3.2.0/gems/actionpack-8.0.2/lib/action_dispatch/middleware/callbacks.rb:30:in `call'
# ./vendor/bundle/ruby/3.2.0/gems/actionpack-8.0.2/lib/action_dispatch/middleware/actionable_exceptions.rb:18:in `call'
# ./vendor/bundle/ruby/3.2.0/gems/actionpack-8.0.2/lib/action_dispatch/middleware/debug_exceptions.rb:31:in `call'
# ./vendor/bundle/ruby/3.2.0/gems/actionpack-8.0.2/lib/action_dispatch/middleware/show_exceptions.rb:32:in `call'
# ./vendor/bundle/ruby/3.2.0/gems/chewy-7.6.0/lib/chewy/railtie.rb:21:in `block in call'
# ./vendor/bundle/ruby/3.2.0/gems/chewy-7.6.0/lib/chewy/strategy.rb:60:in `wrap'
# ./vendor/bundle/ruby/3.2.0/gems/chewy-7.6.0/lib/chewy.rb:151:in `strategy'
# ./vendor/bundle/ruby/3.2.0/gems/chewy-7.6.0/lib/chewy/railtie.rb:21:in `call'
# ./vendor/bundle/ruby/3.2.0/gems/railties-8.0.2/lib/rails/rack/logger.rb:41:in `call_app'
# ./vendor/bundle/ruby/3.2.0/gems/railties-8.0.2/lib/rails/rack/logger.rb:29:in `call'
# ./vendor/bundle/ruby/3.2.0/gems/actionpack-8.0.2/lib/action_dispatch/middleware/remote_ip.rb:96:in `call'
# ./vendor/bundle/ruby/3.2.0/gems/actionpack-8.0.2/lib/action_dispatch/middleware/request_id.rb:34:in `call'
# ./vendor/bundle/ruby/3.2.0/gems/rack-2.2.13/lib/rack/method_override.rb:24:in `call'
# ./vendor/bundle/ruby/3.2.0/gems/rack-2.2.13/lib/rack/runtime.rb:22:in `call'
# ./vendor/bundle/ruby/3.2.0/gems/actionpack-8.0.2/lib/action_dispatch/middleware/executor.rb:16:in `call'
# ./vendor/bundle/ruby/3.2.0/gems/rack-2.2.13/lib/rack/sendfile.rb:110:in `call'
# ./vendor/bundle/ruby/3.2.0/gems/rack-cors-2.0.2/lib/rack/cors.rb:102:in `call'
# ./vendor/bundle/ruby/3.2.0/gems/railties-8.0.2/lib/rails/engine.rb:535:in `call'
# ./vendor/bundle/ruby/3.2.0/gems/rack-test-2.2.0/lib/rack/test.rb:360:in `process_request'
# ./vendor/bundle/ruby/3.2.0/gems/rack-test-2.2.0/lib/rack/test.rb:153:in `request'
# ./vendor/bundle/ruby/3.2.0/gems/actionpack-8.0.2/lib/action_dispatch/testing/integration.rb:297:in `process'
# ./vendor/bundle/ruby/3.2.0/gems/actionpack-8.0.2/lib/action_dispatch/testing/integration.rb:25:in `post'
# ./vendor/bundle/ruby/3.2.0/gems/actionpack-8.0.2/lib/action_dispatch/testing/integration.rb:388:in `post'
# ./spec/support/signed_request_helpers.rb:23:in `post'
# ./spec/requests/oauth/token_spec.rb:8:in `block (3 levels) in <top (required)>'
# ./spec/requests/oauth/token_spec.rb:78:in `block (5 levels) in <top (required)>'
# ./spec/rails_helper.rb:139:in `block (2 levels) in <top (required)>'
# ./vendor/bundle/ruby/3.2.0/gems/webmock-3.25.1/lib/webmock/rspec.rb:39:in `block (2 levels) in <top (required)>'
# ./vendor/bundle/ruby/3.2.0/gems/thor-1.3.2/lib/thor/command.rb:28:in `run'
# ./vendor/bundle/ruby/3.2.0/gems/thor-1.3.2/lib/thor/invocation.rb:127:in `invoke_command'
# ./vendor/bundle/ruby/3.2.0/gems/thor-1.3.2/lib/thor.rb:538:in `dispatch'
# ./vendor/bundle/ruby/3.2.0/gems/thor-1.3.2/lib/thor/base.rb:584:in `start'
I was able to add a binding.break
point in, and could see why the code in this gem is failing:
One option for a solution may be to change authenticate!
in this gem to something like the following:
def authenticate!
- pam_params = params[scope].clone
- pam_params[:request] = request
- if (resource = mapping.to.authenticate_with_pam(pam_params))
+ if (resource = mapping.to.authenticate_with_pam(authentication_hash.merge(password: password, request: request)))
success!(resource)
else
fail(:invalid)
end
end
Which is based on how devise ldap works: https://github.com/cschiewek/devise_ldap_authenticatable/blob/default/lib/devise_ldap_authenticatable/strategy.rb#L13
I suspect what's in authentication_hash
is what you were after by looking at params[scope]
because that's what valid_for_params_auth?
under the hood in doorkeeper does: https://github.com/heartcombo/devise/blob/main/lib/devise/strategies/authenticatable.rb#L77-L80