{"id":12063,"date":"2025-02-18T07:12:40","date_gmt":"2025-02-18T07:12:40","guid":{"rendered":"https:\/\/www.bacancytechnology.com\/qanda\/?p=12063"},"modified":"2025-02-18T07:12:40","modified_gmt":"2025-02-18T07:12:40","slug":"run-code-after-user-login-with-devise-in-rails","status":"publish","type":"post","link":"https:\/\/www.bacancytechnology.com\/qanda\/ruby-on-rails\/run-code-after-user-login-with-devise-in-rails","title":{"rendered":"Ruby on Rails Devise Code After Login"},"content":{"rendered":"<h2>Solution 1: Subclass Devise&#8217;s Sessions Controller<\/h2>\n<p>A common way to add custom behavior after user login is to subclass Devise&#8217;s SessionsController and override the necessary actions. This approach is flexible and keeps the custom logic separate from the Devise setup.<\/p>\n<h3>Steps to Implement:<\/h3>\n<p><strong>1. Update your routes to point to the custom sessions controller:<\/strong><\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"ruby\">\r\n# config\/routes.rb\r\ndevise_for :users, controllers: { sessions: \"custom_sessions\" }\r\n<\/pre>\n<p><strong>2. Create a new sessions controller:<\/strong><\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"ruby\">\r\n# app\/controllers\/custom_sessions_controller.rb\r\nclass CustomSessionsController < Devise::SessionsController\r\n before_action :before_login, only: :create\r\n after_action :after_login, only: :create\r\n private\r\n def before_login\r\n   # Add custom logic to execute before login (optional).\r\n end\r\n def after_login\r\n   # Add custom logic to execute after login, e.g., creating a directory.\r\n   create_user_directory unless directory_exists?\r\n end\r\n def create_user_directory\r\n   # Example implementation\r\n   user_dir = Rails.root.join(\"user_data\", current_user.username)\r\n   FileUtils.mkdir_p(user_dir) unless Dir.exist?(user_dir)\r\n end\r\n def directory_exists?\r\n   Dir.exist?(Rails.root.join(\"user_data\", current_user.username))\r\n end\r\nend\r\n<\/pre>\n<p><strong>3. Test the changes by logging in and verifying that the custom code is executed.<\/strong><\/p>\n<h2>Solution 2: Use Warden Callbacks<\/h2>\n<p>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.<\/p>\n<h3>Steps to Implement:<\/h3>\n<p><strong>1. Add the Warden callback in devise.rb:<\/strong><\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"ruby\">\r\n# config\/initializers\/devise.rb\r\nWarden::Manager.after_authentication do |user, auth, opts|\r\n # Custom logic after login\r\n Rails.logger.info \"User #{user.email} logged in at #{Time.now}\"\r\n\r\n # Check and create directory for the user\r\n user_dir = Rails.root.join(\"user_data\", user.username)\r\n FileUtils.mkdir_p(user_dir) unless Dir.exist?(user_dir)\r\nend\r\n<\/pre>\n<p><strong>2. Restart your Rails server to apply the changes.<\/strong><\/p>\n<p><strong>Notes:<\/strong><\/p>\n<ul>\n<li>Place the callback inside or outside the Devise.setup block, depending on your preference. Either works.<\/li>\n<li>Ensure that the logic in this callback is performant since it runs during authentication.<\/li>\n<\/ul>\n<h2>Solution 3: Add a Custom Login Event Using ActiveSupport::Notifications<\/h2>\n<p>Another approach is to hook into Rails\u2019 ActiveSupport::Notifications to handle post-login events. This keeps the custom logic modular and independent of Devise\u2019s implementation.<\/p>\n<h3>Steps to Implement:<\/h3>\n<p><strong>1. Add the notification in sessions_controller.rb:<\/strong><\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"ruby\">\r\n# app\/controllers\/custom_sessions_controller.rb\r\nclass CustomSessionsController < Devise::SessionsController\r\n after_action :notify_login_event, only: :create\r\n private\r\n def notify_login_event\r\n   ActiveSupport::Notifications.instrument(\"user.logged_in\", user: current_user)\r\n end\r\nend\r\n<\/pre>\n<p><strong>2. Create a subscriber for the event:<\/strong><\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"ruby\">\r\n# config\/initializers\/login_subscriber.rb\r\nActiveSupport::Notifications.subscribe(\"user.logged_in\") do |name, start, finish, id, payload|\r\n user = payload[:user]\r\n Rails.logger.info \"User #{user.email} logged in!\"\r\n\r\n # Check and create directory for the user\r\n user_dir = Rails.root.join(\"user_data\", user.username)\r\n FileUtils.mkdir_p(user_dir) unless Dir.exist?(user_dir)\r\nend\r\n<\/pre>\n<h2>Solution 4: Use Devise Hooks<\/h2>\n<p>Devise provides built-in hooks like after_sign_in_path_for that can be customized to execute specific logic.<\/p>\n<h3>Steps to Implement:<\/h3>\n<p><strong>1. Override after_sign_in_path_for in ApplicationController<\/strong><\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"ruby\">\r\n# app\/controllers\/application_controller.rb\r\nclass ApplicationController < ActionController::Base\r\n protected\r\n\r\n def after_sign_in_path_for(resource)\r\n   # Custom logic after login\r\n   create_user_directory(resource) unless directory_exists?(resource)\r\n   super\r\n end\r\n\r\n private\r\n\r\n def create_user_directory(user)\r\n   user_dir = Rails.root.join(\"user_data\", user.username)\r\n   FileUtils.mkdir_p(user_dir) unless Dir.exist?(user_dir)\r\n end\r\n\r\n def directory_exists?(user)\r\n   Dir.exist?(Rails.root.join(\"user_data\", user.username))\r\n end\r\nend\r\n<\/pre>\n<p><strong>2. This method allows you to handle logic seamlessly after login without altering session controllers or Warden directly:<\/strong><\/p>\n<h2>Conclusion<\/h2>\n<p>Each approach serves a unique purpose based on your app's structure and needs:<\/p>\n<ul>\n<li>Use <strong>controller subclassing<\/strong> for flexibility and customization.<\/li>\n<li>Leverage <strong>Warden callbacks<\/strong> for direct integration with authentication flow.<\/li>\n<li>Apply <strong>ActiveSupport::Notifications<\/strong> for modular, event-driven logic.<\/li>\n<li>Override Devise hooks for quick, built-in post-login actions.<\/li>\n<\/ul>\n<p>Test each solution and choose the one that aligns best with your application's architecture and maintenance strategy.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Solution 1: Subclass Devise&#8217;s Sessions Controller A common way to add custom behavior after user login is to subclass Devise&#8217;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: # [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":12064,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"inline_featured_image":false,"footnotes":""},"categories":[11],"tags":[],"class_list":["post-12063","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-ruby-on-rails"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.bacancytechnology.com\/qanda\/wp-json\/wp\/v2\/posts\/12063"}],"collection":[{"href":"https:\/\/www.bacancytechnology.com\/qanda\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.bacancytechnology.com\/qanda\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.bacancytechnology.com\/qanda\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.bacancytechnology.com\/qanda\/wp-json\/wp\/v2\/comments?post=12063"}],"version-history":[{"count":1,"href":"https:\/\/www.bacancytechnology.com\/qanda\/wp-json\/wp\/v2\/posts\/12063\/revisions"}],"predecessor-version":[{"id":12065,"href":"https:\/\/www.bacancytechnology.com\/qanda\/wp-json\/wp\/v2\/posts\/12063\/revisions\/12065"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.bacancytechnology.com\/qanda\/wp-json\/wp\/v2\/media\/12064"}],"wp:attachment":[{"href":"https:\/\/www.bacancytechnology.com\/qanda\/wp-json\/wp\/v2\/media?parent=12063"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.bacancytechnology.com\/qanda\/wp-json\/wp\/v2\/categories?post=12063"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.bacancytechnology.com\/qanda\/wp-json\/wp\/v2\/tags?post=12063"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}