rodauth-rails

repository·main·Indexed 20 days ago

https://github.com/janko/rodauth-rails

Integration for the Rodauth authentication framework into Ruby on Rails applications. It enables secure authentication features such as MFA, JSON API, and passwordless login while maintaining a Rails development experience. The library provides generators for installation, views, mailers, and migrations, as well as tools for requiring authentication at the controller, router, and middleware levels.

Tokens
10.1K
Snippets
36
Records
44
Agent score
72%

What's inside rodauth-rails

  1. How Rodauth::Rails::App works

    main

    The Rodauth::Rails::App class is a Roda subclass that provides a convenience layer over Rodauth. It consists of three main parts:

    1. Configure block: A wrapper around plugin :rodauth. It can define named auth classes, anonymous classes, or pass additional plugin options.
    2. Route block: A block called for each request before it reaches the Rails router. It yields the request object r.
    3. Rack env: The app injects Rodauth objects into the Rack environment, making them accessible in Rails controllers and views via request.env.

    Accessing Rodauth in Rails:

    • request.env["rodauth"] returns the primary Rodauth instance.
    • request.env["rodauth.admin"] returns a specific named configuration (e.g., if using :admin).
    class RodauthApp < Rodauth::Rails::App
      # named auth class
      configure(RodauthMain)
      configure(RodauthAdmin, :admin)
    
      # anonymous auth class
      configure { ... }
      configure(:admin) { ... }
    
      # plugin options
      configure(RodauthMain, json: :only, render: false)
    
      route do |r|
        # called before each request
      end
    end
  2. How to handle multiple Rodauth configurations

    main

    If your application requires different authentication logic for different account types (e.g., Users vs. Admins), you can create multiple Rodauth::Rails::Auth subclasses and register them in your RodauthApp.

    Workflow:

    1. Create a new subclass of Rodauth::Rails::Auth (e.g., RodauthAdmin).
    2. Register it in RodauthApp using configure.
    3. Route requests in the route block using r.rodauth(:name).
    4. Access the secondary configuration in controllers using rodauth(:name).
    # app/misc/rodauth_app.rb
    class RodauthApp < Rodauth::Rails::App
      configure RodauthMain          # primary configuration
      configure RodauthAdmin, :admin # secondary configuration
    
      route do |r|
        r.rodauth         # route primary rodauth requests
        r.rodauth(:admin) # route secondary rodauth requests
    
        if r.path.match?(%r{\A/admin(\.\w+)?(/|\z)}) 
          rodauth(:admin).require_account
        end
      end
    end
    
    # app/misc/rodauth_admin.rb
    class RodauthAdmin < Rodauth::Rails::Auth
      configure do
        prefix "/admin"
        session_key_prefix "admin_"
        remember_cookie_key "_admin_remember"
        rails_controller { Admin::RodauthController }
      end
    end
    
    # Accessing in application code
    rodauth(:admin).authenticated? # checks "admin_account_id" session value
    rodauth(:admin).login_path    # => "/admin/login"
  3. Capabilities of the `rails` Rodauth feature

    main

    The rails feature is the core integration layer that connects Rodauth to the Rails ecosystem. It provides the following capabilities:

    • Template Rendering: Uses Action View for rendering templates.
    • CSRF Protection: Uses Action Dispatch for authenticity token verification.
    • Lifecycle Hooks: Runs Action Controller callbacks and rescue_from blocks around Rodauth requests.
    • Emailing: Uses Action Mailer to create and deliver emails.
    • Instrumentation: Provides Action Controller instrumentation around Rodauth requests.
    • URL Generation: Uses Action Mailer's default URL options when calling Rodauth outside of a request context.
  4. Generate Mailer Integration

    main

    To modify default email templates and deliver them via background jobs, use the rodauth:mailer generator. This creates a RodauthMailer and provides the necessary configuration blocks to be added to your Rodauth auth class.

    Important: For email links to work, ensure config.action_mailer.default_url_options is configured in your environment files.

    By default, the generated configuration uses #deliver_later (Active Job). To deliver emails synchronously, change these calls to #deliver_now.

    $ rails generate rodauth:mailer
  5. Configure rodauth:install options

    main

    The rodauth:install generator supports several flags to customize your initial setup:

    • Custom table name: Specify a different table for accounts (defaults to accounts).
    • JSON API: Enable endpoints for JSON-based authentication. Use --json for cookie-based authentication or --jwt for token-based authentication.
    • Argon2: Use Argon2 for password hashing instead of the default bcrypt.

    Example commands:

    # Use 'users' table instead of 'accounts'
    rails generate rodauth:install users
    
    # Enable JWT-based JSON API
    rails generate rodauth:install --jwt
    
    # Use Argon2 hashing
    rails generate rodauth:install --argon2
    $ rails generate rodauth:install users
    $ rails generate rodauth:install --json
    $ rails generate rodauth:install --jwt
    $ rails generate rodauth:install --argon2
  6. Require authentication in Rails

    main

    You can require authentication at three different levels:

    Use a before_action in your controllers to call rodauth.require_account, which redirects unauthenticated users to the login page.

    class ApplicationController < ActionController::Base
      private
    
      def authenticate
        rodauth.require_account
      end
    end
    
    class DashboardController < ApplicationController
      before_action :authenticate
    end

    2. Router Level

    Use Rodauth::Rails.authenticate within your config/routes.rb to protect groups of routes. This is useful for mounting Rack apps or applying specific constraints.

    # config/routes.rb
    Rails.application.routes.draw do
      # Require standard authentication
      constraints Rodauth::Rails.authenticate do
        # ... protected routes ...
      end
    
      # Require 2FA setup
      constraints Rodauth::Rails.authenticate { |rodauth| rodauth.uses_two_factor_authentication? } do
        # ... routes available only if 2FA is configured ...
      end
    
      # Require specific 'admin' configuration
      constraints Rodauth::Rails.authenticate(:admin) do
        # ... routes using secondary admin config ...
      end
    end

    3. Middleware Level

    Within your RodauthApp routing block, you can use rodauth.require_account to protect specific paths.

    # config/routes.rb
    Rails.application.routes.draw do
      constraints Rodauth::Rails.authenticate do
        # ... these routes will require authentication ...
      end
    end
  7. Manually insert Rodauth middleware

    main

    By default, the rodauth-rails railtie inserts Rodauth::Rails::Middleware at the end of the middleware stack. If you need to insert it earlier (for example, to ensure certain middleware runs before Rodauth), you must disable auto-insertion and manually add it to the stack.

    Note: If you use middleware like Rack::Attack to throttle signups, ensure it is placed above Rodauth in your Gemfile so its middleware is inserted first.

    # config/initializers/rodauth.rb
    Rodauth::Rails.configure do |config|
      config.middleware = false # disable auto-insertion
    end
    
    Rails.configuration.middleware.insert_before AnotherMiddleware, Rodauth::Rails::Middleware
  8. Generate and customize Rodauth views

    main

    Rodauth comes with built-in templates (using Bootstrap markup). To customize them, run the rodauth:views generator to copy them into app/views/rodauth.

    Options for the generator:

    • --css=tailwind: Generate views with Tailwind markup (requires @tailwindcss/forms).
    • --all: Generate views for all enabled features.
    • --name <name>: Specify a different Rodauth configuration name.
    • Feature selection: Pass specific feature names to generate only those views.

    Example commands:

    # Generate all views
    rails generate rodauth:views --all
    
    # Generate specific features with Tailwind
    rails generate rodauth:views login create_account --css=tailwind
    
    # Generate views for a specific configuration named 'admin'
    rails generate rodauth:views webauthn two_factor_base --name admin
    $ rails generate rodauth:views --all
  9. Testing Rodauth authentication

    main

    For system and integration tests, authentication is exercised via standard HTTP requests to Rodauth endpoints. You can implement helper methods in your ActionDispatch::IntegrationTest to simplify login and logout flows.

    # test/controllers/articles_controller_test.rb
    class ArticlesControllerTest < ActionDispatch::IntegrationTest
      def login(email, password)
        post "/login", params: { email: email, password: password }
        assert_redirected_to "/"
      end
    
      def logout
        post "/logout"
      end
    
      test "required authentication"
      do
        get :index
        assert_response 302
        assert_redirected_to "/login"
    
        account = Account.create!(email: "user@example.com", status: "verified")
        login(account.email, "secret123")
    
        get :index
        assert_response 200
      end
    end
  10. Install rodauth-rails

    main

    To install rodauth-rails, add the gem to your project and run the install generator. The generator creates a Rodauth app, configuration, and necessary database migrations.

    1. Add the gem:
      bundle add rodauth-rails
    2. Run the generator:
      rails generate rodauth:install
    3. Run migrations:
      rails db:migrate
    $ bundle add rodauth-rails
    $ rails generate rodauth:install
    $ rails db:migrate
  11. Use Rodauth as a library

    main

    If you want to use Rodauth as a library via internal_request without letting it handle routing via middleware, use Rodauth::Rails.lib.

    Note: You should set require: false for the rodauth-rails gem in your Gemfile to prevent the middleware from being inserted automatically during boot.

    # Gemfile
    gem "rodauth-rails", require: false
    # app/misc/rodauth_main.rb
    require "rodauth/rails"
    require "sequel/core"
    
    RodauthMain = Rodauth::Rails.lib do
      enable :create_account, :login, :close_account
      db Sequel.postgres(extensions: :activerecord_connection, keep_reference: false)
    end
    
    # Usage
    RodauthMain.create_account(login: "email@example.com", password: "secret123")
  12. Skip loading the Tilt gem

    main

    Rodauth uses the Tilt gem to render built-in view and email templates. If you have already imported all necessary templates into your application and want to remove Tilt as a dependency, you can disable it in the configuration.

    # config/initializers/rodauth.rb
    Rodauth::Rails.configure do |config|
      config.tilt = false # skip loading Tilt gem
    end