Solution 1: Subclass Devise’s Sessions Controller

A common way to add custom behavior after user login is to subclass Devise’s SessionsController and override the necessary actions. This approach is flexible and keeps the custom logic separate from the Devise setup.

Steps to Implement:

1. Update your routes to point to the custom sessions controller:

# config/routes.rb
devise_for :users, controllers: { sessions: "custom_sessions" }

2. Create a new sessions controller:

# app/controllers/custom_sessions_controller.rb
class CustomSessionsController < Devise::SessionsController
 before_action :before_login, only: :create
 after_action :after_login, only: :create
 private
 def before_login
   # Add custom logic to execute before login (optional).
 end
 def after_login
   # Add custom logic to execute after login, e.g., creating a directory.
   create_user_directory unless directory_exists?
 end
 def create_user_directory
   # Example implementation
   user_dir = Rails.root.join("user_data", current_user.username)
   FileUtils.mkdir_p(user_dir) unless Dir.exist?(user_dir)
 end
 def directory_exists?
   Dir.exist?(Rails.root.join("user_data", current_user.username))
 end
end

3. Test the changes by logging in and verifying that the custom code is executed.

Solution 2: Use Warden Callbacks

Devise is built on top of Warden, which provides callbacks to execute code during authentication events. This approach integrates directly into Devise's lifecycle without overriding controllers.

Steps to Implement:

1. Add the Warden callback in devise.rb:

# config/initializers/devise.rb
Warden::Manager.after_authentication do |user, auth, opts|
 # Custom logic after login
 Rails.logger.info "User #{user.email} logged in at #{Time.now}"

 # Check and create directory for the user
 user_dir = Rails.root.join("user_data", user.username)
 FileUtils.mkdir_p(user_dir) unless Dir.exist?(user_dir)
end

2. Restart your Rails server to apply the changes.

Notes:

  • Place the callback inside or outside the Devise.setup block, depending on your preference. Either works.
  • Ensure that the logic in this callback is performant since it runs during authentication.

Solution 3: Add a Custom Login Event Using ActiveSupport::Notifications

Another approach is to hook into Rails’ ActiveSupport::Notifications to handle post-login events. This keeps the custom logic modular and independent of Devise’s implementation.

Steps to Implement:

1. Add the notification in sessions_controller.rb:

# app/controllers/custom_sessions_controller.rb
class CustomSessionsController < Devise::SessionsController
 after_action :notify_login_event, only: :create
 private
 def notify_login_event
   ActiveSupport::Notifications.instrument("user.logged_in", user: current_user)
 end
end

2. Create a subscriber for the event:

# config/initializers/login_subscriber.rb
ActiveSupport::Notifications.subscribe("user.logged_in") do |name, start, finish, id, payload|
 user = payload[:user]
 Rails.logger.info "User #{user.email} logged in!"

 # Check and create directory for the user
 user_dir = Rails.root.join("user_data", user.username)
 FileUtils.mkdir_p(user_dir) unless Dir.exist?(user_dir)
end

Solution 4: Use Devise Hooks

Devise provides built-in hooks like after_sign_in_path_for that can be customized to execute specific logic.

Steps to Implement:

1. Override after_sign_in_path_for in ApplicationController

# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
 protected

 def after_sign_in_path_for(resource)
   # Custom logic after login
   create_user_directory(resource) unless directory_exists?(resource)
   super
 end

 private

 def create_user_directory(user)
   user_dir = Rails.root.join("user_data", user.username)
   FileUtils.mkdir_p(user_dir) unless Dir.exist?(user_dir)
 end

 def directory_exists?(user)
   Dir.exist?(Rails.root.join("user_data", user.username))
 end
end

2. This method allows you to handle logic seamlessly after login without altering session controllers or Warden directly:

Conclusion

Each approach serves a unique purpose based on your app's structure and needs:

  • Use controller subclassing for flexibility and customization.
  • Leverage Warden callbacks for direct integration with authentication flow.
  • Apply ActiveSupport::Notifications for modular, event-driven logic.
  • Override Devise hooks for quick, built-in post-login actions.

Test each solution and choose the one that aligns best with your application's architecture and maintenance strategy.

Support On Demand!

Ruby on Rails

Related Q&A