angularjs - Devise + Angular + Rails 4 : Changing Logged-in user not validating username/password -


...so close desired behavior!

have devise / angularjs / rails 4 project user login/logout working perfectly. sending x-csrf-token , storing user's email in angular cookie user hitting refresh doesn't cause angular lose knowledge of user.

only problem can see this: if logged in admin user then, without logging out... fill in username/login other valid credentials... devise authenticating based on csrf token instead of validating username , password.

therefore, you'll think logged in else, previously-logged-in user. i'm sure pretty simple, can't seem nail it... need force devise/warden always check username/password on create - don't happy csrf token.

here our sessions_controller.rb:

class sessionscontroller < devise::sessionscontroller   respond_to :json   def create     rails.logger.debug("(sessionscontroller.create) ******* ")     user = warden.authenticate!(:scope => :user, :recall => "#{controller_path}#failure")     rails.logger.debug("(sessionscontroller.create) warden.authenticate ")     render :status => 200,       :json => { :success => true,                  :info => "logged in",                  :user => current_user       }   end    def destroy     warden.authenticate!(:scope => :user, :recall => "#{controller_path}#failure")     sign_out     render :status => 200,            :json => { :success => true,                       :info => "logged out",            }   end    def failure     render :status => 401,            :json => { :success => false,                       :info => "login credentials failed"            }   end    def show_current_user     warden.authenticate!(:scope => :user, :recall => "#{controller_path}#failure")     render :status => 200,            :json => { :success => true,                       :info => "current user",                       :user => current_user             }   end end 

here application_controller.rb:

class applicationcontroller < actioncontroller::base   protect_from_forgery #with: :exception   helper_method :require_user, :require_admin    after_filter :set_csrf_cookie_for_ng    def set_csrf_cookie_for_ng     rails.logger.debug("set_csrf_cookie_for_ng called fat:#{form_authenticity_token}")     rails.logger.debug("protect_against_forgery = #{protect_against_forgery?}")     cookies['xsrf-token'] = form_authenticity_token if protect_against_forgery?   end    def require_user     rails.logger.debug("require_user method in application controller")     if !user_signed_in?         flash[:notice] = "please sign in!"         redirect_to new_user_session_path     end   end    def is_admin_user     return false if current_user.nil?     return current_user.admin?   end    def is_any_user     return false if current_user.nil?     return user_signed_in?   end    def verify_admin_user     unless is_admin_user       rails.logger.debug("unauthorized - must admin")       respond_to | format |         format.json { render :json => [], :status => :unauthorized }       end     end   end    def verify_any_user     unless user_signed_in?       rails.logger.debug("unauthorized - must logged-in")       respond_to | format |         format.json { render :json => [], :status => :unauthorized }       end     end   end    protected    def verified_request?     rails.logger.debug("verified_request called f_a_t:#{form_authenticity_token}. x-xsrf-token:#{request.headers['x-xsrf-token']}")     super || form_authenticity_token == request.headers['x-xsrf-token']   end end 

routes.rb:

devise_for :users, :controllers => {:sessions => "sessions"} 

finally, on angularjs side, our httpprovider:

  .config(["$httpprovider", ($httpprovider) ->     $httpprovider.defaults.headers.common["x-csrf-token"] = $("meta[name=csrf-token]").attr("content") 

here see going in logs if logged-in admin, attempt "login-over" non admin... note "user 1" admin logged-in already.

started post "/users/sign_in.json" 127.0.0.1 @ 2014-06-02 23:21:25 -0500 processing sessionscontroller#create json   parameters: {"user"=>{"email"=>"nonadmin@example.com", "password"=>"[filtered]"}, "session"=>{"user"=>{"email"=>"nonadmin@example.com", "password"=>"[filtered]"}}} verified_request called f_a_t:p2fzxg/qwooenp0ttme8urxom9tzwh1bo5y28j8rthc=. x-xsrf-token:p2fzxg/qwooenp0ttme8urxom9tzwh1bo5y28j8rthc= (sessionscontroller.create) *******    user load (0.5ms)  select "users".* "users" "users"."id" = 1 order "users"."id" asc limit 1 (sessionscontroller.create) warden.authenticate  set_csrf_cookie_for_ng called fat:p2fzxg/qwooenp0ttme8urxom9tzwh1bo5y28j8rthc= protect_against_forgery = true completed 200 ok in 3ms (views: 0.8ms | activerecord: 0.5ms) 

appreciate help!

devise/warden (by default) uses session/cookie based authentication, not csrf token thats authenticating session.

you have couple of options going forward

1, disable login form when angular knows user logged in

2, explicitly sign user out (by calling sign_out) in create action

3, (preferred) don't use session authentication on api. api should stateless. mean server shouldn't know whether user 'logged in'. should sending authentication token every request (not relying on session). here http://www.soryy.com/ruby/api/rails/authentication/2014/03/16/apis-with-devise.html nice write up.

the basics of token based authentication are:

  • angular sends username , password server
  • server authenticates username , password, generates 'token' user , sends token angular
  • angular stores token (in local storage / cookie)
  • with each new request (eg: /api/private_thing.json) angular sends 'token' (in header or parameter)
  • server checks token belongs user record permission view 'private_thing'

good luck

update

if going go option 2 change create action to:

def create    sign_out if is_any_user   render :status => 200, json: { success: true, info: "logged in", user: current_user } end 

Comments

Popular posts from this blog

commonjs - How to write a typescript definition file for a node module that exports a function? -

openid - Okta: Failed to get authorization code through API call -

ios - Change Storyboard View using Seague -