AuthTrail Documentation

repository·master·Indexed 20 days ago

https://github.com/ankane/authtrail

A Ruby on Rails gem for tracking Devise login activity. AuthTrail captures metadata from login attempts—including success/failure status, IP addresses, user agents, and geocoding data—to help developers detect suspicious authentication behavior. It supports multiple encryption strategies (Lockbox, Active Record, or none) and provides configuration hooks to exclude, transform, or customize the storage of login activity records.

Tokens
3.6K
Snippets
13
Records
15
Agent score
65%

What's inside AuthTrail

  1. How AuthTrail works

    master

    AuthTrail creates a LoginActivity record every time a user attempts to login via Devise. These records can be used to detect suspicious behavior.

    Each LoginActivity contains:

    • scope: Devise scope
    • strategy: Devise strategy
    • identity: email address
    • success: boolean indicating if login succeeded
    • failure_reason: reason for failure if applicable
    • user: the associated user (if login succeeded)
    • context: controller and action
    • ip: IP address
    • user_agent and referrer: browser metadata
    • city, region, country, latitude, and longitude: geocoding data derived from IP
    • created_at: timestamp of the event
  2. Install AuthTrail

    master

    Add authtrail to your Gemfile. You can choose different encryption strategies during installation:

    With Lockbox encryption

    Requires lockbox and blind_index gems. This is recommended for encrypting email and IP addresses.

    With Active Record encryption

    Uses Rails' built-in Active Record encryption.

    Without encryption

    Stores data in plain text.

    After choosing a strategy, run the generator and migrate your database.

    # Add to Gemfile
    gem "authtrail"
    # Option 1: Lockbox encryption
    rails generate authtrail:install --encryption=lockbox
    rails db:migrate
    
    # Option 2: Active Record encryption
    rails generate authtrail:install --encryption=activerecord
    rails db:migrate
    
    # Option 3: No encryption
    rails generate authtrail:install --encryption=none
    rails db:migrate
  3. Configure Local Geocoding (Privacy & Performance)

    master

    To avoid sending IP addresses to 3rd party services, you can perform geocoding locally using MaxMind databases.

    City-level geocoding

    1. Add gem "maxminddb" to your Gemfile.
    2. Download a GeoLite2 City database.
    3. Configure config/initializers/geocoder.rb to use :geoip2 and point to your .mmdb file.

    Country-level geocoding

    1. Install the geoip-database system package (e.g., sudo apt-get install geoip-database on Ubuntu).
    2. Add gem "geoip" to your Gemfile.
    3. Configure config/initializers/geocoder.rb to use :maxmind_local with the path to your .dat file.
    # City-level configuration
    # config/initializers/geocoder.rb
    Geocoder.configure(
      ip_lookup: :geoip2,
      geoip2: {
        file: "path/to/GeoLite2-City.mmdb"
      }
    )
    
    # Country-level configuration
    # config/initializers/geocoder.rb
    Geocoder.configure(
      ip_lookup: :maxmind_local,
      maxmind_local: {
        file: "/usr/share/GeoIP/GeoIP.dat",
        package: :country
      }
    )
  4. Use Load Balancer Geocoding

    master

    If your load balancer (Nginx, Google Cloud, Cloudflare) provides geolocation in request headers, you can disable AuthTrail's internal geocoding and manually map the headers using AuthTrail.transform_method.

    AuthTrail.geocode = false
    
    AuthTrail.transform_method = lambda do |data, request|
      data[:country] = request.headers["<country-header>"]
      data[:region] = request.headers["<region-header>"]
      data[:city] = request.headers["<city-header>"]
    end
  5. Enable Geocoding

    master

    AuthTrail uses the geocoder gem to derive location data from IP addresses. Geocoding runs in a background job to avoid slowing down web requests.

    1. Add gem "geocoder" to your Gemfile.
    2. Enable it in config/initializers/authtrail.rb by setting AuthTrail.geocode = true.
    3. (Optional) Configure the job queue using AuthTrail.job_queue = :symbol.
    # Gemfile
    gem "geocoder"
    
    # config/initializers/authtrail.rb
    AuthTrail.geocode = true
    AuthTrail.job_queue = :low_priority
  6. Install AuthTrail using the Rails generator

    master

    To install AuthTrail in a Rails application, use the provided Rails generator. You must specify an encryption strategy using the --encryption option. This option determines how the identity and ip columns are stored in the login_activities table and which model implementation is generated.

    Supported encryption values:

    • lockbox: Uses the Lockbox gem for encryption (stores identity_ciphertext, identity_bidx, ip_ciphertext, and ip_bidx).
    • activerecord: Uses built-in ActiveRecord encryption.
    • none: Stores data in plain text.

    Running the generator will:

    1. Create a migration file db/migrate/create_login_activities.rb.
    2. Create the initializer config/initializers/authtrail.rb.
    3. Generate the app/models/login_activity.rb model with the appropriate encryption logic.
    rails generate authtrail:install --encryption=lockbox
  7. Associate login activities with your User model

    master

    The LoginActivity model uses a polymorphic association. To associate it with your user model, add has_many :login_activities, as: :user to your model.

    Note: You must use :user as the polymorphic name regardless of what your actual model is named.

    class User < ApplicationRecord
      has_many :login_activities, as: :user
    end
  8. Customize AuthTrail tracking behavior

    master

    AuthTrail provides several configuration hooks to modify how login activities are captured and stored.

    Exclude certain attempts

    Use AuthTrail.exclude_method to skip tracking for specific data (e.g., automated tests).

    Transform or add data

    Use AuthTrail.transform_method to modify the data hash or add new fields (like request_id) before saving. You can also use this to associate a user on failed attempts by looking them up via the identity.

    Custom storage

    Use AuthTrail.track_method to write data to a destination other than the default login_activities table.

    Custom identity resolution

    Use AuthTrail.identity_method to define how the login identity (e.g., email) is extracted from the request or user object.

    # Exclude specific identities
    AuthTrail.exclude_method = lambda do |data|
      data[:identity] == "capybara@example.org"
    end
    
    # Add request_id to the data
    AuthTrail.transform_method = lambda do |data, request|
      data[:request_id] = request.request_id
    end
    
    # Store the user on failed attempts
    AuthTrail.transform_method = lambda do |data, request|
      data[:user] ||= User.find_by(email: data[:identity])
    end
    
    # Write to a custom destination
    AuthTrail.track_method = lambda do |data|
      # custom logic here
    end
    
    # Custom identity method
    AuthTrail.identity_method = lambda do |request, opts, user|
      if user
        user.email
      else
        request.params.dig(opts[:scope], :email)
      end
    end
  9. Configure AuthTrail global settings

    master

    AuthTrail provides several configuration hooks via class attributes on the AuthTrail module to customize how data is processed, stored, and filtered.

    AttributeTypeDescription
    track_methodlambdaA proc that receives a data hash. This is where the LoginActivity record is actually created and saved.
    identity_methodlambdaA proc that receives (request, opts, user) and returns the identity string. Default attempts to use user.email or params.
    transform_methodlambdaA proc that receives (data, request). Used to mutate the data hash before it is passed to the exclude_method or track_method.
    exclude_methodlambdaA proc that receives (data) and returns a boolean. If it returns true, the activity is not tracked.
    geocodeBooleanEnables/disables background geocoding of IP addresses via AuthTrail::GeocodeJob.
    job_queue(Any)Configuration for the background job queue.
  10. Manage Data Retention

    master

    Since LoginActivity records can accumulate quickly, you should implement a data retention policy by deleting old records using in_batches to avoid performance issues.

    # Delete data older than 2 years
    LoginActivity.where("created_at < ?", 2.years.ago).in_batches.delete_all
    
    # Delete data for a specific user
    LoginActivity.where(user_id: 1, user_type: "User").in_batches.delete_all
  11. Track login activity with AuthTrail.track

    master

    The AuthTrail.track method is the primary entrypoint for manually recording login events. It captures authentication metadata including strategy, scope, identity, success status, and request details (IP, User Agent, Referrer, and Controller/Action context).

    Arguments

    • strategy: (String/Symbol) The authentication strategy used.
    • scope: (String/Symbol) The authentication scope.
    • identity: (String/Object) The identifier for the login attempt (e.g., email).
    • success: (Boolean) Whether the attempt succeeded.
    • request: (Object) An object responding to remote_ip, user_agent, referrer, and params (typically a Rails ActionDispatch::Request).
    • user: (Optional) The user object if the login was successful.
    • failure_reason: (Optional) The reason for failure if success is false.
    AuthTrail.track(
      strategy: :password,
      scope: :user,
      identity: 'user@example.com',
      success: true,
      request: request,
      user: current_user
    )
  12. Customize identity extraction with identity_method

    master

    The identity_method allows you to define how the user's identity (e.g., email) is extracted from a request or user object. The proc receives three arguments: request, opts, and user.

    By default, it tries to use user.email. If no user is present, it attempts to find the email in the request parameters under the provided scope.

    AuthTrail.identity_method = lambda do |request, opts, user|
      user&.email || request.params.dig(opts[:scope], :email)
    end
    AuthTrail.identity_method = lambda do |request, opts, user|
      if user
        user.try(:email)
      else
        scope = opts[:scope]
        request.params[scope] && request.params[scope][:email] rescue nil
      end
    end