### Install Devise Source: https://github.com/heartcombo/devise/blob/main/README.md Run the Devise installation generator to set up initial files and configurations. ```ruby rails generate devise:install ``` -------------------------------- ### Integration Test Setup (Minitest) Source: https://github.com/heartcombo/devise/wiki/How-To:-Test-controllers-with-Rails-(and-RSpec) Include Devise::Test::IntegrationHelpers and sign in a user within the setup method for integration tests. ```ruby class SomeIntegrationTest < ActionDispatch::IntegrationTest include Devise::Test::IntegrationHelpers def setup sign_in FactoryBot.create(:user) end end ``` -------------------------------- ### Install html2slim Gem Source: https://github.com/heartcombo/devise/wiki/How-To:-Create-Haml-and-Slim-Views Install the html2slim gem, which provides tools for converting ERB files to Slim format. ```sh gem install html2slim ``` -------------------------------- ### Controller Spec Example with Devise Login Source: https://github.com/heartcombo/devise/wiki/How-To:-Test-controllers-with-Rails-(and-RSpec) Example of a controller spec using the `login_admin` macro to ensure a current user exists and to perform actions like 'get index'. ```ruby describe MyController do login_admin it "should have a current_user" do # note the fact that you should remove the "validate_session" parameter if this was a scaffold-generated controller expect(subject.current_user).to_not eq(nil) end it "should get index" do # Note, rails 3.x scaffolding may add lines like get :index, {}, valid_session # the valid_session overrides the devise login. Remove the valid_session from your specs get 'index' response.should be_success end end ``` -------------------------------- ### Minimal Devise Links Setup Source: https://github.com/heartcombo/devise/wiki/Troubleshooting-Rails-7-and-Turbo-Drive A common setup for Devise links that conditionally displays user-specific links like email and logout, or login and registration links for non-logged-in users. ```ruby <% if current_user %> <%= link_to current_user.email, edit_user_registration_path %> <%= link_to "Log out", destroy_user_session_path, data: { turbo_method: :delete } %> <% else %> <%= link_to_unless_current "Log in", new_user_session_path %> <%= link_to_unless_current "Register", new_user_registration_path %> <% end %> ``` -------------------------------- ### Install html2haml Gem Source: https://github.com/heartcombo/devise/wiki/How-To:-Create-Haml-and-Slim-Views Install the html2haml gem, which is a tool for converting HTML (or ERB) files to Haml format. ```sh gem install html2haml ``` -------------------------------- ### Run All Devise Tests Source: https://github.com/heartcombo/devise/blob/main/README.md Execute the entire Devise test suite by running the `bin/test` command from the project's top-level directory after installing dependencies. ```bash bin/test ``` -------------------------------- ### Controller Test Setup (Minitest) Source: https://github.com/heartcombo/devise/wiki/How-To:-Test-controllers-with-Rails-(and-RSpec) Include Devise::Test::ControllerHelpers for Devise >= 4.2.0 or Devise::TestHelpers for older versions. Set up the request environment and sign in a user before each test. ```ruby class SomeControllerTest < ActionController::TestCase # For Devise >= 4.2.0 include Devise::Test::ControllerHelpers # Use the following instead if you are on Devise <= 4.2.0 # include Devise::TestHelpers def setup @request.env["devise.mapping"] = Devise.mappings[:admin] sign_in FactoryBot.create(:admin) end end ``` -------------------------------- ### Custom Users Sessions Controller Example Source: https://github.com/heartcombo/devise/blob/main/README.md An example of a custom controller for user sessions, inheriting from Devise::SessionsController. This allows overriding or extending actions. ```ruby class Users::SessionsController < Devise::SessionsController # GET /resource/sign_in # def new # super # end ... end ``` -------------------------------- ### Devise Routes Configuration Source: https://github.com/heartcombo/devise/wiki/How-To:-sign-in-and-out-a-user-in-Request-type-specs-(specs-tagged-with-type:-:request) Example of configuring Devise routes in `config/routes.rb`, including setting up an authenticated root path and a default root path. ```ruby Rails.application.routes.draw do devise_for :users authenticated do root to: "secret#index", as: :authenticated_root end root to: "home#index" # ... end ``` -------------------------------- ### Configure Devise Controller Layouts in config/application.rb Source: https://github.com/heartcombo/devise/wiki/How-To:-Create-custom-layouts Use `config.to_prepare` in `config/application.rb` to set layouts for specific Devise controllers. This example sets a layout for `Devise::SessionsController`. ```ruby config.to_prepare do # Configure single controller layout Devise::SessionsController.layout "layout_for_sessions_controller" # Or to configure mailer layout Devise::Mailer.layout "email" # email.haml or email.erb end ``` -------------------------------- ### Define User Abilities with CanCan Source: https://github.com/heartcombo/devise/wiki/How-To:-Manage-Users-with-an-Admin-Role-(CanCan-method) Sets up authorization rules. This example grants all privileges to users with the 'admin' role. Customize this class to define specific permissions for different roles. ```ruby class Ability include CanCan::Ability def initialize(user) can :manage, :all if user.role == "admin" end end ``` -------------------------------- ### Configure Routes for Custom Sessions Controller Source: https://github.com/heartcombo/devise/wiki/How-To:-redirect-to-a-specific-page-on-successful-(oauth)-sign-in Example of how to configure routes in `config/routes.rb` to use a custom `SessionsController` for Devise. ```ruby devise_for :users, controllers: {sessions: 'users/sessions'} ``` -------------------------------- ### Controller Spec: Allow Authenticated Access Source: https://github.com/heartcombo/devise/wiki/How-To:-Stub-authentication-in-controller-specs Example controller spec demonstrating how to test for allowed access when a user is signed in using the `sign_in` helper. ```ruby it "allows authenticated access" do sign_in get :index expect(response).to be_success end ``` -------------------------------- ### Simple User Sign-in and Sign-out in Request Spec Source: https://github.com/heartcombo/devise/wiki/How-To:-sign-in-and-out-a-user-in-Request-type-specs-(specs-tagged-with-type:-:request) This example demonstrates signing a user in and out within a request spec using the `sign_in` and `sign_out` helpers. Ensure `rails-controller-testing` gem is added to your Gemfile for `expect(response).to render_template`. ```ruby #./spec/requests/sessions_spec.rb require 'rails_helper' RSpec.describe "Sessions" do it "signs user in and out" do # user = create(:user) ## uncomment if using FactoryBot # user = User.create(email: 'test@test.com', password: "password", password_confirmation: "password") ## uncomment if not using FactoryBot sign_in user get root_path expect(response).to render_template(:index) # add gem 'rails-controller-testing' to your Gemfile first. sign_out user get root_path expect(response).not_to render_template(:index) # add gem 'rails-controller-testing' to your Gemfile first. end end ``` -------------------------------- ### Role Enum Methods Source: https://github.com/heartcombo/devise/wiki/How-To:-Add-an-Admin-Role Examples of methods available for interacting with the role enum. ```ruby user.admin? user.admin! user.role user.user? user.vip? ``` -------------------------------- ### Devise PasswordsController Respond With Logic Source: https://github.com/heartcombo/devise/wiki/How-To:-Redirect-URL-after-sending-reset-password-instructions This is an example of the default `respond_with` logic in Devise's PasswordsController, which is often overridden in custom controllers. ```ruby respond_with resource, location: after_resetting_password_path_for(resource) ``` -------------------------------- ### Generate Devise Views and Convert to Haml (Windows) Source: https://github.com/heartcombo/devise/wiki/How-To:-Create-Haml-and-Slim-Views On Windows, generate Devise ERB views. Install html2haml, then use a for loop to convert ERB to Haml recursively and delete the original ERB files. ```cmd rails generate devise:views gem install html2haml for /r app\views\devise %I in (*) do html2haml -e "%I" "%~dpnI.haml" del /f /s /q app\views\devise\*.erb ``` -------------------------------- ### Generate Devise Views and Convert to Slim (Windows) Source: https://github.com/heartcombo/devise/wiki/How-To:-Create-Haml-and-Slim-Views For Windows users, after generating Devise ERB views, install html2slim. Utilize a for loop to convert ERB to Slim recursively and remove the original ERB files. ```cmd rails generate devise:views gem install html2slim for /r app\views\devise %I in (*) do erb2slim "%I" "%~dpnI.slim" del /f /s /q app\views\devise\*.erb ``` -------------------------------- ### Configure Navigational Formats for Devise Source: https://github.com/heartcombo/devise/wiki/Troubleshooting-Rails-7-and-Turbo-Drive If you need to use `rails-ujs` with Rails 7, ensure `turbo_stream` is included in `config.navigational_formats` within the Devise setup. ```ruby Devise.setup do |config| # ... config.navigational_formats = ['*/*', :html, :turbo_stream] # ... end ``` -------------------------------- ### Configure Model Hashing Options Source: https://github.com/heartcombo/devise/blob/main/README.md Configure Devise modules within your model. This example shows setting the 'stretches' option for the database authenticatable module. ```ruby devise :database_authenticatable, :registerable, :confirmable, :recoverable, stretches: 13 ``` -------------------------------- ### Customize Sign-Out Redirect Path (Multiple Models) Source: https://github.com/heartcombo/devise/wiki/How-To:-Change-the-redirect-path-after-destroying-a-session-i.e.-signing-out Handle sign-out redirects for multiple models by checking `resource_or_scope`. This example redirects users to their respective login pages or the root path. ```ruby class ApplicationController < ActionController::Base private # Overwriting the sign_out redirect path method def after_sign_out_path_for(resource_or_scope) if resource_or_scope == :user new_user_session_path elsif resource_or_scope == :admin new_admin_session_path else root_path end end end ``` -------------------------------- ### Custom Sign-out Route with Dynamic HTTP Method Source: https://github.com/heartcombo/devise/wiki/How-To:-Change-the-default-sign_in-and-sign_out-routes When using `:sign_out_via`, the HTTP method for sign-out can vary. This example uses `match` with `via` to dynamically handle the sign-out HTTP method. ```ruby devise_for :users, skip: [:sessions] as :user do get 'signin', to: 'devise/sessions#new', as: :new_user_session post 'signin', to: 'devise/sessions#create', as: :user_session match 'signout', to: 'devise/sessions#destroy', as: :destroy_user_session, via: Devise.mappings[:user].sign_out_via end ``` -------------------------------- ### Implement Custom Failure App Logic Source: https://github.com/heartcombo/devise/wiki/How-To:-Do-not-redirect-to-login-page-after-session-timeout Define the `redirect` method in your custom failure app. This example redirects to the `attempted_path` if the failure message is `:timeout`, otherwise it calls the superclass's `redirect` method. ```ruby class CustomFailureApp < Devise::FailureApp def redirect store_location! message = warden.message || warden_options[:message] if message == :timeout redirect_to attempted_path else super end end end ``` -------------------------------- ### Execute Migration Source: https://github.com/heartcombo/devise/wiki/How-To:-Add-an-Admin-Role Run this command to apply the pending database migrations. ```bash $ rails db:migrate ``` -------------------------------- ### Log in a Test User with FactoryBot Source: https://github.com/heartcombo/devise/wiki/How-To:-Test-with-Capybara Simplify user creation and login using FactoryBot. ```ruby user = FactoryBot.create(:user) login_as(user, :scope => :user) ``` -------------------------------- ### Migrate Database Source: https://github.com/heartcombo/devise/wiki/How-To:-Allow-users-to-sign-in-with-something-other-than-their-email-address Run the database migration to apply the changes, including the addition of the new custom authentication field. ```bash rake db:migrate ``` -------------------------------- ### Implement Guest User Logic in Application Controller Source: https://github.com/heartcombo/devise/wiki/How-To:-Create-a-guest-user This code defines methods to manage guest users, including finding, creating, and handling the transition to a logged-in user. It ensures a guest user is available when no logged-in user exists. ```ruby class ApplicationController < ActionController::Base protect_from_forgery # if user is logged in, return current_user, else return guest_user def current_or_guest_user if current_user if session[:guest_user_id] && session[:guest_user_id] != current_user.id logging_in # reload guest_user to prevent caching problems before destruction guest_user(with_retry = false).try(:reload).try(:destroy) session[:guest_user_id] = nil end current_user else guest_user end end # find guest_user object associated with the current session, # creating one as needed def guest_user(with_retry = true) # Cache the value the first time it's gotten. @cached_guest_user ||= User.find(session[:guest_user_id] ||= create_guest_user.id) rescue ActiveRecord::RecordNotFound # if session[:guest_user_id] invalid session[:guest_user_id] = nil guest_user if with_retry end private # called (once) when the user logs in, insert any code your application needs # to hand off from guest_user to current_user. def logging_in # For example: # guest_comments = guest_user.comments.all # guest_comments.each do |comment| # comment.user_id = current_user.id # comment.save! # end end def create_guest_user u = User.new(name: "guest", email: "guest_#{Time.now.to_i}#{rand(100)}@example.com") u.save!(validate: false) session[:guest_user_id] = u.id u end end ``` -------------------------------- ### Get Current Signed-In User Source: https://github.com/heartcombo/devise/blob/main/README.md Helper method to retrieve the currently signed-in user object. ```ruby current_user ``` -------------------------------- ### Destroy User Action Source: https://github.com/heartcombo/devise/wiki/How-To:-Manage-users-through-a-CRUD-interface Example of a destroy action in a UsersController, handling JSON, XML, and HTML responses. ```ruby def destroy @user.destroy! respond_to do |format| format.json { respond_to_destroy(:ajax) } format.xml { head :ok } format.html { respond_to_destroy(:html) } end end ``` -------------------------------- ### Add OmniAuth Azure AD V2 Gem Source: https://github.com/heartcombo/devise/wiki/Microsoft-Azure-Active-Directory-(Azure-AD)-was-renamed-to-Microsoft-Entra-ID Add the omniauth-azure-activedirectory-v2 gem to your Gemfile and run bundle install. ```ruby # Gemfile.rb gem 'omniauth-azure-activedirectory-v2' ``` -------------------------------- ### Set up a Second Devise Model (Admin) Source: https://github.com/heartcombo/devise/blob/main/README.md Configure a second Devise model, such as 'Admin', by creating its migration, defining its devise modules, setting up routes, and using specific before_action filters and helper methods. ```ruby # Create a migration with the required fields create_table :admins do |t| t.string :email t.string :encrypted_password t.timestamps null: false end # Inside your Admin model devise :database_authenticatable, :timeoutable # Inside your routes devise_for :admins # Inside your protected controller before_action :authenticate_admin! # Inside your controllers and views admin_signed_in? current_admin admin_session ``` -------------------------------- ### Basic OmniAuth Callbacks Controller Source: https://github.com/heartcombo/devise/wiki/OmniAuth:-Overview Create a controller that inherits from `Devise::OmniauthCallbacksController` to handle authentication callbacks. This is a minimal setup. ```ruby class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController end ``` -------------------------------- ### Configure OmniAuth in devise.rb Source: https://github.com/heartcombo/devise/wiki/OmniAuth-with-multiple-models This is the original configuration for OmniAuth within Devise's setup. It should be moved to a separate file for multi-model support. ```ruby Devise.setup do |config| config.omniauth :twitter, ENV['TWITTER_KEY'], ENV['TWITTER_SECRET'] end ``` -------------------------------- ### Configure Devise Routes Source: https://github.com/heartcombo/devise/wiki/How-To:-Change-an-already-existing-table-to-add-devise-required-columns Add this line to your `routes.rb` file to enable Devise for the users model. This sets up the necessary routes for authentication. ```ruby devise_for :users ``` -------------------------------- ### OmniAuth Routes for Rails 5 Source: https://github.com/heartcombo/devise/wiki/OmniAuth-with-multiple-models Define OmniAuth routes for Rails 5. This example specifies the controller and constraints for authentication callbacks. ```ruby get "/auth/:action/callback", :controller => "authentications", :constraints => { :action => /twitter|google/ } ``` -------------------------------- ### Mongoid Model Fields Before Devise 2.0 Source: https://github.com/heartcombo/devise/wiki/How-To:-Upgrade-to-Devise-2.0-migration-schema-style Before Devise 2.0, Devise automatically configured MongoDB. This example shows the implicit configuration. ```ruby ## Database authenticatable field :email, :type => String, :null => false field :encrypted_password, :type => String, :null => false ## Recoverable field :reset_password_token, :type => String field :reset_password_sent_at, :type => Time ## Rememberable field :remember_created_at, :type => Time ## Trackable field :sign_in_count, :type => Integer field :current_sign_in_at, :type => Time field :last_sign_in_at, :type => Time field :current_sign_in_ip, :type => String field :last_sign_in_ip, :type => String ## Encryptable # field :password_salt, :type => String ## Confirmable # field :confirmation_token, :type => String # field :confirmed_at, :type => Time # field :confirmation_sent_at, :type => Time # field :unconfirmed_email, :type => String # Only if using reconfirmable ## Lockable # field :failed_attempts, :type => Integer # Only if lock strategy is :failed_attempts # field :unlock_token, :type => String # Only if unlock strategy is :email or :both # field :locked_at, :type => Time # Token authenticatable # field :authentication_token, :type => String ## Invitable # field :invitation_token, :type => String ``` -------------------------------- ### Devise Root Route for Rails 6 Source: https://github.com/heartcombo/devise/wiki/How-To:-Redirect-to-a-specific-page-on-successful-sign-in Defines the root route for Devise using the 'get' method, recommended for Rails 6. ```ruby get '/user' => "welcome#index", :as => :user_root ``` -------------------------------- ### Define Custom Sign-in and Sign-out Routes with Skip Sessions Source: https://github.com/heartcombo/devise/wiki/How-To:-Change-the-default-sign_in-and-sign_out-routes Skip default session routes and define custom sign-in and sign-out routes using `as`. This provides full control over session-related URLs. ```ruby devise_for :users, skip: [:sessions] as :user do get 'signin', to: 'devise/sessions#new', as: :new_user_session post 'signin', to: 'devise/sessions#create', as: :user_session delete 'signout', to: 'devise/sessions#destroy', as: :destroy_user_session end ``` -------------------------------- ### Devise Routes for Rails 5.1+ Source: https://github.com/heartcombo/devise/wiki/How-To:-Define-a-different-root-route-for-logged-out-users Configure Devise routes using `authenticated` for authenticated users. This setup is compatible with Rails 5.1 and later. ```ruby Rails.application.routes.draw do devise_for :users authenticated :user do root 'secret#index', as: :authenticated_root end root "home#index" end ``` -------------------------------- ### Configure OmniAuth Routes for Users Source: https://github.com/heartcombo/devise/wiki/OmniAuth:-Overview Set up routes for OmniAuth callbacks and define sign-in and sign-out paths when using OmniAuth exclusively. ```ruby devise_for :users, :controllers => { :omniauth_callbacks => "users/omniauth_callbacks" } devise_scope :user do get 'sign_in', :to => 'devise/sessions#new', :as => :new_user_session get 'sign_out', :to => 'devise/sessions#destroy', :as => :destroy_user_session end ``` -------------------------------- ### Enable Autoloading for Lib Directory Source: https://github.com/heartcombo/devise/wiki/How-To:-Redirect-to-a-specific-page-when-the-user-email-is-not-confirmed? If `CustomFailureApp` is located in the `/lib` directory, ensure it's autoloaded by adding `config.autoload_paths` to your `application.rb` file. ```ruby config.autoload_paths << Rails.root.join('lib').join('devise') ``` -------------------------------- ### Alternative Model Scoped Helpers Source: https://github.com/heartcombo/devise/blob/main/README.md Example of Devise helpers when the model is named 'Member' instead of 'User'. Adjust helper names accordingly. ```ruby before_action :authenticate_member! member_signed_in? current_member member_session ``` -------------------------------- ### Send Confirmation Instructions to All Users Source: https://github.com/heartcombo/devise/wiki/How-To:-Add-:confirmable-to-Users Iterate through all users and call `send_confirmation_instructions` on each to send confirmation emails. This is useful after enabling confirmable for an existing user base. ```ruby User.find_each { |user| user.send_confirmation_instructions } ``` -------------------------------- ### Log in a Test User with Warden Source: https://github.com/heartcombo/devise/wiki/How-To:-Test-with-Capybara Use the `login_as` helper with a user resource and specify the scope. ```ruby user = User.create!(:email => 'test@example.com', :password => 'f4k3p455w0rd') login_as(user, :scope => :user) ``` -------------------------------- ### Configure Devise Initializer with REST_AUTH_SITE_KEY Source: https://github.com/heartcombo/devise/wiki/How-To:-Migrate-from-restful_authentication-to-Devise If REST_AUTH_SITE_KEY is set, configure Devise with stretches set to 10 and the pepper set to your REST_AUTH_SITE_KEY value. ```ruby config.stretches = 10 config.pepper = "" ``` -------------------------------- ### HTTP Basic Authentication in Tests Source: https://github.com/heartcombo/devise/wiki/How-To:-Use-HTTP-Auth-Basic-with-Devise This helper method allows you to simulate HTTP Basic Authentication in your tests when not using session store. Ensure the user's password matches the one provided. ```ruby def sign_in_basic(user) request.env['HTTP_AUTHORIZATION'] = ActionController::HttpAuthentication::Basic.encode_credentials(user.email, "password") end ``` -------------------------------- ### OmniAuth Routes for Rails 3 and 4 Source: https://github.com/heartcombo/devise/wiki/OmniAuth-with-multiple-models Define OmniAuth routes for Rails 3 and 4. This example directs specific authentication callbacks to the `authentications` controller. ```ruby get "/auth/:action/callback", :to => "authentications", :constraints => { :action => /twitter|google/ } ``` -------------------------------- ### Controller Spec: Block Unauthenticated Access Source: https://github.com/heartcombo/devise/wiki/How-To:-Stub-authentication-in-controller-specs Example controller spec demonstrating how to test for blocked access when no user is signed in using the `sign_in nil` helper. ```ruby it "blocks unauthenticated access" do sign_in nil get :index expect(response).to redirect_to(new_user_session_path) end ``` -------------------------------- ### Run Devise Tests for a Specific File Source: https://github.com/heartcombo/devise/blob/main/README.md To focus testing on a particular file, provide its path as an argument to the `bin/test` command. This is useful for verifying changes in a specific model or controller. ```bash bin/test test/models/trackable_test.rb ``` -------------------------------- ### Override after_confirmation_path_for for Devise 3/4 Source: https://github.com/heartcombo/devise/wiki/How-To:-Email-only-sign-up Customize the redirect path after a user confirms their email. This example redirects to the password edit page, generating a reset token. ```ruby protected def after_confirmation_path_for(resource_name, resource) token = resource.send(:set_reset_password_token) edit_password_path(resource, reset_password_token: token) end ``` -------------------------------- ### Implement password_match? and overwrite password_required? Source: https://github.com/heartcombo/devise/wiki/How-To:-Email-only-sign-up Add these methods to your User model to ensure password confirmation matches and to allow sign-ups without an initial password. ```ruby class User < ActiveRecord::Base def password_required? super if confirmed? end def password_match? self.errors[:password] << "can't be blank" if password.blank? self.errors[:password_confirmation] << "can't be blank" if password_confirmation.blank? self.errors[:password_confirmation] << "does not match password" if password != password_confirmation password == password_confirmation && !password.blank? end end ``` -------------------------------- ### Seed Initial Roles Source: https://github.com/heartcombo/devise/wiki/How-To:-Add-a-default-role-to-a-User Populate the Role table with predefined roles using `find_or_create_by` to ensure they exist. Run `rake db:seed` to execute. ```ruby ['registered', 'banned', 'moderator', 'admin'].each do |role| Role.find_or_create_by({name: role}) end ``` -------------------------------- ### Configure Devise Omniauth Routes for Locales Source: https://github.com/heartcombo/devise/wiki/How-To:-OmniAuth-inside-localized-scope Define routes in `routes.rb` to support localized omniauth callbacks. This setup ensures that the locale is considered when initiating omniauth authentication. ```ruby Rails.application.routes.draw do # We need to define devise_for just omniauth_callbacks:auth_callbacks otherwise it does not work with scoped locales # see https://github.com/plataformatec/devise/issues/2813 devise_for :users, only: :omniauth_callbacks, controllers: { omniauth_callbacks: 'omniauth_callbacks' } scope '(:locale)' do # We define here a route inside the locale thats just saves the current locale in the session get 'omniauth/:provider' => 'omniauth#localized', as: :localized_omniauth devise_for :users, skip: :omniauth_callbacks, controllers: { passwords: 'passwords', registrations: 'registrations', omniauth_callbacks: 'omniauth_callbacks' } end end ``` -------------------------------- ### Warden Strategy for Guest User Authentication Source: https://github.com/heartcombo/devise/wiki/How-To:-Create-a-guest-user This Warden strategy is used to authenticate guest users based on the presence of `session[:guest_user_id]`. It should be added to Devise's setup. ```ruby Warden::Strategies.add(:guest_user) do def valid? session[:guest_user_id].present? end def authenticate! u = User.where(id: session[:guest_user_id]).first success!(u) if u.present? end end ``` -------------------------------- ### Configure Router to Use Custom Controllers Source: https://github.com/heartcombo/devise/blob/main/README.md Update your routes to direct Devise-related requests to your custom controllers. This example shows how to use a custom sessions controller for users. ```ruby devise_for :users, controllers: { sessions: 'users/sessions' } ``` -------------------------------- ### Define Custom User Root Path Source: https://github.com/heartcombo/devise/wiki/How-To:-Redirect-to-a-specific-page-on-successful-sign-in,-sign-up,-or-sign-out Devise uses `user_root_path` (or similar, like `admin_root_path`) as the default redirect after sign-in if defined. This example shows how to define such a route. ```ruby match '/welcome' => "welcome#index", as: :user_root Using Rails 4 get '/welcome' => "welcome#index", as: :user_root ``` -------------------------------- ### Implement `new_with_session` for User Model Source: https://github.com/heartcombo/devise/wiki/OmniAuth:-Overview Implement this method in your User model to copy data from the session when a user is initialized before sign-up, such as an email from Facebook data. ```ruby class User < ApplicationRecord def self.new_with_session(params, session) super.tap do |user| if data = session["devise.facebook_data"] && session["devise.facebook_data"]["extra"]["raw_info"] user.email = data["email"] if user.email.blank? end end end end ``` -------------------------------- ### Mount Resque Server with Devise Authorization Source: https://github.com/heartcombo/devise/wiki/How-To:-Protect-Resque-Web-with-Devise Mount the Resque server and apply additional authorization checks using Devise. This example restricts access to users who are administrators. ```ruby #routes.rb ... admin_constraint = lambda do |request| current_user = request.env['warden'].user current_user.present? && current_user.respond_to?(:is_admin?) && current_user.is_admin? end constraints admin_constraint do mount Resque::Server.new, at: '/resque-web' end ... ``` -------------------------------- ### Define a User Factory for FactoryBot Source: https://github.com/heartcombo/devise/wiki/How-To:-Test-with-Capybara Create a user factory in your factories file to use with FactoryBot. ```ruby FactoryBot.define do factory :user do email { 'test@example.com' } password { 'f4k3p455w0rd' } # using dynamic attributes over static attributes in FactoryBot # if needed # is_active true end end ``` -------------------------------- ### Set Layout for Individual Devise Controller Source: https://github.com/heartcombo/devise/wiki/How-To:-Create-custom-layouts Subclass a Devise controller and override the `layout` method. This example sets a specific layout for the `edit` action of the registrations controller. ```ruby # With custom single controller class Users::RegistrationsController < Devise::RegistrationsController layout "devise", only: [:edit] end # then add this to your routes: devise_for :users, controllers: { registrations: 'users/registrations' } ``` -------------------------------- ### Redirect After Sign-in (Non-OAuth) Source: https://github.com/heartcombo/devise/wiki/How-To:-redirect-to-a-specific-page-on-successful-(oauth)-sign-in Handles redirects after a standard sign-in, prioritizing the stored location, then the referer, and finally the root path. It includes a check to prevent redirect loops by comparing the current referer with the sign-in URL. This method should be placed in `ApplicationController`. ```ruby class ApplicationController < ActionController::Base protect_from_forgery protected def after_sign_in_path_for(resource) sign_in_url = new_user_session_url if request.referer == sign_in_url super else stored_location_for(resource) || request.referer || root_path end end end ``` -------------------------------- ### Basic User Authentication Links Source: https://github.com/heartcombo/devise/wiki/How-To:-Add-sign_in,-sign_out,-and-sign_up-links-to-your-layout-template Displays login/register links for guests and user email/logout links for signed-in users. Ensure `user_signed_in?` helper is available. ```erb <% if user_signed_in? %> <%= link_to current_user.email, edit_user_registration_path %> <%= link_to "Log out", destroy_user_session_path, method: :delete %> <% else %> <%= link_to "Log in", new_user_session_path %> <%= link_to "Register", new_user_registration_path %> <% end %> ``` -------------------------------- ### Configure Devise Routes with Custom Controllers Source: https://github.com/heartcombo/devise/wiki/Tool:-Generate-and-customize-controllers Add the generated controllers to your `devise_for` routes by specifying the controller path. This example maps the 'users' scope to the custom 'users/registrations' controller. ```ruby devise_for :users, controllers: { registrations: 'users/registrations' } ``` -------------------------------- ### Seamless User Login After Registration Source: https://github.com/heartcombo/devise/wiki/How-To:-Automatically-generate-password-for-users-(simpler-registration) After creating a new user with a generated password, use this to log them in seamlessly. This is useful for providing an immediate logged-in experience. ```ruby sign_in(:user, user) ``` -------------------------------- ### Generate Specific Devise Controller with -c Flag Source: https://github.com/heartcombo/devise/wiki/Tool:-Generate-and-customize-controllers Generate a controller for one or more modules using the `-c` flag. This example generates the registrations controller within the users scope. ```ruby $ rails g devise:controllers users -c=registrations #=> `/controllers/users/registrations_controller`.rb` ``` -------------------------------- ### Expose current_or_guest_user to Views Source: https://github.com/heartcombo/devise/wiki/How-To:-Create-a-guest-user Add `helper_method :current_or_guest_user` to the controller to make the `current_or_guest_user` method accessible within views. ```ruby helper_method :current_or_guest_user ``` -------------------------------- ### Customize Devise Route Names Source: https://github.com/heartcombo/devise/blob/main/README.md Customize the path names for Devise routes using the `path_names` option within `devise_for`. This example renames several common Devise paths. ```ruby devise_for :users, path: 'auth', path_names: { sign_in: 'login', sign_out: 'logout', password: 'secret', confirmation: 'verification', unlock: 'unblock', registration: 'register', sign_up: 'cmon_let_me_in' } ``` -------------------------------- ### Database Migration for Confirmable Fields Source: https://github.com/heartcombo/devise/wiki/How-To:-Add-:confirmable-to-Users This migration adds the necessary columns (`confirmation_token`, `confirmed_at`, `confirmation_sent_at`) to the users table and indexes the confirmation token. It also updates all existing users to be confirmed. ```ruby class AddConfirmableToDevise < ActiveRecord::Migration # Note: You can't use change, as User.update_all will fail in the down migration def up add_column :users, :confirmation_token, :string add_column :users, :confirmed_at, :datetime add_column :users, :confirmation_sent_at, :datetime # add_column :users, :unconfirmed_email, :string # Only if using reconfirmable add_index :users, :confirmation_token, unique: true # User.reset_column_information # Need for some types of updates, but not for update_all. # To avoid a short time window between running the migration and updating all existing # users as confirmed, do the following User.update_all confirmed_at: DateTime.now # All existing user accounts should be able to log in after this. end def down remove_index :users, :confirmation_token remove_columns :users, :confirmation_token, :confirmed_at, :confirmation_sent_at # remove_columns :users, :unconfirmed_email # Only if using reconfirmable end end ``` -------------------------------- ### Devise User Fixture for Testing Source: https://github.com/heartcombo/devise/wiki/How-To:-Use-HTTP-Auth-Basic-with-Devise Example of a YAML fixture for a Devise user, demonstrating how to set the encrypted password for testing purposes. This assumes 'password' is the plain text password. ```yaml one: email: 'some@user.com' encrypted_password: <%= Devise::Encryptor.digest(User, 'password') %> ``` -------------------------------- ### Basic CSS for Horizontal Menu Source: https://github.com/heartcombo/devise/wiki/How-To:-Add-sign_in,-sign_out,-and-sign_up-links-to-your-layout-template Provides basic styling for a horizontal navigation menu using CSS. This is intended to be applied to `ul.hmenu` elements. ```css ul.hmenu { list-style: none; margin: 0 0 2em; padding: 0; } ul.hmenu li { display: inline; } ``` -------------------------------- ### Custom Sessions Controller for Redirect Source: https://github.com/heartcombo/devise/wiki/How-To:-redirect-to-a-specific-page-on-successful-(oauth)-sign-in Example of a custom `SessionsController` that stores a redirect location from `params[:redirect_to]` before calling the superclass's `new` action. This requires corresponding route configuration. ```ruby module Users class SessionsController < Devise::SessionsController def new self.resource = resource_class.new(sign_in_params) store_location_for(resource, params[:redirect_to]) super end end end ``` -------------------------------- ### Sign in User from Controller Source: https://github.com/heartcombo/devise/wiki/How-To:-Sign-in-from-a-controller Use `sign_in` to authenticate a user from a controller. You can pass a user object directly or find it using parameters. Ensure the user is valid before signing in. ```ruby sign_in(User.find(params[:id]), scope: :user) ``` -------------------------------- ### Configure Session Store for Any Domain Source: https://github.com/heartcombo/devise/wiki/How-To:-Use-subdomains This configuration allows session cookies to work with any domain. It requires setting `domain: :all` and `tld_length` in `config/initializers/session_store.rb`. ```ruby Rails.application.config.session_store :cookie_store, key: '_my_app_session', domain: :all, tld_length: 2 ``` -------------------------------- ### Create Migration for Trackable Columns Source: https://github.com/heartcombo/devise/wiki/How-To:-Add-:trackable-to-Users Generate a new migration file to add the necessary columns for the trackable module to the users table. This includes sign-in counts and timestamps. ```ruby class AddTrackableToDevise < ActiveRecord::Migration def up add_column :users, :sign_in_count, :integer, default: 0, null: false add_column :users, :current_sign_in_at, :datetime add_column :users, :last_sign_in_at, :datetime add_column :users, :current_sign_in_ip, :inet add_column :users, :last_sign_in_ip, :inet end def down remove_columns :users, :sign_in_count, :current_sign_in_at, :last_sign_in_at, :current_sign_in_ip, :last_sign_in_ip end end ``` -------------------------------- ### Override Devise Controller Redirects Source: https://github.com/heartcombo/devise/wiki/How-To:-redirect-to-a-specific-page-on-successful-(oauth)-sign-in Provides examples for overriding `after_sign_up_path_for`, `after_update_path_for`, and `after_resetting_password_path_for` in custom controllers to prevent redirect loops. This involves defining custom controller files and updating routes. ```ruby # routes.rb devise_for :users, controllers: { registrations: 'users/registrations', passwords: 'users/passwords' } ``` ```ruby # users/registrations_controller.rb class Users::RegistrationsController < Devise::RegistrationsController protected def after_sign_up_path_for(resource) signed_in_root_path(resource) end def after_update_path_for(resource) signed_in_root_path(resource) end end ``` ```ruby # users/passwords_controller.rb class Users::PasswordsController < Devise::PasswordsController protected def after_resetting_password_path_for(resource) signed_in_root_path(resource) end end ``` -------------------------------- ### Configure Permitted Parameters for Devise Source: https://github.com/heartcombo/devise/wiki/Lockdown-Username-Change Customize Devise's sign-up and account update parameters by overriding `configure_permitted_parameters`. This example permits 'username' for sign-up but restricts 'account_update' to 'email', 'password', and 'remember_me'. ```ruby class ApplicationController < ActionController::Base before_action :configure_permitted_parameters, if: :devise_controller? protected def configure_permitted_parameters added_attrs = [:username, :email, :password, :password_confirmation, :remember_me] lockdown = [:email, :password, :password_confirmation, :remember_me] devise_parameter_sanitizer.permit :sign_up, keys: added_attrs #You remove the :username this way it won't accept it when you change it. devise_parameter_sanitizer.permit :account_update, keys: lockdown end end ``` -------------------------------- ### Create Migration for Reindexing Users by Subdomain Source: https://github.com/heartcombo/devise/wiki/How-to:-Scope-login-to-subdomain For existing projects, create a new migration to remove the old email index and add a new index scoped to email and subdomain. ```sh rails g migration reindex_users_by_email_and_subdomain ``` ```ruby # db/migrate/XXX_reindex_users_by_email_and_subdomain.rb def up remove_index :users, :email add_index :users, [:email, :subdomain], :unique => true end def down remove_index :users, [:email, :subdomain] add_index :users, :email, :unique => true end ``` -------------------------------- ### Alternative: Using 'as' for Custom Sign-in Route Source: https://github.com/heartcombo/devise/wiki/How-To:-Change-the-default-sign_in-and-sign_out-routes The `as` keyword is an alias for `devise_scope`, providing an equivalent way to define custom routes for Devise. ```ruby as :user do get 'login', to: 'devise/sessions#new' end ``` -------------------------------- ### Add Password Management Methods to Devise Model Source: https://github.com/heartcombo/devise/wiki/How-To:-Override-confirmations-so-users-can-pick-their-own-passwords-as-part-of-confirmation-activation Includes methods to attempt setting a password without the current one and to check if a password has been set. Useful for password reset or initial setup flows. ```ruby # new function to set the password without knowing the current password used in our confirmation controller. def attempt_set_password(params) p = {} p[:password] = params[:password] p[:password_confirmation] = params[:password_confirmation] update_attributes(p) end # new function to return whether a password has been set def has_no_password? self.encrypted_password.blank? end # new function to provide access to protected method unless_confirmed def only_if_unconfirmed unless_confirmed {yield} end ``` -------------------------------- ### Add Microsoft Login Button Source: https://github.com/heartcombo/devise/wiki/Microsoft-Azure-Active-Directory-(Azure-AD)-was-renamed-to-Microsoft-Entra-ID Include a button in your application layout to initiate the Microsoft OAuth flow. Use `prompt: 'select_account'` to ensure the user is always prompted to select an account. ```erb <%= button_to "Login", user_microsoft_omniauth_authorize_path(prompt: 'select_account'), class: 'navbar-link', data: { turbo: false } %> ``` -------------------------------- ### Create Custom Controller Helper for Devise Source: https://github.com/heartcombo/devise/wiki/How-To:-Stub-authentication-in-controller-specs This helper method simplifies signing in users (or nil for no user) within controller specs. Place this in spec/support/controller_helpers.rb. ```ruby module ControllerHelpers def sign_in(user = double('user')) if user.nil? allow(request.env['warden']).to receive(:authenticate!).and_throw(:warden, {scope: :user}) allow(controller).to receive(:current_user).and_return(nil) else allow(request.env['warden']).to receive(:authenticate!).and_return(user) allow(controller).to receive(:current_user).and_return(user) end end end ``` -------------------------------- ### Configure Multiple Devise Controller Layouts with Conditional Logic Source: https://github.com/heartcombo/devise/wiki/How-To:-Create-custom-layouts Apply different layouts to various Devise controllers within a `config.to_prepare` block, including conditional logic for user-signed-in status. ```ruby config.to_prepare do # Optional: Ensure that Routes and therefore Controller mixins like authenticate_user! are defined before autoloading Controllers Rails.application.reload_routes! Devise::SessionsController.layout "devise" Devise::RegistrationsController.layout proc{ |controller| user_signed_in? ? "application" : "devise" } Devise::ConfirmationsController.layout "devise" Devise::UnlocksController.layout "devise" Devise::PasswordsController.layout "devise" end ``` -------------------------------- ### Call current_or_guest_user in Sessions and Registrations Controllers Source: https://github.com/heartcombo/devise/wiki/How-To:-Create-a-guest-user After calling `super` in the `create` action of sessions and registrations controllers, call `current_or_guest_user` to ensure the handoff from guest to current user is processed. ```ruby def create super current_or_guest_user end ``` -------------------------------- ### Generate Devise Views and Convert to Haml (Unix) Source: https://github.com/heartcombo/devise/wiki/How-To:-Create-Haml-and-Slim-Views First, generate the default Devise ERB views. Then, install the html2haml gem and use it to convert ERB files to Haml, removing the original ERB files. ```sh rails generate devise:views gem install html2haml for file in app/views/devise/**/*.erb; do html2haml -e $file ${file%erb}haml && rm $file; done ``` -------------------------------- ### Linking to Custom Sign Up Path in Views Source: https://github.com/heartcombo/devise/wiki/How-To:-Change-Default-Sign_up---Registration-Path-with-Custom-Path Use the `new_user_registration_path` helper in your views to link to the custom sign-up page defined in `routes.rb`. ```erb <%= link_to "Sign up", new_user_registration_path %> ``` -------------------------------- ### Configure Nested Routes for Admin Users Source: https://github.com/heartcombo/devise/wiki/How-to-manage-users-with-a-standard-Rails-controller Use nested resources in `routes.rb` to manage users under an admin namespace, avoiding conflicts with Devise's default routes. This setup allows for standard scaffolding under `admin/users`. ```ruby devise_for :users namespace :admin do resources :users end ``` -------------------------------- ### Generate Devise Views and Convert to Slim (Unix) Source: https://github.com/heartcombo/devise/wiki/How-To:-Create-Haml-and-Slim-Views After generating Devise ERB views, install the html2slim gem. Use the erb2slim tool to convert ERB files to Slim, with an option to delete the original ERB files. ```sh rails generate devise:views gem install html2slim for file in app/views/devise/**/*.erb; do erb2slim $file ${file%erb}slim && rm $file; done ``` -------------------------------- ### Configure Devise Controller Layouts in devise.rb Source: https://github.com/heartcombo/devise/wiki/How-To:-Create-custom-layouts Append Devise layout configurations to the `config/initializers/devise.rb` file using `Rails.application.config.to_prepare`. ```ruby # append to end of config/initializers/devise.rb Rails.application.config.to_prepare do Devise::RegistrationsController.layout proc { |controller| user_signed_in? ? "application" : "devise" } # And/or Sessions, Confirmations, Unlocks, Passwords end ``` -------------------------------- ### Custom Devise Session Serialization for Multiple Classes Source: https://github.com/heartcombo/devise/wiki/How to: Write your own serializer Implement custom serialization logic to handle multiple classes within a single scope. This example serializes both the class name and ID, then deserializes by constantizing the class name. ```ruby config.warden do |manager| manager.serialize_into_session(:user) do |user| [user.class, user.id] end manager.serialize_from_session(:user) do |keys| klass, id = keys klass.constantize.find(id) end end ``` -------------------------------- ### Override after_inactive_sign_up_path_for Source: https://github.com/heartcombo/devise/wiki/How-To:-Redirect-to-a-specific-page-on-successful-sign-in,-sign-up,-or-sign-out If the account requires confirmation, override `after_inactive_sign_up_path_for` to specify the redirect path for users who have registered but not yet confirmed their account. ```ruby class RegistrationsController < Devise::RegistrationsController protected def after_inactive_sign_up_path_for(resource) '/an/example/path' # Or :prefix_to_your_route end end ``` -------------------------------- ### Store User Location in ApplicationController Source: https://github.com/heartcombo/devise/wiki/How-To:-[Redirect-back-to-current-page-after-sign-in,-sign-out,-sign-up,-update] Implement `before_action` callbacks to store the user's location before authentication. Ensure the location is only stored for GET requests and not for Devise-specific controllers or AJAX/Turbo Frame requests to prevent infinite redirects. ```ruby class ApplicationController < ActionController::Base before_action :store_user_location!, if: :storable_location? # The callback which stores the current location must be added before you authenticate the user # as `authenticate_user!` (or whatever your resource is) will halt the filter chain and redirect # before the location can be stored. before_action :authenticate_user! private # Its important that the location is NOT stored if: # - The request method is not GET (non idempotent) # - The request is handled by a Devise controller such as Devise::SessionsController as that could cause an # infinite redirect loop. # - The request is an Ajax request as this can lead to very unexpected behaviour. # - The request is not a Turbo Frame request ([turbo-rails](https://github.com/hotwired/turbo-rails/blob/main/app/controllers/turbo/frames/frame_request.rb)) def storable_location? request.get? && is_navigational_format? && !devise_controller? && !request.xhr? && !turbo_frame_request? end def store_user_location! # :user is the scope we are authenticating store_location_for(:user, request.fullpath) end end ``` -------------------------------- ### Define :guest_user factory Source: https://github.com/heartcombo/devise/wiki/How-To:-Create-a-guest-user Define a FactoryBot factory for a guest user. This factory creates a User instance with a guest role and bypasses validation during saving. ```ruby FactoryBot.define do factory :user do ... end factory :guest_user, class: 'User' do sequence(:email) { Faker::Internet.email } role :guest to_create { |instance| instance.save(validate: false) } end end ```