Rodauth Documentation

repository·master·Indexed 23 days ago

https://github.com/jeremyevans/rodauth

A secure, modular authentication framework for Ruby Rack applications built on Roda and Sequel. It provides features such as MFA, JWT, WebAuthn, Argon2 password hashing, active session management, account expiration, and password reuse prevention, with a strong emphasis on database-level security best practices.

Tokens
9.4K
Snippets
8
Records
52
Agent score
84%

What's inside Rodauth

  1. Overview of Rodauth

    master

    Rodauth is an advanced authentication framework for Ruby designed to work with any Rack application. While built using Roda and Sequel, it is flexible enough to be used with other web frameworks and database libraries.

    Key characteristics include:

    • Security: High-security defaults, including support for protecting password hashes via database functions in PostgreSQL, MySQL, and Microsoft SQL Server.
    • Simplicity: Configuration is handled via a Domain Specific Language (DSL).
    • Flexibility: Most parts of the framework can be easily overridden.
    • API Support: Provides both HTML and JSON APIs for all supported features.
    • MFA & Passwordless: Supports multiple multifactor and passwordless authentication methods.
  2. Handle inactive sessions

    master

    When a user's session is invalidated because a newer session has taken precedence, the no_longer_active_session method is triggered. This method performs the following actions:

    1. Clears the current session.
    2. Sets the HTTP response status to the value of inactive_session_error_status (default 401).
    3. Sets the error reason to :inactive_session.
    4. Sets the error flash message to the value of single_session_error_flash.
    5. Redirects the user to the location defined by single_session_redirect.
  3. How global logout works in Active Sessions

    master

    The active_sessions feature supports a 'global logout' mechanism that allows a user to invalidate all their active sessions at once.

    By default, the before_logout hook checks for the presence of the global_logout_param (defaulting to 'global_logout').

    • If the parameter is present: Rodauth calls remove_all_active_sessions, effectively logging the user out of every device/browser.
    • If the parameter is absent: Rodauth calls remove_current_session, logging the user out of only the current session.

    To implement a global logout UI, you can use logout_additional_form_tags to render the necessary form field (e.g., a checkbox) in your logout form.

  4. Rotate Argon2 secrets

    master

    To rotate your Argon2 secret without forcing all users to reset their passwords, use the argon2_old_secret configuration.

    When a user logs in, Rodauth attempts to verify the password using the current argon2_secret. If that fails, it attempts verification using argon2_old_secret. If the old secret succeeds, Rodauth sets an internal flag @update_password_hash = true, which triggers a re-hash of the password using the new argon2_secret during the login process.

  5. Secure Password Hash Access (PostgreSQL, MySQL, MSSQL)

    master

    To maximize security, Rodauth can protect password hashes by restricting direct access via database functions. This prevents an attacker with SQL injection or RCE capabilities from reading the raw password hashes.

    How it works:

    1. Two Database Accounts:
      • The app account (used by your application) has no direct access to read password hashes.
      • The ph account (password hash account) owns the functions that check passwords and retrieve salts.
    2. Execution: The app account executes functions set up by the ph account to verify passwords without ever seeing the hash itself.
    3. Capabilities: The app account can still INSERT, UPDATE, and DELETE password hashes, but cannot SELECT them directly.

    Note: This is not required and works correctly without it, but is highly recommended for supported databases.

  6. How WebAuthn autofill works

    master

    The webauthn_autofill feature enhances the WebAuthn login experience by automatically handling authenticator selection.

    When enabled, it modifies the login form's autocomplete value to include webauthn if the current path matches the login_path. It also injects a webauthn-autofill template into the login form footer if a valid login has not yet been entered.

    Technically, it relies on a JavaScript file served via a specific route to handle the client-side interaction. The feature depends on the webauthn_login feature being enabled.

  7. Using HMAC for enhanced security

    master

    By default, Rodauth does not use HMACs for backwards compatibility, but it is strongly encouraged to set the hmac_secret configuration method. Enabling an HMAC secret provides additional security for several features:

    • Email/Remember/OTP Tokens: When hmac_secret is set, tokens sent via email or stored in 'remember' cookies use an HMAC. If the database is leaked, the raw tokens are useless without the hmac_secret.
    • WebAuthn: Required to check that authentication challenges have not been modified.
    • Active Sessions: Required because the database stores an HMAC of the active session ID.
    • Single Session: Ensures the single session secret in the session is HMACed.

    Graceful Transitions:

    • Email: Use allow_raw_email_token? = true temporarily to allow old tokens to work.
    • Remember: Use raw_remember_token_deadline to allow old tokens to work until they expire.
    • OTP: There is no simple transition; you must either revoke all keys or use otp_keys_use_hmac? to manage the transition logic.
  8. Define custom features in Rodauth

    master

    Rodauth uses a plugin-like system for defining features. A feature is defined using Rodauth.define(name, constant=nil, &block).

    When defining a feature, you can use several DSL methods to configure how it behaves:

    • depends(*deps): Declares dependencies on other features.
    • route(name, default, &block): Defines a route for the feature. This automatically creates helper methods like #{name}_path and #{name}_url.
    • email(type, subject, opts): Configures email-related functionality (subject, body, creation, and sending).
    • after(name) and before(name): Defines hooks that run before or after a specific feature action.
    • auth_value_method(meth, value): Defines a configuration method that returns a static value.
    • auth_method(meth, &block): Defines a configuration method that executes a block.
    • redirect(name, &block): Defines a redirect behavior for the feature.
    • view(page, title, name): Configures view rendering for the feature.
  9. Microsoft SQL Server Database Setup for Rodauth

    master

    It is recommended to use the ph account as the superuser for the database and have it GRANT permissions to the app account.

    CREATE LOGIN rodauth_test WITH PASSWORD = 'rodauth_test';
    CREATE LOGIN rodauth_test_password WITH PASSWORD = 'rodauth_test';
    CREATE DATABASE rodauth_test;
    USE rodauth_test;
    CREATE USER rodauth_test FOR LOGIN rodauth_test;
    GRANT CONNECT, EXECUTE TO rodauth_test;
    EXECUTE sp_changedbowner 'rodauth_test_password';
  10. Running Rodauth Migrations with Multiple Users

    master

    Because Rodauth uses two different database accounts (app and ph), you must run two separate migrations.

    On PostgreSQL, the first migration (creating tables) is run by the app account. The second migration (creating password hash functions/tables) is run by the ph account.

    To run the password user migration using Sequel's migration API:

    Sequel.extension :migration
    
    Sequel.postgres('DATABASE_NAME', user: 'PASSWORD_USER_NAME') do |db|
      Sequel::Migrator.run(db, 'path/to/password_user/migrations', table: 'schema_info_password')
    end

    If you are not using PostgreSQL, MySQL, or MSSQL, or cannot use multiple accounts, you can combine both migrations into one, removing the permission and function code.

  11. PostgreSQL Database Setup for Rodauth

    master

    For maximum security on PostgreSQL, use three distinct database accounts:

    1. Superuser: Used to load extensions.
    2. app account: The application user (should own the database).
    3. ph account: Handles access to password hashes.

    1. Create Accounts

    # Create the app account (often named after the database)
    createuser -U postgres ${DATABASE_NAME}
    
    # Create the ph account
    createuser -U postgres ${DATABASE_NAME}_password

    2. Create Database

    createdb -U postgres -O ${DATABASE_NAME} ${DATABASE_NAME}

    3. Load Extensions (e.g., for case-insensitive logins)

    psql -U postgres -c "CREATE EXTENSION citext" ${DATABASE_NAME}

    4. Grant Schema Rights (PostgreSQL 15+)

    PostgreSQL 15+ requires the ph account to have writable access to the public schema for setup:

    psql -U postgres -c "GRANT CREATE ON SCHEMA public TO ${DATABASE_NAME}_password" ${DATABASE_NAME}
  12. Basic Usage of Rodauth in Roda

    master

    Rodauth is a Roda plugin. You load it using the plugin :rodauth syntax. The configuration is handled within a block using a DSL. The most important method is enable, which specifies which features (e.g., :login, :logout) to load.

    Once features are enabled, you can use auth methods (which accept blocks to override default behavior) and auth value methods (which set configuration values, optionally via blocks for dynamic behavior).

    plugin :rodauth do
      enable :login, :logout
      # Auth value method example
      accounts_table :users
      
      # Auth method example (hook)
      after_login do
        LOGGER.info "#{account[:email]} logged in from #{request.ip}"
      end
    end