1

I'd like to be able to set the root url in routes.rb based on the logged in/out state of Authlogic. Here is an example of what I need to do:

root :to => 'users#index', :constraints => lambda { |request| # ??? }  
root :to => 'welcome#index'  
get '/' => 'users#index', :as => 'user_root'

This would enable logged in users to go to users#index and logged out users to go to welcome#index without doing a redirect. I do know how to set a before_filter and redirect accordingly, but I'd rather not do that.

I know this is possible with Devise but I need to solve this for Authlogic. Similar question here without an answer.

Community
  • 1
  • 1
Jey Balachandran
  • 3,585
  • 5
  • 27
  • 36

3 Answers3

2

Authlogic already have the machinery native (tested against Rails 6.0 and Authlogic 6.0):

class AuthlogicSignedInConstraint
  def matches?(request)
    Authlogic::Session::Base.controller = Authlogic::ControllerAdapters::AbstractAdapter.new(request)
    user_session = UserSession.find
    user_session && user_session.user
  end
end
2

You can create a custom route constraint pretty easily. Any object that responds to matches? can be handed to the :constraints route option. To check for Authlogic authentication, you would need to pull your Authlogic methods from your ApplicationController and put them somewhere else. I recommend using a concern for these anyways.

# app/concerns/authlogic_methods.rb
module AuthlogicHelpers
  extend ActiveSupport::Concern

  module InstanceMethods
    private # All methods are private

    def current_user_session
      return @current_user_session if defined?(@current_user_session)
      @current_user_session = UserSession.find
    end

    def current_user
      return @current_user if defined?(@current_user)
      @current_user = current_user_session && current_user_session.user
    end
  end
end

# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
  include AuthlogicHelpers
end

# config/initializers/route_constraints.rb
module RouteConstraints
  class LoggedIn
    include AuthlogicHelpers
    def matches?(request)
      current_user
    end
  end
end

# config/routes.rb
constraints RouteConstraints::LoggedIn.new do
  root :to => 'dashboard#show'
end

Note that we're instantiating a new RouteConstraints::LoggedIn object. This is because the Authlogic methods current_user and current_user_session are defined as instance methods and create instance variables — if they aren't separated for each request, you won't be sure a user is actually logged in or another one was logged in before.

You might run into problems if Authlogic tries to access params directly. In this case, you will probably need to override the UserSession.new method to accept the request object as an argument and call request.params to get the params.

coreyward
  • 77,547
  • 20
  • 137
  • 166
  • I found this answer while experiencing the same issue myself. However, it looks like this won't work with Rails 4 due to the changes either in Rails or AuthLogic. I think AuthLogic now requires a controller and access to cookies for it to be activated. Also the methods inside `AuthlogicHelpers#InstanceMethods ` should just be moved to the body of `AuthlogicHelpers`. Again likely a change with `ActiveSupport::Concern`. – Aaron Feb 25 '16 at 22:15
  • @Aaron I wouldn't bother with using Authlogic today. The `has_secure_password` support in Rails combined with a vanilla UserSession object and a sessions controller is all you need to implement authenticated sessions. – coreyward Feb 25 '16 at 22:29
  • Thanks @coreyward. I'll look into this. Unfortunately I'm upgrading a Rails 3 app to 4 and it has been using Authlogic for a while so it would be another set of changes I'd have to wait until the future to consider. – Aaron Feb 26 '16 at 16:31
  • @Aaron Ah, understandable. Migrating systems with existing users is difficult. Hopefully you can get Authlogic working for you under Rails 4. – coreyward Feb 26 '16 at 22:25
  • It's not an ideal solution but we can eagerly handle another root route by examining the request object and letting the controller redirect again if it happens to be invalid or spoofed. Something like `get '/', to: 'some_authed_controller#action', constraints: lambda {|req| req.env['rack.session']['some_user_key'].present? }` – Aaron Feb 27 '16 at 14:22
0

Authlogic in recent versions require a controller object to be instantiated otherwise it can't find the session data necessary for loading a session.

In fact, contained under authlogic/controller_adapters/ are adapters for various controller implementations: Rails Controllers, Sinatra and Rack.

The interface is fairly simple, so I wrote an adapter to work with the ActionDispatch::Request that one has access to within a constraint.

This has been tested on Rails 4.2, Rails 5 should be similar.

class AuthlogicRequestAdapter
  attr_reader :request

  def initialize(request)
    @request = request
  end

  def authenticate_with_http_basic
  end

  def cookies
    request.cookies
  end

  def params
    request.params
  end

  def session
    _, session_hash = Rails.application.config.session_store.new(Rails.application, Rails.application.config.session_options).load_session(request.env)
    session_hash
  end
end

Example of use in Rails

Route Constraint

class UserRouteConstraint
  def matches?(request)
    Authlogic::Session::Base.controller = AuthlogicRequestAdapter.new(request)
    user = UserSession.find

    if user&.record.present?
      true
    else
      false
    end
  end
end

routes.rb

MyRailsApp::Application.routes.draw do
  # ... other routes

  constraints(UserRouteContraint.new) do
    root :to => 'users#index'
  end

  root :to => 'welcome#index'  
  get '/' => 'users#index', :as => 'user_root'
end
chriscz
  • 169
  • 2
  • 9