Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unpermitted parameter: session #130

Closed
rmagnum2002 opened this issue Feb 3, 2015 · 35 comments
Closed

Unpermitted parameter: session #130

rmagnum2002 opened this issue Feb 3, 2015 · 35 comments

Comments

@rmagnum2002
Copy link

Hi, I get this error when signing in, tried a couple of hours to understand what is wrong and no luck, I even thought to override the devise_token_auth/session controller to permit session param too, but that will be the last thing to do unless I find something better:

Started POST "/api/auth/sign_in" for 127.0.0.1 at 2015-02-03 17:01:19 +0200
Processing by DeviseTokenAuth::SessionsController#create as HTML
  Parameters: {"email"=>"[email protected]", "password"=>"[FILTERED]", "session"=>{"email"=>"[email protected]", "password"=>"[FILTERED]"}}
Unpermitted parameter: session
  User Load (0.8ms)  SELECT  "users".* FROM "users" WHERE (uid = '[email protected]' AND provider='email')  ORDER BY "users"."id" ASC LIMIT 1
Completed 401 Unauthorized in 16ms (Views: 6.7ms | ActiveRecord: 2.7ms)
rails 4.2.0
devise_token_auth 0.1.31

Where this session param comes from and what do I do about it. Previous issues related to this didn't helped at all.
Thank you.

@nicolas-besnard
Copy link
Contributor

This is a Rails behavior. Try to create an other controller, you'll have :

Parameters: { "foo" => "bar", "ControllerName" => { "foo" => "bar" } }

Here's the documentation

@rmagnum2002
Copy link
Author

@nicolas-besnard doesn't this means that I need to copy all the session controller functionality to this new controller? If there is a session controller in this gem, why would I create a new one?
I need somehow to deal with this session param, in Devise gem for example there were a method you could override to specify additional param you want to permit for sign_in or sign_up actions, is there a way to do so in this gem?

And just to update my issue, when I created the user in console, I had validation errors saying UID and PROVIDER can not be blank. So I added a random UID and provider a random word, in my case "basic".
May be this has something to do with it.

@nicolas-besnard
Copy link
Contributor

What I meant is that the params sessions created is created by Rails. Not by this gem.

if you need more parameters on sign_in or sign_up just add them in your request beside the email and password parameters.

When you sign_in with EMAIL, the uid is the email provided and the provider is 'email'.

@rmagnum2002
Copy link
Author

I don't need more params, I just don't understand what session param is doing here and how do I premit it in this controller: https://github.com/lynndylanhurley/devise_token_auth/blob/master/app/controllers/devise_token_auth/sessions_controller.rb

or do I even need this session param? may be it's a bug that comes from ng-token-auth, why in the world would be useful to have a request with duplicated email and password param?

Why the param looks like this:

Parameters: {"email"=>"[email protected]", "password"=>"[FILTERED]", "session"=>{"email"=>"[email protected]", "password"=>"[FILTERED]"}}

and why do I need email and password in param root and in session? And if it needs to be in session too, how do I override the resource_params in sessions controller to allow session param too?

    def resource_params
      params.permit(devise_parameter_sanitizer.for(:sign_in))
    end

where can I find

   devise_parameter_sanitizer.for(:sign_in)

@nicolas-besnard
Copy link
Contributor

