2

I would like to reset session whenever user agent and/or user ip changes. Project uses Rails with Devise for authentication, CanCanCan for authorisation.

My approach is:

class ApplicationController < ActionController::Base

  before_action :authorize_ip

  def authorize_ip
    if warden.authenticated?
      warden.session['ip'] ||= request.ip
      warden.session['user_agent'] ||= request.user_agent
      warden.logout if warden.session['ip'] != 'request.ip' &&
        warden.session['user_agent'] != request.user_agent
    end
  end
end

From my understanding, it should set warden.session['ip'] and user_agent once and then for following requests whenever request['ip'] or user_agent changes, session should be dropped and User should be logged out. However, when tested with different browsers, warden.session['user_agent'] changes according to what browser I use. I suppose I'm misunderstanding something. Also, if there is a better approach to this problem, please share.

Thanks!

ntfx
  • 113
  • 7
  • Basically what you are doing won't work since it depends on the order of the filter chain. I would look into creating a [custom authentication strategy for Warden](http://stackoverflow.com/questions/4223083/custom-authentication-strategy-for-devise) instead. However I have personally had tons of problems with using auth solutions based on IP when using a mobile connection - especially in 3 world countries. Checking the user agent is an arcane practice with little merit since it is so easily spoofed. – max Jan 20 '16 at 16:12
  • Thanks for the tip. I've tried writing a Warden strategy, however I've ran into two problems: 1) Warden seems to only authenticate on login and the goal here is to authenticate (authorise?) every request after user has logged in. 2) For some reason in the strategy even with ```false``` coming out of ```valid?``` Warden still executes the authenticate method, therefore breaking everything on login page. First issue makes second kind of irrelevant. – ntfx Jan 21 '16 at 09:22

1 Answers1

6

I've solved the issue by adding this to initializers/devise.rb

  Warden::Manager.after_authentication do |_user, auth, _opts|
    auth.raw_session['warden.user.ip'] = auth.request.ip
    auth.raw_session['warden.user.user_agent'] = auth.request.user_agent
  end

and this to application controller:

class ApplicationController < ActionController::Base

  before_action :authorize_ip_user_agent

  protected

  def authorize_ip_user_agent
    return true unless session['warden.user.ip']
    warden.logout if session['warden.user.ip'] != request.ip &&
      session['warden.user.user_agent'] != request.user_agent
  end
end
ntfx
  • 113
  • 7