{"id":13409875,"url":"https://github.com/janko/rodauth-rails","last_synced_at":"2025-04-10T23:23:22.865Z","repository":{"id":37004350,"uuid":"254049553","full_name":"janko/rodauth-rails","owner":"janko","description":"Rails integration for Rodauth authentication framework","archived":false,"fork":false,"pushed_at":"2024-10-11T08:39:33.000Z","size":1118,"stargazers_count":596,"open_issues_count":0,"forks_count":40,"subscribers_count":8,"default_branch":"main","last_synced_at":"2024-10-14T02:11:11.231Z","etag":null,"topics":["account-management","authentication","json-api","jwt","multifactor-authentication","rails","rodauth"],"latest_commit_sha":null,"homepage":"https://github.com/jeremyevans/rodauth","language":"HTML","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/janko.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE.txt","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":"janko"}},"created_at":"2020-04-08T09:56:12.000Z","updated_at":"2024-10-13T18:50:43.000Z","dependencies_parsed_at":"2023-10-14T17:52:52.842Z","dependency_job_id":"cd63ce1e-a4aa-4ab2-974a-9363cee76ca9","html_url":"https://github.com/janko/rodauth-rails","commit_stats":{"total_commits":640,"total_committers":21,"mean_commits":"30.476190476190474","dds":0.04843750000000002,"last_synced_commit":"fbba5a299c67a6821c9547d7e758799627cd8206"},"previous_names":[],"tags_count":64,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/janko%2Frodauth-rails","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/janko%2Frodauth-rails/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/janko%2Frodauth-rails/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/janko%2Frodauth-rails/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/janko","download_url":"https://codeload.github.com/janko/rodauth-rails/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248313237,"owners_count":21082821,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["account-management","authentication","json-api","jwt","multifactor-authentication","rails","rodauth"],"created_at":"2024-07-30T20:01:03.837Z","updated_at":"2025-04-10T23:23:22.840Z","avatar_url":"https://github.com/janko.png","language":"HTML","funding_links":["https://github.com/sponsors/janko"],"categories":["HTML","Ruby","Gems"],"sub_categories":["Articles"],"readme":"# rodauth-rails\n\nProvides Rails integration for the [Rodauth] authentication framework.\n\n## Resources\n\n🔗 Useful links:\n\n* [Rodauth documentation](http://rodauth.jeremyevans.net/documentation.html)\n* [Rails demo](https://github.com/janko/rodauth-demo-rails)\n* [JSON API guide](https://github.com/janko/rodauth-rails/wiki/JSON-API)\n* [OmniAuth guide](https://github.com/janko/rodauth-rails/wiki/OmniAuth)\n* [JSON Request Documentation for Rodauth](https://documenter.getpostman.com/view/26686011/2s9YC7SWn9)\n\n🎥 Screencasts / Streams:\n\n* [Rails Authentication with Rodauth](https://www.youtube.com/watch?v=2hDpNikacf0) \\[8:23\\]\n* [Multifactor Authentication via TOTP with Rodauth](https://youtu.be/9ON-kgXpz2A) \\[4:36\\]\n* [Multifactor Authentication via Recovery Codes with Rodauth](https://youtu.be/lkFCcE1Q5-w) \\[4:24\\]\n* [Adding Admin Accounts with Rodauth](https://www.youtube.com/watch?v=N6z7AtKSpNI) \\[1:25:55\\]\n* [Integrating Passkeys into Rails with Rodauth](https://www.youtube.com/watch?v=kGzgmfCmnmY) \\[59:47\\]\n\n📚 Articles:\n\n* [Rodauth: A Refreshing Authentication Solution for Ruby](https://janko.io/rodauth-a-refreshing-authentication-solution-for-ruby/)\n* [Rails Authentication with Rodauth](https://janko.io/adding-authentication-in-rails-with-rodauth/)\n* [Multifactor Authentication in Rails with Rodauth](https://janko.io/adding-multifactor-authentication-in-rails-with-rodauth/)\n* [How to build an OIDC provider using rodauth-oauth on Rails](https://honeyryderchuck.gitlab.io/2021/03/15/oidc-provider-on-rails-using-rodauth-oauth.html)\n* [What It Took to Build a Rails Integration for Rodauth](https://janko.io/what-it-took-to-build-a-rails-integration-for-rodauth/)\n* [Social Login in Rails with Rodauth](https://janko.io/social-login-in-rails-with-rodauth/)\n* [Passkey Authentication with Rodauth](https://janko.io/passkey-authentication-with-rodauth/)\n\n## Why Rodauth?\n\nThere are already several popular authentication solutions for Rails (Devise,\nSorcery, Clearance, Authlogic), so why would you choose Rodauth? Here are some\nof the advantages that stand out for me:\n\n* multifactor authentication ([TOTP][otp], [SMS codes][sms_codes], [recovery codes][recovery_codes], [passkeys][webauthn])\n* standardized [JSON API support][json] for every feature (including [JWT][jwt])\n* enterprise security features ([password complexity][password_complexity], [disallow password reuse][disallow_password_reuse], [password expiration][password_expiration], [session expiration][session_expiration], [single session][single_session], [account expiration][account_expiration])\n* passwordless authentication ([email][email_auth], [passkeys][webauthn_login])\n* [audit logging][audit_logging] for any action\n* ability to protect password hashes even in case of SQL injection ([more details][password protection])\n* uniform configuration DSL with before/after hooks around everything\n\n### Sequel\n\nOne common concern for people coming from other Rails authentication frameworks\nis the fact that Rodauth uses [Sequel] for database interaction instead of\nActive Record. For Rails apps using Active Record, rodauth-rails configures Sequel to [reuse\nActive Record's database connection][sequel-activerecord_connection]. This\nmakes it run smoothly alongside Active Record, even allowing calling Active\nRecord code from within Rodauth configuration. So, for all intents and\npurposes, Sequel can be treated just as an implementation detail of Rodauth.\n\n## Installation\n\nAdd the gem to your project:\n\n```sh\n$ bundle add rodauth-rails\n```\n\nNext, run the install generator:\n\n```sh\n$ rails generate rodauth:install\n```\n\nThis generator will create a Rodauth app and configuration with common\nauthentication features enabled, a database migration with tables required by\nthose features, and a few other files.\n\nFeel free to remove any features you don't need, along with their corresponding\ntables. Afterwards, run the migration:\n\n```sh\n$ rails db:migrate\n```\n\n### Install options\n\nThe install generator will use the `accounts` table by default. You can specify a different table name:\n\n```sh\n$ rails generate rodauth:install users\n```\n\nIf you want Rodauth endpoints to be exposed via [JSON API]:\n\n```sh\n$ rails generate rodauth:install --json # cookied-based authentication\n# or\n$ rails generate rodauth:install --jwt # token-based authentication\n```\n\nTo use Argon2 instead of bcrypt for password hashing:\n\n```sh\n$ rails generate rodauth:install --argon2\n```\n\n## Usage\n\nThe Rodauth app will be called for each request before it reaches the Rails\nrouter. It handles requests to Rodauth endpoints, and allows you to call\nadditional code before your main routes.\n\n```sh\n$ rails middleware\n# ...\n# use Rodauth::Rails::Middleware (calls your Rodauth app)\n# run YourApp::Application.routes\n```\n\n### Routes\n\nBecause requests to Rodauth endpoints are handled by Roda, Rodauth routes will\nnot show in `rails routes`. You can use the `rodauth:routes` rake task to view\nthe list of endpoints based on currently loaded features:\n\n```sh\n$ rails rodauth:routes\n```\n```\nRoutes handled by RodauthApp:\n\n                   login  GET|POST  /login                   rodauth.login_path\n          create_account  GET|POST  /create-account          rodauth.create_account_path\n   verify_account_resend  GET|POST  /verify-account-resend   rodauth.verify_account_resend_path\n          verify_account  GET|POST  /verify-account          rodauth.verify_account_path\n         change_password  GET|POST  /change-password         rodauth.change_password_path\n            change_login  GET|POST  /change-login            rodauth.change_login_path\n                  logout  GET|POST  /logout                  rodauth.logout_path\n                remember  GET|POST  /remember                rodauth.remember_path\n  reset_password_request  GET|POST  /reset-password-request  rodauth.reset_password_request_path\n          reset_password  GET|POST  /reset-password          rodauth.reset_password_path\n     verify_login_change  GET|POST  /verify-login-change     rodauth.verify_login_change_path\n           close_account  GET|POST  /close-account           rodauth.close_account_path\n```\n\nUsing this information, you can add some basic authentication links to your\nnavigation header:\n\n```erb\n\u003c% if rodauth.logged_in? %\u003e\n  \u003c%= button_to \"Sign out\", rodauth.logout_path, method: :post %\u003e\n\u003c% else %\u003e\n  \u003c%= link_to \"Sign in\", rodauth.login_path %\u003e\n  \u003c%= link_to \"Sign up\", rodauth.create_account_path %\u003e\n\u003c% end %\u003e\n```\n\nThese routes are fully functional, feel free to visit them and interact with the\npages. The templates that ship with Rodauth aim to provide a complete\nauthentication experience, and the forms use [Bootstrap] markup.\n\n### Current account\n\nThe Rodauth object defines a `#rails_account` method, which returns a model\ninstance of the currently logged in account. You can create a helper method for\neasy access from controllers and views:\n\n```rb\nclass ApplicationController \u003c ActionController::Base\n  private\n\n  def current_account\n    rodauth.rails_account\n  end\n  helper_method :current_account # skip if inheriting from ActionController::API\nend\n```\n\n### Requiring authentication\n\nYou can require authentication for routes at the middleware level in in your Rodauth\napp's routing block, which helps keep the authentication logic encapsulated:\n\n```rb\n# app/misc/rodauth_app.rb\nclass RodauthApp \u003c Rodauth::Rails::App\n  route do |r|\n    r.rodauth # route rodauth requests\n\n    if r.path.start_with?(\"/dashboard\") # /dashboard/* routes\n      rodauth.require_account # redirect to login page if not authenticated\n    end\n  end\nend\n```\n\nYou can also require authentication at the controller layer:\n\n```rb\nclass ApplicationController \u003c ActionController::Base\n  private\n\n  def authenticate\n    rodauth.require_account # redirect to login page if not authenticated\n  end\nend\n```\n```rb\nclass DashboardController \u003c ApplicationController\n  before_action :authenticate\nend\n```\n\nAdditionally, routes can be authenticated at the Rails router level:\n\n```rb\n# config/routes.rb\nRails.application.routes.draw do\n  constraints Rodauth::Rails.authenticate do\n    # ... these routes will require authentication ...\n  end\n\n  constraints Rodauth::Rails.authenticate { |rodauth| rodauth.uses_two_factor_authentication? } do\n    # ... these routes will be available only if 2FA is setup ...\n  end\n\n  constraints Rodauth::Rails.authenticate(:admin) do\n    # ... these routes will be authenticated with secondary \"admin\" configuration ...\n  end\n\n  constraints -\u003e (r) { !r.env[\"rodauth\"].logged_in? } do # or env[\"rodauth.admin\"]\n    # ... these routes will be available only if not authenticated ...\n  end\nend\n```\n\n### Controller\n\nYour Rodauth configuration is linked to a Rails controller, which is primarily used to render views and handle CSRF protection, but will also execute any callbacks and rescue handlers defined on it around Rodauth endpoints.\n\n```rb\n# app/misc/rodauth_main.rb\nclass RodauthMain \u003c Rodauth::Rails::Auth\n  configure do\n    rails_controller { RodauthController }\n  end\nend\n```\n```rb\nclass RodauthController \u003c ApplicationController\n  before_action :verify_captcha, only: :login, if: -\u003e { request.post? } # executes before Rodauth endpoints\n  rescue_from(\"SomeError\") { |exception| ... } # rescues around Rodauth endpoints\nend\n```\n\nVarious methods are available in your Rodauth configuration to bridge the gap with the controller:\n\n```rb\nclass RodauthMain \u003c Rodauth::Rails::Auth\n  configure do\n    # calling methods on the controller:\n    after_create_account do\n      rails_controller_eval { some_controller_method(account_id) }\n    end\n\n    # accessing Rails URL helpers:\n    login_redirect { rails_routes.dashboard_path }\n\n    # accessing Rails request object:\n    after_change_password do\n      if rails_request.format.turbo_stream?\n        return_response rails_render(turbo_stream: [turbo_stream.replace(...)])\n      end\n    end\n\n    # accessing Rails cookies:\n    after_login { rails_cookies.permanent[:last_account_id] = account_id }\n  end\nend\n```\n\n## Views\n\nThe templates built into Rodauth are useful when getting started, but soon\nyou'll want to start editing the markup. You can run the following command to\ncopy Rodauth templates into your Rails app:\n\n```sh\n$ rails generate rodauth:views\n```\n\nThis will generate views for Rodauth features you have currently enabled into\nthe `app/views/rodauth` directory (provided that `RodauthController` is set for\nthe main configuration).\n\nThe generator accepts various options:\n\n```sh\n# generate views with Tailwind markup (requires @tailwindcss/forms plugin)\n$ rails generate rodauth:views --css=tailwind\n\n# specify Rodauth features to generate views for\n$ rails generate rodauth:views login create_account lockout otp\n\n# generate views for all Rodauth features\n$ rails generate rodauth:views --all\n\n# specify a different Rodauth configuration\n$ rails generate rodauth:views webauthn two_factor_base --name admin\n```\n\n## Mailer\n\nWhen you're ready to modify the default email templates and safely deliver them\nin a background job, you can run the following command to generate the mailer\nintegration:\n\n```sh\n$ rails generate rodauth:mailer\n```\n\nThis will create a `RodauthMailer` along with email templates, as well as output\nthe necessary configuration that you should copy into your auth class:\n\n```rb\n# app/misc/rodauth_main.rb\nclass RodauthMain \u003c Rodauth::Rails::Auth\n  configure do\n    create_verify_account_email do\n      RodauthMailer.verify_account(self.class.configuration_name, account_id, verify_account_key_value)\n    end\n    create_reset_password_email do\n      RodauthMailer.reset_password(self.class.configuration_name, account_id, reset_password_key_value)\n    end\n    create_verify_login_change_email do |_login|\n      RodauthMailer.verify_login_change(self.class.configuration_name, account_id, verify_login_change_key_value)\n    end\n  end\nend\n```\n\nFor email links to work, you need to have\n`config.action_mailer.default_url_options` set for each environment.\n\n```rb\n# config/environments/development.rb\nconfig.action_mailer.default_url_options = { host: \"localhost\", port: 3000 }\n```\n\nThe generator accepts various options:\n\n```sh\n# generate mailer integration for specified features\n$ rails generate rodauth:mailer email_auth lockout webauthn_modify_email\n\n# generate mailer integration for all Rodauth features\n$ rails generate rodauth:mailer --all\n\n# specify different Rodauth configuration to select enabled features\n$ rails generate rodauth:mailer --name admin\n```\n\nNote that the generated Rodauth configuration calls `#deliver_later`, which\nuses Active Job to deliver emails in a background job. If you want to deliver\nemails synchronously, you can modify the configuration to call `#deliver_now`\ninstead.\n\nIf you're using a background processing library without an Active Job adapter,\nor a 3rd-party service for sending transactional emails, see [this wiki\npage][custom mailer job] on how to set it up.\n\n## Migrations\n\nThe install generator will create a migration for tables used by the Rodauth\nfeatures enabled by default. For any additional features, you can use the\nmigration generator to create the required tables:\n\n```sh\n$ rails generate rodauth:migration otp sms_codes recovery_codes\n```\n```rb\n# db/migration/*_create_rodauth_otp_sms_codes_recovery_codes.rb\nclass CreateRodauthOtpSmsCodesRecoveryCodes \u003c ActiveRecord::Migration\n  def change\n    create_table :account_otp_keys do |t| ... end\n    create_table :account_sms_codes do |t| ... end\n    create_table :account_recovery_codes do |t| ... end\n  end\nend\n```\n\nIf you're storing account records in a table other than `accounts`, you'll want\nto specify the appropriate table prefix when generating new migrations:\n\n```sh\n$ rails generate rodauth:migration base active_sessions --prefix user\n\n# Add the following to your Rodauth configuration:\n#\n#   accounts_table :users\n#   active_sessions_table :user_active_session_keys\n#   active_sessions_account_id_column :user_id\n```\n```rb\n# db/migration/*_create_rodauth_user_base_active_sessions.rb\nclass CreateRodauthUserBaseActiveSessions \u003c ActiveRecord::Migration\n  def change\n    create_table :users do |t| ... end\n    create_table :user_active_session_keys do |t| ... end\n  end\nend\n```\n\nYou can change the default migration name:\n\n```sh\n$ rails generate rodauth:migration email_auth --name create_account_email_auth_keys\n```\n```rb\n# db/migration/*_create_account_email_auth_keys.rb\nclass CreateAccountEmailAuthKeys \u003c ActiveRecord::Migration\n  def change\n    create_table :account_email_auth_keys do |t| ... end\n  end\nend\n```\n\n## Model\n\nThe [rodauth-model] gem provides a `Rodauth::Model` mixin that can be included\ninto the account model, which defines a password attribute and associations for\ntables used by enabled authentication features.\n\n```rb\nclass Account \u003c ActiveRecord::Base # Sequel::Model\n  include Rodauth::Rails.model # or `Rodauth::Rails.model(:admin)`\nend\n```\n```rb\n# setting password hash\naccount = Account.create!(email: \"user@example.com\", password: \"secret123\")\naccount.password_hash #=\u003e \"$2a$12$k/Ub1I2iomi84RacqY89Hu4.M0vK7klRnRtzorDyvOkVI.hKhkNw.\"\n\n# clearing password hash\naccount.password = nil\naccount.password_hash #=\u003e nil\n\n# associations\naccount.remember_key #=\u003e #\u003cAccount::RememberKey\u003e (record from `account_remember_keys` table)\naccount.active_session_keys #=\u003e [#\u003cAccount::ActiveSessionKey\u003e,...] (records from `account_active_session_keys` table)\n```\n\n## Multiple configurations\n\nIf you need to handle multiple types of accounts that require different\nauthentication logic, you can create new configurations for them. This\nis done by creating new `Rodauth::Rails::Auth` subclasses, and registering\nthem under a name.\n\n```rb\n# app/misc/rodauth_app.rb\nclass RodauthApp \u003c Rodauth::Rails::App\n  configure RodauthMain          # primary configuration\n  configure RodauthAdmin, :admin # secondary configuration\n\n  route do |r|\n    r.rodauth         # route primary rodauth requests\n    r.rodauth(:admin) # route secondary rodauth requests\n\n    if request.path.start_with?(\"/admin\")\n      rodauth(:admin).require_account\n    end\n  end\nend\n```\n```rb\n# app/misc/rodauth_admin.rb\nclass RodauthAdmin \u003c Rodauth::Rails::Auth\n  configure do\n    # ... enable features ...\n    prefix \"/admin\"\n    session_key_prefix \"admin_\"\n    remember_cookie_key \"_admin_remember\" # if using remember feature\n\n    # search views in `app/views/admin/rodauth` directory\n    rails_controller { Admin::RodauthController }\n  end\nend\n```\n```rb\n# app/controllers/admin/rodauth_controller.rb\nclass Admin::RodauthController \u003c ApplicationController\nend\n```\n\nThen in your application you can reference the secondary Rodauth instance:\n\n```rb\nrodauth(:admin).authenticated? # checks \"admin_account_id\" session value\nrodauth(:admin).login_path #=\u003e \"/admin/login\"\n```\n\nYou'll likely want to save the information of which account belongs to which\nconfiguration to the database, see [this guide][account types] on how you can do\nthat. Note that you can also [share configuration via inheritance][inheritance].\n\n## Outside of a request\n\nThe [internal_request] and [path_class_methods] features are supported, with defaults taken from `config.action_mailer.default_url_options`.\n\n```rb\n# internal requests\nRodauthApp.rodauth.create_account(login: \"user@example.com\", password: \"secret123\")\nRodauthApp.rodauth(:admin).verify_account(account_login: \"admin@example.com\")\n\n# path and URL methods\nRodauthApp.rodauth.close_account_path #=\u003e \"/close-account\"\nRodauthApp.rodauth(:admin).otp_setup_url #=\u003e \"http://localhost:3000/admin/otp-setup\"\n```\n\n### Calling instance methods\n\nIf you need to access Rodauth methods not exposed as internal requests, you can\nuse `Rodauth::Rails.rodauth` to retrieve the Rodauth instance (this requires enabling\nthe internal_request feature):\n\n```rb\naccount = Account.find_by!(email: \"user@example.com\")\nrodauth = Rodauth::Rails.rodauth(account: account) #=\u003e #\u003cRodauthMain::InternalRequest ...\u003e\n\nrodauth.compute_hmac(\"token\") #=\u003e \"TpEJTKfKwqYvIDKWsuZhkhKlhaBXtR1aodskBAflD8U\"\nrodauth.open_account? #=\u003e true\nrodauth.two_factor_authentication_setup? #=\u003e true\nrodauth.password_meets_requirements?(\"foo\") #=\u003e false\nrodauth.locked_out? #=\u003e false\n```\n\nIn addition to the `:account` option, the `Rodauth::Rails.rodauth`\nmethod accepts any options supported by the internal_request feature.\n\n```rb\n# main configuration\nRodauth::Rails.rodauth(env: { \"HTTP_USER_AGENT\" =\u003e \"programmatic\" })\nRodauth::Rails.rodauth(session: { two_factor_auth_setup: true })\n\n# secondary configuration\nRodauth::Rails.rodauth(:admin, params: { \"param\" =\u003e \"value\" })\n```\n\nYou can override default URL options ad-hoc by modifying `#rails_url_options`:\n\n```rb\nrodauth.base_url #=\u003e \"https://example.com\"\nrodauth.rails_url_options[:host] = \"subdomain.example.com\"\nrodauth.base_url #=\u003e \"https://subdomain.example.com\"\n```\n\n### Using as a library\n\nRodauth offers a [`Rodauth.lib`][library] method for when you want to use it as a library (via [internal requests][internal_request]), as opposed to having it route requests. This gem provides a `Rodauth::Rails.lib` counterpart that does the same but with Rails integration:\n\n```rb\n# skip require on boot to avoid inserting Rodauth middleware\ngem \"rodauth-rails\", require: false\n```\n```rb\n# app/misc/rodauth_main.rb\nrequire \"rodauth/rails\"\nrequire \"sequel/core\"\n\nRodauthMain = Rodauth::Rails.lib do\n  enable :create_account, :login, :close_account\n  db Sequel.postgres(extensions: :activerecord_connection, keep_reference: false)\n  # ...\nend\n```\n```rb\nRodauthMain.create_account(login: \"email@example.com\", password: \"secret123\")\nRodauthMain.login(login: \"email@example.com\", password: \"secret123\")\nRodauthMain.close_account(account_login: \"email@example.com\")\n```\n\n## Testing\n\nFor system and integration tests, which run the whole middleware stack,\nauthentication can be exercised normally via HTTP endpoints. For example, given\na controller\n\n\n```rb\n# app/controllers/articles_controller.rb\nclass ArticlesController \u003c ApplicationController\n  before_action -\u003e { rodauth.require_account }\n\n  def index\n    # ...\n  end\nend\n```\n\nOne can write `ActionDispatch::IntegrationTest` test helpers for `login` and\n`logout` by making requests to the Rodauth endpoints:\n\n```rb\n# test/controllers/articles_controller_test.rb\nclass ArticlesControllerTest \u003c ActionDispatch::IntegrationTest\n  def login(email, password)\n    post \"/login\", params: { email: email, password: password }\n    assert_redirected_to \"/\"\n  end\n\n  def logout\n    post \"/logout\"\n    assert_redirected_to \"/\"\n  end\n\n  test \"required authentication\" do\n    get :index\n\n    assert_response 302\n    assert_redirected_to \"/login\"\n    assert_equal \"Please login to continue\", flash[:alert]\n\n    account = Account.create!(email: \"user@example.com\", password: \"secret123\", status: \"verified\")\n    login(account.email, \"secret123\")\n\n    get :index\n    assert_response 200\n\n    logout\n\n    get :index\n    assert_response 302\n    assert_equal \"Please login to continue\", flash[:alert]\n  end\nend\n```\n\nFor more examples and information about testing with rodauth, see\n[this wiki page about testing](https://github.com/janko/rodauth-rails/wiki/Testing).\n\n## Configuring\n\nThe `rails` feature rodauth-rails loads provides the following configuration methods:\n\n| Name                        | Description                                                        |\n| :----                       | :----------                                                        |\n| `rails_render(**options)`   | Renders the template with given render options.                    |\n| `rails_csrf_tag`            | Hidden field added to Rodauth templates containing the CSRF token. |\n| `rails_csrf_param`          | Value of the `name` attribute for the CSRF tag.                    |\n| `rails_csrf_token`          | Value of the `value` attribute for the CSRF tag.                   |\n| `rails_check_csrf!`         | Verifies the authenticity token for the current request.           |\n| `rails_controller_instance` | Instance of the controller with the request env context.           |\n| `rails_controller`          | Controller class to use for rendering and CSRF protection.         |\n| `rails_account_model`       | Model class connected with the accounts table.                     |\n| `rails_url_options`         | Options used for generating URLs outside of a request (defaults to `config.action_mailer.default_url_options`) |\n\n```rb\nclass RodauthMain \u003c Rodauth::Rails::Auth\n  configure do\n    rails_account_model { MyApp::Account }\n    rails_controller { MyApp::RodauthController }\n  end\nend\n```\n\n### Manually inserting middleware\n\nYou can choose to insert the Rodauth middleware somewhere earlier than\nin front of the Rails router:\n\n```rb\n# config/initializers/rodauth.rb\nRodauth::Rails.configure do |config|\n  config.middleware = false # disable auto-insertion\nend\n\nRails.configuration.middleware.insert_before AnotherMiddleware, Rodauth::Rails::Middleware\n```\n\n### Skipping Tilt\n\nRodauth uses the [Tilt] gem to render built-in view \u0026 email templates. If you don't want to have Tilt as a dependency, you can disable it, provided that you've imported all view \u0026 email templates into your app:\n\n```rb\n# config/initializers/rodauth.rb\nRodauth::Rails.configure do |config|\n  config.tilt = false # skip loading Tilt gem\nend\n```\n\n## How it works\n\n### Rack middleware\n\nThe railtie inserts [`Rodauth::Rails::Middleware`](/lib/rodauth/rails/middleware.rb)\nat the end of the middleware stack, which is just a wrapper around your Rodauth app.\n\n```sh\n$ rails middleware\n# ...\n# use Rodauth::Rails::Middleware\n# run MyApp::Application.routes\n```\n\n\u003e [!NOTE]\n\u003e If you're using a middleware that should be called before Rodauth routes, make sure that middleware is inserted *before* Rodauth.\n\u003e\n\u003e For example, if you're using [Rack::Attack] to throttle signups, make sure you put the `rack-attack` gem *above* `rodauth-rails` in the Gemfile, so that its middleware is inserted first.\n\n### Roda app\n\nThe [`Rodauth::Rails::App`](/lib/rodauth/rails/app.rb) class is a [Roda]\nsubclass that provides a convenience layer over Rodauth.\n\n#### Configure block\n\nThe `configure` call is a wrapper around `plugin :rodauth`. By convention, it receives an\nauth class and configuration name as positional arguments (which get converted into\n`:auth_class` and `:name` plugin options), a block for anonymous auth classes,\nand also accepts any additional plugin options.\n\n```rb\nclass RodauthApp \u003c Rodauth::Rails::App\n  # named auth class\n  configure(RodauthMain)\n  configure(RodauthAdmin, :admin)\n\n  # anonymous auth class\n  configure { ... }\n  configure(:admin) { ... }\n\n  # plugin options\n  configure(RodauthMain, json: :only, render: false)\nend\n```\n\n#### Route block\n\nThe `route` block is called for each request, before it reaches the Rails\nrouter, and it's yielded the request object.\n\n```rb\nclass RodauthApp \u003c Rodauth::Rails::App\n  route do |r|\n    # called before each request\n  end\nend\n```\n\n#### Rack env\n\nThe app sets Rodauth objects for each registered configuration in the Rack env,\nso that they're accessible downstream by the Rails router, controllers and views:\n\n```rb\nrequest.env[\"rodauth\"]       #=\u003e #\u003cRodauthMain\u003e\nrequest.env[\"rodauth.admin\"] #=\u003e #\u003cRodauthAdmin\u003e (if using multiple configurations)\n```\n\n### Auth class\n\nThe [`Rodauth::Rails::Auth`](/lib/rodauth/rails/auth.rb) class is a subclass of\n`Rodauth::Auth`, which preloads the `rails` rodauth feature, sets [HMAC] secret to\nRails' secret key base, and modifies some [configuration defaults][restoring defaults].\n\n```rb\nclass RodauthMain \u003c Rodauth::Rails::Auth\n  configure do\n    # authentication configuration\n  end\nend\n```\n\n### Rodauth feature\n\nThe [`rails`](/lib/rodauth/rails/feature.rb) Rodauth feature loaded by\n`Rodauth::Rails::Auth` provides the main part of the Rails integration for Rodauth:\n\n* uses Action View for template rendering\n* uses Action Dispatch for CSRF protection\n* runs Action Controller callbacks and rescue from blocks around Rodauth requests\n* uses Action Mailer to create and deliver emails\n* uses Action Controller instrumentation around Rodauth requests\n* uses Action Mailer's default URL options when calling Rodauth outside of a request\n\n## License\n\nThe gem is available as open source under the terms of the [MIT\nLicense](https://opensource.org/licenses/MIT).\n\n## Code of Conduct\n\nEveryone interacting in the rodauth-rails project's codebases, issue trackers,\nchat rooms and mailing lists is expected to follow the [code of\nconduct](CODE_OF_CONDUCT.md).\n\n[Rodauth]: https://github.com/jeremyevans/rodauth\n[Sequel]: https://github.com/jeremyevans/sequel\n[Bootstrap]: https://getbootstrap.com/\n[Roda]: http://roda.jeremyevans.net/\n[HMAC]: http://rodauth.jeremyevans.net/rdoc/files/README_rdoc.html#label-HMAC\n[database authentication functions]: http://rodauth.jeremyevans.net/rdoc/files/README_rdoc.html#label-Password+Hash+Access+Via+Database+Functions\n[sequel-activerecord_connection]: https://github.com/janko/sequel-activerecord_connection\n[plugin options]: http://rodauth.jeremyevans.net/rdoc/files/README_rdoc.html#label-Plugin+Options\n[hmac]: http://rodauth.jeremyevans.net/rdoc/files/README_rdoc.html#label-HMAC\n[otp]: http://rodauth.jeremyevans.net/rdoc/files/doc/otp_rdoc.html\n[sms_codes]: http://rodauth.jeremyevans.net/rdoc/files/doc/sms_codes_rdoc.html\n[recovery_codes]: http://rodauth.jeremyevans.net/rdoc/files/doc/recovery_codes_rdoc.html\n[webauthn]: http://rodauth.jeremyevans.net/rdoc/files/doc/webauthn_rdoc.html\n[webauthn_login]: http://rodauth.jeremyevans.net/rdoc/files/doc/webauthn_login_rdoc.html\n[json]: http://rodauth.jeremyevans.net/rdoc/files/doc/json_rdoc.html\n[jwt]: http://rodauth.jeremyevans.net/rdoc/files/doc/jwt_rdoc.html\n[email_auth]: http://rodauth.jeremyevans.net/rdoc/files/doc/email_auth_rdoc.html\n[audit_logging]: http://rodauth.jeremyevans.net/rdoc/files/doc/audit_logging_rdoc.html\n[password protection]: https://github.com/jeremyevans/rodauth#label-Password+Hash+Access+Via+Database+Functions\n[password_complexity]: http://rodauth.jeremyevans.net/rdoc/files/doc/password_complexity_rdoc.html\n[disallow_password_reuse]: http://rodauth.jeremyevans.net/rdoc/files/doc/disallow_password_reuse_rdoc.html\n[password_expiration]: http://rodauth.jeremyevans.net/rdoc/files/doc/password_expiration_rdoc.html\n[session_expiration]: http://rodauth.jeremyevans.net/rdoc/files/doc/session_expiration_rdoc.html\n[single_session]: http://rodauth.jeremyevans.net/rdoc/files/doc/single_session_rdoc.html\n[account_expiration]: http://rodauth.jeremyevans.net/rdoc/files/doc/account_expiration_rdoc.html\n[simple_ldap_authenticator]: https://github.com/jeremyevans/simple_ldap_authenticator\n[internal_request]: http://rodauth.jeremyevans.net/rdoc/files/doc/internal_request_rdoc.html\n[path_class_methods]: https://rodauth.jeremyevans.net/rdoc/files/doc/path_class_methods_rdoc.html\n[account types]: https://github.com/janko/rodauth-rails/wiki/Account-Types\n[custom mailer job]: https://github.com/janko/rodauth-rails/wiki/Custom-Mailer-Job\n[Turbo]: https://turbo.hotwired.dev/\n[rodauth-model]: https://github.com/janko/rodauth-model\n[JSON API]: https://github.com/janko/rodauth-rails/wiki/JSON-API\n[inheritance]: http://rodauth.jeremyevans.net/rdoc/files/doc/guides/share_configuration_rdoc.html\n[library]: https://github.com/jeremyevans/rodauth#label-Using+Rodauth+as+a+Library\n[restoring defaults]: https://github.com/janko/rodauth-rails/wiki/Restoring-Rodauth-Defaults\n[Rack::Attack]: https://github.com/rack/rack-attack\n[Tilt]: https://github.com/jeremyevans/tilt\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjanko%2Frodauth-rails","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjanko%2Frodauth-rails","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjanko%2Frodauth-rails/lists"}