`why do I need email and password in param root and in session?``

You don't, it's a normal Rails behavior. When you call an url http://yousite.com/foo?bar=title

You'll end with :

Parameters: { "bar" => "title", "ControllerName" => { "bar" => "title" } }
where can I find devise_parameter_sanitizer.for(:sign_in)

In the Devise documentation.

@rmagnum2002
Copy link
Author

Found the devise_parameter_sanitizer method. Thanks, but I don't think I need to touch that.
Also, in rails with devise login request looks different, because we use the rails form_for helper.

Parameters: {"utf8"=>"", "authenticity_token"=>"+Y0glSMrpy5CHm8=", "user"=>{"email"=>"[email protected]", "password"=>"[FILTERED]", "remember_me"=>"0"}, "commit"=>"Log in"}

there is no session param and this is how a normal Rails behavior should be.

However, considering that I am not using a form_for helper in this case, it's just a plain html form with some angular things in it,

<form ng-submit="submitLogin(loginForm)" role="form" ng-init="loginForm = {}">

and the important part might be:

ng-submit="submitLogin(loginForm)"

probably the function submitLogin from angular module ng-token-auth somehow adds this session param.
https://github.com/lynndylanhurley/ng-token-auth/blob/master/src/ng-token-auth.coffee#L189

@nicolas-besnard
Copy link
Contributor

in ng-token-auth, you send the authentication information NOT IN an object (for example: <input name="email" />). '. The curl representation will be :

curl -XPOST -H 'Content-Type: application/json' http://localhost:8080/users/auth/sign_in -d '{"username": "foo", "password": "bar"}'

In this case, Rails will map the request's params in a key with the name of your controller :

Parameters: { "bar" => "title", "ControllerName" => { "bar" => "title" } }

TRUST ME, THIS IS A NORMAL BEHAVIOR IN RAILS try to hit a route with a POST request in curl.

Now, in Devise (without ng-token-auth or thisgem) , it seems you are using a form. You can see in this form that your form value is mapped to user(for example: <input name="user[email"] />).
So, you'll end with this

Parameters: { "ControllerName" => { "bar" => "title" } }

@rmagnum2002
Copy link
Author

"THIS IS A NORMAL BEHAVIOR IN RAILS" - ok, I got it :)

"your form value is mapped" - No my friend, the value is not mapped to user the way rails does, cause the form I use is a template from assets used by angular. Here is an example of it:
https://github.com/lynndylanhurley/ng-token-auth#example-use-in-a-controller-2
I just added id attributes to the fields, nothing more, also attributes are not mapped into a session like session[email].. but if you say rails adds it, thanks for the help and I'll try to dig in more.

Thank you for your time.

btw, where is the controller name in this request:

Parameters: {"utf8"=>"", "authenticity_token"=>"+Y0glSMrpy5CHm8=", "user"=>{"email"=>"[email protected]", "password"=>"[FILTERED]", "remember_me"=>"0"}, "commit"=>"Log in"}

just wondering, this request is consumed by sessions_controller from devise, where is the session param? If you have time please explain this. Thank you so much again.

@nicolas-besnard
Copy link
Contributor

There's no session param (nore ControllerName param), because you don't specify any params who's not an object.

As I said, you should try to hit this same route with a curl request an add an other parameter such as :

curl -XPOST -H 'Content-Type: application/json' http://localhost:3000/ -d '{"user": { "email": "[email protected]", "password": "password" }, "orphan_parameter": "fooBar" }'

Sorry if I sound "harsh". Wasn't my intention.

@rmagnum2002
Copy link
Author

"Sorry if I sound "harsh"" - No problem, honestly, I asked for it.. anyway.. let me explain where my problem starts. It started right here:
https://www.airpair.com/ruby-on-rails/posts/authentication-with-angularjs-and-ruby-on-rails

he uses devise_token_auth with ng-token-auth, as you'll see, there is no form in a rails way, there is an angular template that stores this form, and this form has

ng-model="loginForm.password" and ng-model="loginForm.email" that stores the email and password, and ng-submit="submitLogin(loginForm)" takes care that on submit these values goes to some angular controller and using the params he got it makes the get request to the api

Started POST "/api/auth/sign_in" for 127.0.0.1 at 2015-02-03 22:19:48 +0200
Processing by DeviseTokenAuth::SessionsController#create as HTML
  Parameters: {"email"=>"[email protected]", "password"=>"[FILTERED]", "session"=>{"email"=>"[email protected]", "password"=>"[FILTERED]"}}
Unpermitted parameter: session
  User Load (0.6ms)  SELECT  "users".* FROM "users" WHERE (uid = '[email protected]' AND provider='email')  ORDER BY "users"."id" ASC LIMIT 1
Completed 401 Unauthorized in 5ms (Views: 1.6ms | ActiveRecord: 0.6ms)

so I suspect it's angular that pust the session param in the request, cause I tried to map email and password field into user, like user[email] and user[params], and the request did not changed at all (refreshed the page, restarted server), the request was the same, that's why I am thinking that request to the api is done by angular with this dumb session param that is not permitted by devise, normally devise should get something like:

Parameters: {"user" => {"email"=>"[email protected]", "password"=>"[FILTERED]"}, ...}

and based on params[:user][:email] and params[:user][:password] do the authentication.

If you have time and will to look over that tutorial hoping you might spot something I missed, I would appreciate it very much. Else, I appreciate it anyway :) and this issue can be closed. But someone might get into the same issue in the future.
Thank you.

@nicolas-besnard
Copy link
Contributor

cause I tried to map email and password field into user, like user[email] and user[params]

Where did you try do to this ? You have to do it in your Angular controller, not in the view.

I am thinking that request to the api is done by angular with this dumb session param that is not permitted by devise, normally devise should get something like:

Once again, it's not done by Angular, it's a normal Rails Behavior ^^
Try to hit your endpoint with curl by adding a random parameter (I put the command line to execute in a previous comment) and you'll see that, if the parameter is orphan, he'll be present twice on your parameter : one on the root of your parameters, one has a member.

@lynndylanhurley
Copy link
Owner

@rmagnum2002 - can you post the body of the request? This can be found in the "network" tab of your browser's web inspector.

@rmagnum2002
Copy link
Author

@lynndylanhurley
Copy link
Owner

That looks correct - it's just the response for an invalid username and/or password. What's the problem again?

@rmagnum2002
Copy link
Author

Well, I am sure 100% that the pass and email are correct.
Also, you know, devise has this 'params.premit' thing, right? If I would do rails + devise authentication without angular, the form would send params in a form like

Parameters: {"user" => {"email"=>"[email protected]", "password"=>"[FILTERED]"}, ...}

and devise actually permit this params, as it builds it from the resource name (user in our case) and email + password for session create process.

Now, your gem has devise as dependency, right? So it means in sessions controller your have:

   def resource_params
      params.permit(devise_parameter_sanitizer.for(:sign_in))
    end

where devise_parameter_sanitizer is a method/class from devise gem
https://github.com/plataformatec/devise/blob/master/lib/devise/parameter_sanitizer.rb

In the end params permit in your gem's session controller will look like:

   params.permit(user: [:email, :password])

and this will work with the request in form like this:

  Parameters: {"user" => {"email"=>"[email protected]", "password"=>"[FILTERED]"}, ...}

but when I am using it with angular, email and password param comes out of user param

  {"email"=>"[email protected]", "password"=>"[FILTERED]", "session"=>{"email"=>"[email protected]", "password"=>"[FILTERED]"}}

session param and content of it is not permitted by sessions controller, params email and password are not in user => {} hash.. so.. there is no way this will work out of the box.. I need somehow to tell angular how to build the request.

If it does not make any sense what I am saying, just leave it and close the issue.
Thank you for your time.

@lynndylanhurley
Copy link
Owner

The params shouldn't be nested inside a user object.

Instead of this:

Parameters: {"user" => {"email"=>"[email protected]", "password"=>"[FILTERED]"}, ...}

The params should look like this:

Parameters: {"email"=>"[email protected]", "password"=>"[FILTERED]"}

Can you think of any reason that the params are nested inside the user object?

@rmagnum2002
Copy link
Author

I am not saying they should be, but knowing the way devise and permit.params in devsie works, email and password should be nested in user param, cause this is the way sessions controller permits it.
I am ok with having the params outside of user, but devise will not permit them, it will look for user.email and user.password, without it it will be like sending nothing and authentication will fail.

Or, let's forget about the params are sent in request, and I'll ask you a question:
What this line would do?
https://github.com/lynndylanhurley/devise_token_auth/blob/master/app/controllers/devise_token_auth/sessions_controller.rb#L84
what params this method permits?

@rmagnum2002
Copy link
Author

Face-palm, I think I am getting closer.

That tutorial did not mentioned anything about that the user I create in console should have:
1 - a provider with value "email"
2 - an uid with value same as the email user uses to log in

and unauthorized error I got because app was looking for user with uid "[email protected]" and provider "email", so it wasn't found.

now I have another problem to handle, but it's not related to this gem.

2.2.0 :004 > User.last
  User Load (1.0ms)  SELECT  "users".* FROM "users"  ORDER BY "users"."id" DESC LIMIT 1
 => #<User id: 1, provider: "email", uid: "[email protected]", encrypted_password: "$2a$10$22mHqp39i.8QhMl2su3ef.xg8tPHTqJt5j72EOg4MLJ...", reset_password_token: nil, reset_password_sent_at: nil, remember_created_at: nil, sign_in_count: 0, current_sign_in_at: nil, last_sign_in_at: nil, current_sign_in_ip: nil, last_sign_in_ip: nil, confirmation_token: "08c9ca9cf521287016bf94d5af0c05db4484be74d285b0ceb4...", confirmed_at: nil, confirmation_sent_at: "2015-02-05 08:52:04", unconfirmed_email: nil, name: nil, nickname: nil, image: nil, email: "[email protected]", tokens: {}, created_at: "2015-02-05 08:52:04", updated_at: "2015-02-05 08:52:04"> 
2.2.0 :005 > User.where(uid: '[email protected]', provider: 'email')
  User Load (0.4ms)  SELECT "users".* FROM "users" WHERE "users"."uid" = $1 AND "users"."provider" = $2  [["uid", "[email protected]"], ["provider", "email"]]
 => #<ActiveRecord::Relation []> 

Thank you both for your time and patience.

@lynndylanhurley
Copy link
Owner

I am not saying they should be, but knowing the way devise and permit.params in devsie works, email and password should be nested in user param, cause this is the way sessions controller permits it.

The way that I understand it, parameter sanitizers are just whitelists. They don't actually modify or re-structure the existing params, they just filter out params that haven't been explicitly allowed. The documentation for strong params is here.

What this line would do?

The documentation is here:
https://github.com/plataformatec/devise#strong-parameters

and unauthorized error I got because app was looking for user with uid "[email protected]" and provider "email", so it wasn't found.

I think this gem's behavior has changed since that tutorial was written. Sorry for the confusion - I'll update the docs ASAP!

@rmagnum2002
Copy link
Author

I'll create a summary of the issue, if any one follows that tutorial as well:

In order to not get Unauthorized error user must have the following data filled in:

  1. a provider with value "email"
  2. an uid with value same as the email user uses to log in
  3. user should be confirmed, confirmed_at field should not be null, I made a post request with postman chrome plugin and I got the error back - An email was sent to activate your account blah blah blah

Also, when you create the user and specify the provider as "email", the UID field will be automatically set as the email of the user.
Sign in process now works ok, except the error in logs that complains about session param:

Unpermitted parameter: session

But this does not brakes the sign in in any way, it's just blacklisted. And this is not related with the controller name, cause controller name is 'sessions', but the param is 'session' Investigating.

@lynndylanhurley
Copy link
Owner

Awesome, thanks @rmagnum2002!

@nicolas-besnard
Copy link
Contributor

But this does not brakes the sign in in any way, it's just blacklisted. And this is not related with the controller name, cause controller name is 'sessions', but the param is 'session' Investigating.

Once again, try to hit a controller with a POST request after defining params.require(:relation).permit(:title) and do a search with this line on a model. Like I said before this a a Rails normal behavior.

Take a look at the documentation and just read the first line.

I copy it for you, because I'm done trying to make you understand THIS IS NORMAL :

You can send parameters like this:

{"name": "Konata"}

And it will be wrapped into a nested hash with the key name matching the controller's name. For example, if you're posting to UsersController, your new params hash will look like this:

{"name" => "Konata", "user" => {"name" => "Konata"}}

@rmagnum2002
Copy link
Author

I hit it with a post request from chromes postman plugin, and there were no session param there, if this is a normal rails behavior, why session param is not present when doing requests with postman?

Logs from the request with postman:

Started POST "/api/auth/sign_in" for 127.0.0.1 at 2015-02-06 00:11:52 +0200
Processing by DeviseTokenAuth::SessionsController#create as */*
  Parameters: {"email"=>"[email protected]", "password"=>"[FILTERED]"}
  User Load (0.4ms)  SELECT  "users".* FROM "users" WHERE (uid = '[email protected]' AND provider='email')  ORDER BY "users"."id" ASC LIMIT 1
   (0.2ms)  BEGIN
  SQL (0.3ms)  UPDATE "users" SET "tokens" = $1, "updated_at" = $2 WHERE "users"."id" = $3  [["tokens", "{\"Clmy2Lrzz0G4tZ261M-l1Q\
....

How come there is no session param if it's a Rails normal behavior? I just don't get it. While post request that comes from angular generates session param:

Started POST "/api/auth/sign_in" for 127.0.0.1 at 2015-02-05 23:59:28 +0200
Processing by DeviseTokenAuth::SessionsController#create as HTML
  Parameters: {"email"=>"[email protected]", "password"=>"[FILTERED]", "session"=>{"email"=>"[email protected]", "password"=>"[FILTERED]"}}
Unpermitted parameter: session
  User Load (1.3ms)  SELECT  "users".* FROM "users" WHERE (uid = '[email protected]' AND provider='email')  ORDER BY "users"."id" ASC LIMIT 1
Unpermitted parameter: session
Unpermitted parameter: session
Unpermitted parameter: session
   (0.2ms)  BEGIN
  SQL (0.4ms)  UPDATE "users" SET "tokens" = $1, "updated_at" = $2 WHERE "users"."id" = $3  [["tokens", "{\"Clmy2Lrzz0G4tZ261M-l1Q\"

@rmagnum2002
Copy link
Author

@nicolas-besnard

Take a look at the documentation and just read the first line.

now I got it. well, I'll stay with this message in logs then, till I find a way to override permitted_params.
Btw, does this gem provide a method like devise do to override params for sigin? or I just do it like in devise and it will work?

@nicolas-besnard
Copy link
Contributor

On your request, we can see one difference.
On the first one (where you don't have the session parameter) :
`Processing by DeviseTokenAuth::SessionsController#create as /``

