Devise OTP

repository·master·Indexed 43 days ago

https://github.com/wmlele/devise-otp

A Two-Factor Authentication extension for the Devise authentication framework. It implements RFC 6238 Time-Based One-Time Passwords (TOTP) using the rotp library, enabling compatibility with authenticator apps like Google Authenticator.

Tokens
5.7K
Snippets
22
Records
24
Agent score
55%

What's inside Devise OTP

  1. Enforce Mandatory OTP in Controllers

    master

    If config.otp_mandatory is enabled, you must ensure users are redirected to the enrollment form. Use the ensure_mandatory_{scope}_otp! method in your controllers. This works similarly to authenticate_{scope}!. It is best used as a before_action alongside your standard authentication check.

    class ApplicationController < ActionController::Base
      before_action :authenticate_user!
      before_action :ensure_mandatory_user_otp!
    end
  2. Install Devise OTP

    master

    To add Devise OTP to your Rails application, first ensure Devise is already installed. Then, add the gem to your Gemfile and run bundle. Finally, run the installation generator to add configuration options to your Devise initializer.

    Steps:

    1. Add gem "devise-otp" to your Gemfile.
    2. Run bundle.
    3. Run rails g devise_otp:install.
    # Add to Gemfile
    gem "devise-otp"
    
    # Run in terminal
    $ bundle
    $ rails g devise_otp:install
  3. Get Started with Devise OTP

    master

    To implement OTP for your users, you need to generate your Devise model and then add the OTP module to it. You should also provide a way for users to access their OTP settings via a link in your navigation.

    Steps:

    1. Generate your Devise model (e.g., User) if you haven't already: rails g devise MODEL.
    2. Add OTP to that model: rails g devise_otp MODEL.
    3. Add a link to the OTP Token settings in your views. Replace user with the lowercase name of your model.

    Example link: <%= link_to "OTP Token", user_otp_token_path %>

    # 1. Create Devise model
    rails g devise User
    
    # 2. Add OTP to the model
    rails g devise_otp User
    <%# 3. Add link to navigation (replace 'user' with your model name) %>
    <%= link_to "OTP Token", user_otp_token_path %>
  4. Customize Devise OTP views and CSS

    master

    Custom Views

    To modify the default OTP views, use the generator to eject them into your application: rails g devise_otp:views

    By default, files are placed in app/views/devise. To change this location, configure config.otp_controller_path in your Devise initializer.

    Custom CSS

    To use the default styling, import the CSS file into your application's stylesheet: @import url("devise-otp.css");

    Alternatively, you can copy the contents of devise-otp.css directly into your own stylesheet.

    # Eject views
    rails g devise_otp:views
    /* In your application.css */
    @import url("devise-otp.css");
  5. Configure Devise OTP options

    master

    Configuration options are added to config/initializers/devise.rb by the installation generator. You can adjust these settings to control the OTP behavior in your application.

    Available Configuration Keys:

    KeyDescription
    config.otp_mandatoryIf true, users are forced to enroll in OTP during their next sign-in before completing the session.
    config.otp_authentication_timeoutTime allowed to authenticate with the token (default: 3.minutes).
    config.otp_drift_windowAllowance for clock drift between device and server, expressed in 30-second increments (default: 3).
    config.otp_credentials_refreshTime after which users must re-authenticate with password/OTP to change OTP info (default: 15.minutes).
    config.otp_recovery_tokensNumber of one-time recovery tokens provided (default: 10; set to false to disable).
    config.otp_trust_persistenceDuration a browser is 'trusted' to skip OTP challenges (default: 1.month; set to false to disable).
    config.otp_issuerName of the token issuer in the provisioning URL (default: Rails application class).
    config.otp_controller_pathThe view path for OTP controllers (default: 'devise').
  6. Generate Devise OTP configuration for a model

    master

    Use the devise_otp Rails generator to automatically inject the :otp_authenticatable directive into your specified model and generate the necessary ActiveRecord migration. This simplifies the setup of Two-Factor Authentication by modifying the model file to include the OTP module and creating the database columns required for OTP storage.

    rails generate devise_otp ModelName
  7. Configure Devise::OTP authentication settings

    master

    The following configuration keys are available for the Devise::Models configuration block to customize OTP behavior:

    • otp_authentication_timeout: Duration for which an OTP challenge remains valid.
    • otp_drift_window: Number of 30-second windows allowed for clock drift during TOTP validation.
    • otp_trust_persistence: Configuration for how long a device is trusted.
    • otp_mandatory: Boolean indicating if OTP is required for all users.
    • otp_credentials_refresh: Setting for refreshing credentials.
    • otp_issuer: The name of the issuer displayed in authenticator apps (defaults to the Rails application's module parent name).
    • otp_recovery_tokens: The number of recovery tokens to generate/provide.
    • otp_controller_path: The path for the OTP controller.
  8. Configure Devise OTP settings

    master

    After installation, you can configure the OTP extension within the Devise.setup block in config/initializers/devise.rb. The following configuration keys are available:

    • otp_mandatory: (Boolean) If true, users are forced to enroll in OTP during their next sign-in before completing the session.
    • otp_drift_window: (Integer) The allowance for clock drift between the user's device and the server, expressed in minutes (e.g., 3). Note: This must be a raw number, not a Rails duration like 3.minutes.
    • otp_credentials_refresh: (Duration) The time interval after which users must re-enter their password (and OTP challenge) before they can view or modify OTP information.
    • otp_recovery_tokens: (Integer) The number of one-time recovery tokens provided to users for emergency access. Set to false to disable.
    • otp_trust_persistence: (Duration) How long a browser can be marked as "trusted" to skip future OTP challenges. Set to false to disable.
    • otp_issuer: (String) The name of the token issuer included in the provisioning URL (e.g., 'my_application'). Defaults to the Rails application class name.
    • otp_controller_path: (String) The custom view path for Devise OTP controllers.
    Devise.setup do |config|
      # OTP is mandatory, users will be asked to enroll on next sign-in
      config.otp_mandatory = true
    
      # Drift window in minutes (use a number, NOT 3.minutes)
      config.otp_drift_window = 3
    
      # Re-authentication interval for OTP info changes
      config.otp_credentials_refresh = 15.minutes
    
      # Number of recovery tokens for emergency access
      config.otp_recovery_tokens = 10
    
      # How long a browser remains 'trusted'
      config.otp_trust_persistence = 1.month
    
      # Name of the token issuer
      config.otp_issuer = 'my_application'
    
      # Custom view path for controllers
      config.otp_controller_path = 'devise'
    end
  9. Configure Devise OTP global settings

    master

    You can customize the behavior of the OTP authentication module by setting global configuration options on the Devise module. These settings affect how tokens are validated, how long devices remain trusted, and how frequently credentials must be refreshed.

    Available configuration keys:

    • otp_mandatory: (Boolean) If true, forces OTP authentication for all users. Defaults to false.
    • otp_authentication_timeout: (Duration) The time window for OTP authentication. Defaults to 3.minutes.
    • otp_recovery_tokens: (Integer or false) The number of recovery tokens allowed. Set to false to disable recovery tokens. Defaults to 10.
    • otp_trust_persistence: (Duration, nil, or false) How long a device remains 'trusted' (skipping OTP). Set to nil or false to disable device trust. Defaults to 30.days.
    • otp_drift_window: (Integer) The allowed time drift for OTP tokens. Defaults to 3.
    • otp_credentials_refresh: (Duration or false) The interval after which the user must re-enter both password and token. Set to false to disable. Defaults to 15.minutes.
    • otp_issuer: (String) The name of the token issuer. Defaults to the Rails application's module parent name.
    • otp_controller_path: (String) The custom view path for OTP controllers. Defaults to "devise".
    Devise.otp_mandatory = true
    Devise.otp_trust_persistence = 7.days
    Devise.otp_recovery_tokens = 5
    Devise.otp_issuer = "MySecureApp"
  10. Validate OTP tokens (Time-based and Recovery)

    master

    The module provides methods to verify both standard time-based TOTP tokens and one-time recovery HOTP tokens.

    Time-based OTP (TOTP)

    Use validate_otp_token(token) or validate_otp_time_token(token) to verify a standard TOTP token. Validation accounts for clock drift based on the configured otp_drift_window.

    Recovery OTP (HOTP)

    Use validate_otp_recovery_token(token) to verify a recovery token. Note that successful validation of a recovery token automatically increments the otp_recovery_counter on the model.

    Unified Validation

    validate_otp_token(token, recovery: true/false) allows you to specify which type of token is being provided.

    # Validate a standard TOTP token
    if user.validate_otp_token(params[:token])
      # success
    end
    
    # Validate a recovery token
    if user.validate_otp_token(params[:token], true)
      # success
    end
  11. Manage OTP secrets and state on the User model

    master

    When a model includes Devise::Models::OtpAuthenticatable, it gains several methods to manage the lifecycle of OTP credentials and session challenges.

    Key lifecycle methods:

    • populate_otp_secrets!: Generates and saves otp_auth_secret, otp_recovery_secret, and otp_persistence_seed if any are missing.
    • enable_otp!: Sets otp_enabled to true and records the current time.
    • disable_otp!: Sets otp_enabled to false.
    • clear_otp_fields!: Wipes all OTP-related fields (secrets, challenges, and counters) and resets failed attempts.
    • generate_otp_challenge!(expires = nil): Generates a unique otp_session_challenge and sets an expiration time based on the configured otp_authentication_timeout.
    # Example: Setting up a user for OTP
    user.populate_otp_secrets!
    user.enable_otp!
    
    # Example: Generating a challenge for a login session
    challenge = user.generate_otp_challenge!