AuthTrail
Track Devise login activity
🍊 Battle-tested at Instacart
Installation
Add this line to your application’s Gemfile:
gem "authtrail"To encrypt email and IP addresses with Lockbox, install Lockbox and Blind Index and run:
rails generate authtrail:install --encryption=lockbox
rails db:migrateTo use Active Record encryption, run:
rails generate authtrail:install --encryption=activerecord
rails db:migrateIf you prefer not to encrypt data, run:
rails generate authtrail:install --encryption=none
rails db:migrateTo enable geocoding, see the Geocoding section.
How It Works
A LoginActivity record is created every time a user tries to login. You can then use this information to detect suspicious behavior. Data includes:
-
scope- Devise scope -
strategy- Devise strategy -
identity- email address -
success- whether the login succeeded -
failure_reason- if the login failed -
user- the user if the login succeeded -
context- controller and action -
ip- IP address -
user_agentandreferrer- from browser -
city,region,country,latitude, andlongitude- from IP -
created_at- time of event
Features
Exclude certain attempts from tracking - useful if you run acceptance tests
AuthTrail.exclude_method = lambda do |data|
data[:identity] == "capybara@example.org"
endAdd or modify data - also add new fields to the login_activities table if needed
AuthTrail.transform_method = lambda do |data, request|
data[:request_id] = request.request_id
endStore the user on failed attempts
AuthTrail.transform_method = lambda do |data, request|
data[:user] ||= User.find_by(email: data[:identity])
endWrite data somewhere other than the login_activities table
AuthTrail.track_method = lambda do |data|
# code
endUse a custom identity method
AuthTrail.identity_method = lambda do |request, opts, user|
if user
user.email
else
request.params.dig(opts[:scope], :email)
end
endAssociate login activity with your user model
class User < ApplicationRecord
has_many :login_activities, as: :user # use :user no matter what your model name
endThe LoginActivity model uses a polymorphic association so it can be associated with different user models.
Geocoding
AuthTrail uses Geocoder for geocoding. We recommend configuring local geocoding or load balancer geocoding so IP addresses are not sent to a 3rd party service. If you do use a 3rd party service and adhere to GDPR, be sure to add it to your subprocessor list.
To enable geocoding, add this line to your application’s Gemfile:
gem "geocoder"And update config/initializers/authtrail.rb:
AuthTrail.geocode = trueGeocoding is performed in a background job so it doesn’t slow down web requests. Set the job queue with:
AuthTrail.job_queue = :low_priorityLocal Geocoding
For privacy and performance, we recommend geocoding locally.
For city-level geocoding, download the GeoLite2 City database.
Add this line to your application’s Gemfile:
gem "maxminddb"And create config/initializers/geocoder.rb with:
Geocoder.configure(
ip_lookup: :geoip2,
geoip2: {
file: "path/to/GeoLite2-City.mmdb"
}
)For country-level geocoding, install the geoip-database package. It’s preinstalled on Heroku. For Ubuntu, use:
sudo apt-get install geoip-databaseAdd this line to your application’s Gemfile:
gem "geoip"And create config/initializers/geocoder.rb with:
Geocoder.configure(
ip_lookup: :maxmind_local,
maxmind_local: {
file: "/usr/share/GeoIP/GeoIP.dat",
package: :country
}
)Load Balancer Geocoding
Some load balancers can add geocoding information to request headers.
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>"]
endCheck out this example
Data Retention
Delete older data with:
LoginActivity.where("created_at < ?", 2.years.ago).in_batches.delete_allDelete data for a specific user with:
LoginActivity.where(user_id: 1, user_type: "User").in_batches.delete_allOther Notes
We recommend using this in addition to Devise’s Lockable module and Rack::Attack.
Check out Hardening Devise and Secure Rails for more best practices.
History
View the changelog
Contributing
Everyone is encouraged to help improve this project. Here are a few ways you can help:
- Report bugs
- Fix bugs and submit pull requests
- Write, clarify, or fix documentation
- Suggest or add new features
To get started with development and testing:
git clone https://github.com/ankane/authtrail.git
cd authtrail
bundle install
bundle exec rake test