On the second one (where you do have the session parameter) :
Processing by DeviseTokenAuth::SessionsController#create as HTML

According to the doc, you can specify which format you want to wrap. Try to redo your first request but this time add a header 'Content-Type: application/json'. And now, you'll see the session parameter.

@rmagnum2002
Copy link
Author

 add a header 'Content-Type: application/json'. And now, you'll see the session parameter.

Wow, what a noob you are talking to... You were right, session param is here if I add Content-Type to header.
Thank you!

@nicolas-besnard
Copy link
Contributor

No problem, I face this problem when I started learning Rails. It gave me headache !

@christophermlne
Copy link

i've read through this conversation twice, and i still don't know what the solution was.

—how does one tell the session controller to permit the session parameter?
—is this even what we should be doing as users of the gem to solve this problem?

any advice appreciated

@christophermlne
Copy link

for those looking for a solution to this problem, there is an initializer file called wrap_parameters.rb in the config folder—there are instructions in the comments of that file for disabling the parameter wrapping behaviour

@nicolas-besnard
Copy link
Contributor

how does one tell the session controller to permit the session parameter?

This is caused by ActionController ParamsWrapper

is this even what we should be doing as users of the gem to solve this problem?

This is not a problem as it's a normal Rails behavior.

for those looking for a solution to this problem, there is an initializer file called wrap_parameters.rb in the config folder—there are instructions in the comments of that file for disabling the parameter wrapping behaviour

Or to add in the controller wrap_parameters false

@brianunlam
Copy link

@rmagnum2002
guys! I've learnt a lot with this post. My problem was solved thanks rmagnum2002 (it was the provider with value "email"). And about the wrap_parameters, I've seen this behaviour before and I thought it was Angular. Best Regards!

@buncis
Copy link

buncis commented Feb 27, 2019

if you guys have this particular problem and want to get rid the warning because its annoying
all you need to do is add this wrap_parameters false into your overrided controller

see more here

@artcoder87
Copy link

Not solved for me yet

@damien-roche
Copy link

damien-roche commented Jun 12, 2020

Create an initializer if it doesn't exist wrap_paramters.rb:

ActiveSupport.on_load(:action_controller) do
  wrap_parameters format: []
end

This is one of those annoying and questionable defaults. Can't say I have ever wanted Rails to wrap parameters automatically. If I want them wrapped I'll wrap them when I send..?

@leandrolasnor
Copy link

module ::Overrides
  class SessionsController < DeviseTokenAuth::SessionsController
    protected

    def resource_params
      params.except(:session).permit(*params_for_resource(:sign_in))
    end
  end
end

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

9 participants