Devise Authentication

repository·main·Indexed 12 days ago

https://github.com/heartcombo/devise

A modular authentication solution for Ruby on Rails applications built on top of the Warden Rack middleware. Devise provides a complete MVC implementation for common tasks including registration, password resets, and social login, supporting multiple models and various ORMs such as ActiveRecord and Mongoid.

Tokens
13K
Snippets
51
Records
70
Agent score
98%

What's inside Devise

  1. What is Devise and how does it work?

    main

    Devise is a flexible, Rack-based authentication solution for Ruby on Rails built on top of Warden. It is designed as a complete MVC solution using Rails engines.

    A key feature of Devise is its modularity: you can include only the specific authentication behaviors your application requires. It also supports having multiple models (e.g., User and Admin) signed in simultaneously.

  2. Understand Devise's relationship with Warden

    main
    Devise is built on top of Warden, which is a general Rack-based authentication framework. While Devise provides high-level Rails integration and features, Warden handles the underlying authentication logic at the Rack level. For deep customization of the authentication layer or understanding how Rack middleware handles authentication, refer to the Warden documentation.
  3. Devise Authentication Modules (continued)

    main

    Continuing the list of core modules:

    • Rememberable: Manages user sessions via a token stored in a cookie to remember the user.
    • Trackable: Tracks sign-in count, timestamps, and IP addresses.
    • Timeoutable: Automatically expires sessions after a specified period of inactivity.
    • Validatable: Provides default validations for email and password (can be customized).
    • Lockable: Locks an account after a set number of failed sign-in attempts, with options to unlock via email or after a time period.
  4. Test Devise with Controller and Integration tests

    main

    Devise provides test helpers to sign users in and out during testing.

    Controller Tests (Rails >= 5): Include Devise::Test::IntegrationHelpers. If testing internal Devise controllers, you must manually set the devise.mapping in the request environment.

    Integration Tests: Include Devise::Test::IntegrationHelpers. Unlike controller tests, integration tests can infer the mapping from the routes.

    RSpec Configuration: Include helpers in your spec_helper.rb or rails_helper.rb.

    # Controller Test
    class PostsControllerTest < ActionController::TestCase
      include Devise::Test::IntegrationHelpers
    
      test 'GET new'
        @request.env['devise.mapping'] = Devise.mappings[:user]
        sign_in users(:alice)
        get :new
      end
    end
    
    # Integration Test
    class PostsTests < ActionDispatch::IntegrationTest
      include Devise::Test::IntegrationHelpers
    
      test 'login'
        sign_in users(:bob)
        # ...
      end
    end
  5. Install and setup Devise

    main

    Devise 5 requires Rails 7 or later. To install, add the gem to your bundle and run the Devise installation generator. After installation, you must configure default URL options for the Devise mailer in your environment configuration files (e.g., config/environments/development.rb).

    Important: Always inspect the generated initializer at config/initializers/devise.rb to understand all available configuration options.

    bundle add devise
    rails generate devise:install

    Example configuration for config/environments/development.rb

    config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }
  6. Running Devise tests with different ORMs or Rails versions

    main

    Since Devise supports both ActiveRecord and Mongoid, and multiple Rails versions, you can use environment variables to modify the test environment.

    Using Mongoid

    Set DEVISE_ORM=mongoid to run tests against a MongoDB server (version 2.0+ required).

    DEVISE_ORM=mongoid bin/test

    Simulating specific Rails versions

    Use the BUNDLE_GEMFILE variable to point to specific Gemfiles located in the gemfiles/ directory of the repository.

    Example: Testing with Ruby 3.4 and Rails 8.0:

    chruby 3.4.0
    BUNDLE_GEMFILE=gemfiles/Gemfile-rails-8-0 bundle install
    BUNDLE_GEMFILE=gemfiles/Gemfile-rails-8-0 bin/test

    Example: Testing with Mongoid on Rails 8.0:

    BUNDLE_GEMFILE=gemfiles/Gemfile-rails-8-0 bundle install
    BUNDLE_GEMFILE=gemfiles/Gemfile-rails-8-0 DEVISE_ORM=mongoid bin/test
  7. Add Devise to a model

    main

    Use the Devise generator to create a model (or configure an existing one) and set up the necessary routes. Replace MODEL with your class name (e.g., User or Admin).

    If you add specific modules like :confirmable or :lockable in the model, you must also manually uncomment the corresponding sections in the generated migration file before running rails db:migrate.

    rails generate devise MODEL
  8. Customize Devise views

    main

    Devise views are packaged within the gem. To customize them, run the devise:views generator to copy them into your application.

    Scoped Views: If you have multiple models (e.g., User and Admin) and want different views for each, set config.scoped_views = true in config/initializers/devise.rb. You can then generate views specific to a scope:

    rails generate devise:views users
    rails generate devise:views
    # Or for specific modules
    rails generate devise:views -v registrations confirmations
  9. Use Devise in Rails API Mode

    main

    API-only applications do not support cookie-based authentication by default. To use Devise in API mode, use the http_authenticatable strategy (HTTP Basic Auth).

    Setup:

    1. Enable the strategy in the Devise initializer: config.http_authenticatable = [:database]
    2. Middleware Fix: API mode changes the middleware stack order, which can break Devise::Test::IntegrationHelpers. Add the following to config/environments/test.rb:
    Rails.application.config.middleware.insert_before Warden::Manager, ActionDispatch::Cookies
    Rails.application.config.middleware.insert_before Warden::Manager, ActionDispatch::Session::CookieStore
    # config/initializers/devise.rb
    config.http_authenticatable = [:database]
    
    # config/environments/test.rb
    Rails.application.config.middleware.insert_before Warden::Manager, ActionDispatch::Cookies
    Rails.application.config.middleware.insert_before Warden::Manager, ActionDispatch::Session::CookieStore
  10. Configure Strong Parameters for Devise

    main

    When adding custom attributes to your Devise models (e.g., username), you must permit them via the devise_parameter_sanitizer in your ApplicationController. This is necessary because Devise only permits a default set of parameters for sign_in, sign_up, and account_update actions.

    You can permit simple scalar values or use a block for more complex logic like nested attributes or arrays (e.g., checkboxes).

    class ApplicationController < ActionController::Base
      before_action :configure_permitted_parameters, if: :devise_controller?
    
      protected
    
      def configure_permitted_parameters
        # For simple scalar types
        devise_parameter_sanitizer.permit(:sign_up, keys: [:username])
    
        # For nested attributes
        devise_parameter_sanitizer.permit(:sign_up, keys: [:first_name, :last_name, address_attributes: [:country, :state, :city, :area, :postal_code]])
    
        # For arrays (e.g., checkboxes)
        devise_parameter_sanitizer.permit(:sign_up) do |user_params|
          user_params.permit({ roles: [] }, :email, :password, :password_confirmation)
        end
      end
    end
  11. Customize Devise controllers

    main

    To add custom logic to Devise actions (like triggering a background job after sign-in), follow these steps:

    1. Generate custom controllers: rails generate devise:controllers [scope] (e.g., users).
    2. Update routes: Tell your router to use the new controllers using the controllers option in devise_for.
    3. Extend actions: Inherit from the Devise controller and use super to maintain default behavior while adding your own.
    # 1. Generate: rails generate devise:controllers users
    
    # 2. In config/routes.rb
    devise_for :users, controllers: { sessions: 'users/sessions' }
    
    # 3. In app/controllers/users/sessions_controller.rb
    class Users::SessionsController < Devise::SessionsController
      def create
        super do |resource|
          BackgroundWorker.trigger(resource)
        end
      end
    end
  12. Reporting a bug in Devise

    main

    When reporting a bug in the Devise issue tracker, follow these guidelines to ensure your report is actionable:

    1. Search first: Check the existing issues to ensure the bug hasn't already been reported.
    2. Provide Environment Details: Include your specific versions for Ruby, Rails, and Devise.
    3. Describe Behavior: Clearly state the Current behavior versus the Expected behavior.
    4. Include Reproducible Code: Provide code samples, error messages, stack traces, or steps to reproduce the error. Providing a sample application or a minimal test case that reproduces the error is highly encouraged.

    Important Security Note: Do not report security vulnerabilities through GitHub issues. Instead, email heartcombo.oss@gmail.com directly.