Awesome
AuthTrail
Track Devise login activity
:tangerine: 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:migrate
To use Active Record encryption (Rails 7+, experimental), run:
rails generate authtrail:install --encryption=activerecord
rails db:migrate
If you prefer not to encrypt data, run:
rails generate authtrail:install --encryption=none
rails db:migrate
To 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 scopestrategy
- Devise strategyidentity
- email addresssuccess
- whether the login succeededfailure_reason
- if the login faileduser
- the user if the login succeededcontext
- controller and actionip
- IP addressuser_agent
andreferrer
- from browsercity
,region
,country
,latitude
, andlongitude
- from IPcreated_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"
end
Add 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
end
Store the user on failed attempts
AuthTrail.transform_method = lambda do |data, request|
data[:user] ||= User.find_by(email: data[:identity])
end
Write data somewhere other than the login_activities
table
AuthTrail.track_method = lambda do |data|
# code
end
Use a custom identity method
AuthTrail.identity_method = lambda do |request, opts, user|
if user
user.email
else
request.params.dig(opts[:scope], :email)
end
end
Associate login activity with your user model
class User < ApplicationRecord
has_many :login_activities, as: :user # use :user no matter what your model name
end
The 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 = true
Geocoding is performed in a background job so it doesn’t slow down web requests. Set the job queue with:
AuthTrail.job_queue = :low_priority
Local 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-database
Add 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>"]
end
Check out this example
Data Retention
Delete older data with:
LoginActivity.where("created_at < ?", 2.years.ago).in_batches.delete_all
Delete data for a specific user with:
LoginActivity.where(user_id: 1, user_type: "User").in_batches.delete_all
Other 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